OpenCPN Partial API docs
Loading...
Searching...
No Matches
comm_bridge.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose:
5 * Author: David Register, Alec Leamas
6 *
7 ***************************************************************************
8 * Copyright (C) 2022 by David Register, Alec Leamas *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26// For compilers that support precompilation, includes "wx.h".
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif // precompiled headers
32
33#include <wx/event.h>
34#include <wx/string.h>
35#include <wx/tokenzr.h>
36#include <wx/fileconf.h>
37
38#include "comm_ais.h"
39#include "comm_appmsg_bus.h"
40#include "comm_bridge.h"
41#include "comm_drv_registry.h"
42#include "comm_navmsg_bus.h"
43#include "comm_vars.h"
44#include "config_vars.h"
45#include "idents.h"
46#include "OCPNPlatform.h"
47#include "ocpn_types.h"
48#include "own_ship.h"
49
50
51// comm event definitions
52wxDEFINE_EVENT(EVT_N2K_129029, ObservedEvt);
53wxDEFINE_EVENT(EVT_N2K_129025, ObservedEvt);
54wxDEFINE_EVENT(EVT_N2K_129026, ObservedEvt);
55wxDEFINE_EVENT(EVT_N2K_127250, ObservedEvt);
56wxDEFINE_EVENT(EVT_N2K_129540, ObservedEvt);
57
58wxDEFINE_EVENT(EVT_N0183_RMC, ObservedEvt);
59wxDEFINE_EVENT(EVT_N0183_HDT, ObservedEvt);
60wxDEFINE_EVENT(EVT_N0183_HDG, ObservedEvt);
61wxDEFINE_EVENT(EVT_N0183_HDM, ObservedEvt);
62wxDEFINE_EVENT(EVT_N0183_VTG, ObservedEvt);
63wxDEFINE_EVENT(EVT_N0183_GSV, ObservedEvt);
64wxDEFINE_EVENT(EVT_N0183_GGA, ObservedEvt);
65wxDEFINE_EVENT(EVT_N0183_GLL, ObservedEvt);
66wxDEFINE_EVENT(EVT_N0183_AIVDO, ObservedEvt);
67
68wxDEFINE_EVENT(EVT_DRIVER_CHANGE, wxCommandEvent);
69
70wxDEFINE_EVENT(EVT_SIGNALK, ObservedEvt);
71
72bool debug_priority = 0;
73
74void ClearNavData(NavData &d){
75 d.gLat = NAN;
76 d.gLon = NAN;
77 d.gSog = NAN;
78 d.gCog = NAN;
79 d.gHdt = NAN;
80 d.gHdm = NAN;
81 d.gVar = NAN;
82 d.n_satellites = -1;
83 d.SID = 0;
84}
85
90static void SendBasicNavdata() {
91 auto msg = std::make_shared<BasicNavDataMsg>(
92 gLat, gLon, gSog, gCog, gVar, gHdt, wxDateTime::Now().GetTicks());
93 AppMsgBus::GetInstance().Notify(std::move(msg));
94}
95
96
97static inline double GeodesicRadToDeg(double rads) {
98 return rads * 180.0 / M_PI;
99}
100
101static inline double MS2KNOTS(double ms) {
102 return ms * 1.9438444924406;
103}
104
105// CommBridge implementation
106
107BEGIN_EVENT_TABLE(CommBridge, wxEvtHandler)
108EVT_TIMER(WATCHDOG_TIMER, CommBridge::OnWatchdogTimer)
109END_EVENT_TABLE()
110
112
113CommBridge::~CommBridge() {}
114
115bool CommBridge::Initialize() {
116
117 InitializePriorityContainers();
118 ClearPriorityMaps();
119
120 LoadConfig();
121 PresetPriorityContainers();
122
123 // Clear the watchdogs
124 PresetWatchdogs();
125
126 m_watchdog_timer.SetOwner(this, WATCHDOG_TIMER);
127 m_watchdog_timer.Start(1000, wxTIMER_CONTINUOUS);
128
129 // Initialize the comm listeners
130 InitCommListeners();
131
132 // Initialize a listener for driver state changes
133 driver_change_listener.Listen(
134 CommDriverRegistry::GetInstance().evt_driverlist_change.key, this,
135 EVT_DRIVER_CHANGE);
136 Bind(EVT_DRIVER_CHANGE, [&](wxCommandEvent ev) {
137 OnDriverStateChange(); });
138
139
140 return true;
141}
142
143void CommBridge::PresetWatchdogs() {
144 m_watchdogs.position_watchdog = 20; // A bit longer watchdog for startup latency.
145 m_watchdogs.velocity_watchdog = 20;
146 m_watchdogs.variation_watchdog = 20;
147 m_watchdogs.heading_watchdog = 20;
148 m_watchdogs.satellite_watchdog = 20;
149}
150
151void CommBridge::SelectNextLowerPriority(const std::unordered_map<std::string, int> &map,
152 PriorityContainer &pc) {
153
154 int best_prio = 100;
155 for (auto it = map.begin(); it != map.end(); it++) {
156 if (it->second > pc.active_priority){
157 best_prio = wxMin(best_prio, it->second);
158 }
159 }
160
161 pc.active_priority = best_prio;
162 pc.active_source.clear();
163 pc.active_identifier.clear();
164
165}
166
167void CommBridge::OnWatchdogTimer(wxTimerEvent& event) {
168 // Update and check watchdog timer for GPS data source
169 m_watchdogs.position_watchdog--;
170 if (m_watchdogs.position_watchdog <= 0) {
171 if (m_watchdogs.position_watchdog % 5 == 0) {
172 // Send AppMsg telling of watchdog expiry
173 auto msg = std::make_shared<GPSWatchdogMsg>(GPSWatchdogMsg::WDSource::position,
174 m_watchdogs.position_watchdog);
175 auto& msgbus = AppMsgBus::GetInstance();
176 msgbus.Notify(std::move(msg));
177
178 wxString logmsg;
179 logmsg.Printf(_T(" ***GPS Watchdog timeout at Lat:%g Lon: %g"), gLat,
180 gLon);
181 wxLogMessage(logmsg);
182 }
183
184 gSog = NAN;
185 gCog = NAN;
186 gRmcDate.Empty();
187 gRmcTime.Empty();
188
189 // Are there any other lower priority sources?
190 // If so, adopt that one.
191 SelectNextLowerPriority(priority_map_position, active_priority_position);
192 }
193
194 // Update and check watchdog timer for SOG/COG data source
195 m_watchdogs.velocity_watchdog--;
196 if (m_watchdogs.velocity_watchdog <= 0) {
197 gSog = NAN;
198 gCog = NAN;
199 if (g_nNMEADebug && (m_watchdogs.velocity_watchdog == 0))
200 wxLogMessage(_T(" ***Velocity Watchdog timeout..."));
201 if (m_watchdogs.velocity_watchdog % 5 == 0) {
202 // Send AppMsg telling of watchdog expiry
203 auto msg = std::make_shared<GPSWatchdogMsg>(GPSWatchdogMsg::WDSource::velocity,
204 m_watchdogs.velocity_watchdog);
205 auto& msgbus = AppMsgBus::GetInstance();
206 msgbus.Notify(std::move(msg));
207 }
208 // Are there any other lower priority sources?
209 // If so, adopt that one.
210 SelectNextLowerPriority(priority_map_velocity, active_priority_velocity);
211 }
212
213 // Update and check watchdog timer for True Heading data source
214 m_watchdogs.heading_watchdog--;
215 if (m_watchdogs.heading_watchdog <= 0) {
216 gHdt = NAN;
217 if (g_nNMEADebug && (m_watchdogs.heading_watchdog == 0))
218 wxLogMessage(_T(" ***HDT Watchdog timeout..."));
219
220 // Are there any other lower priority sources?
221 // If so, adopt that one.
222 SelectNextLowerPriority(priority_map_heading, active_priority_heading);
223 }
224
225 // Update and check watchdog timer for Magnetic Variation data source
226 m_watchdogs.variation_watchdog--;
227 if (m_watchdogs.variation_watchdog <= 0) {
228 g_bVAR_Rx = false;
229 if (g_nNMEADebug && (m_watchdogs.variation_watchdog == 0))
230 wxLogMessage(_T(" ***VAR Watchdog timeout..."));
231
232 // Are there any other lower priority sources?
233 // If so, adopt that one.
234 SelectNextLowerPriority(priority_map_variation, active_priority_variation);
235 }
236
237 // Update and check watchdog timer for GSV, GGA and SignalK (Satellite data)
238 m_watchdogs.satellite_watchdog--;
239 if (m_watchdogs.satellite_watchdog <= 0) {
240 g_bSatValid = false;
241 g_SatsInView = 0;
242 g_priSats = 99;
243 if (g_nNMEADebug && (m_watchdogs.satellite_watchdog == 0))
244 wxLogMessage(_T(" ***SAT Watchdog timeout..."));
245
246 // Are there any other lower priority sources?
247 // If so, adopt that one.
248 SelectNextLowerPriority(priority_map_satellites, active_priority_satellites);
249 }
250}
251
252void CommBridge::MakeHDTFromHDM() {
253 // Here is the one place we try to create gHdt from gHdm and gVar,
254
255
256 if (!std::isnan(gHdm)) {
257 // Set gVar if needed from manual entry. gVar will be overwritten if
258 // WMM plugin is available
259 if (std::isnan(gVar) && (g_UserVar != 0.0)) gVar = g_UserVar;
260 gHdt = gHdm + gVar;
261 if (!std::isnan(gHdt)) {
262 if (gHdt < 0)
263 gHdt += 360.0;
264 else if (gHdt >= 360)
265 gHdt -= 360.0;
266
267 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
268 }
269 }
270}
271
272void CommBridge::InitCommListeners() {
273 // Initialize the comm listeners
274 auto& msgbus = NavMsgBus::GetInstance();
275
276 // GNSS Position Data PGN 129029
277 //----------------------------------
278 Nmea2000Msg n2k_msg_129029(static_cast<uint64_t>(129029));
279 listener_N2K_129029.Listen(n2k_msg_129029, this, EVT_N2K_129029);
280
281 Bind(EVT_N2K_129029, [&](ObservedEvt ev) {
282 HandleN2K_129029(UnpackEvtPointer<Nmea2000Msg>(ev));
283 });
284
285 // Position rapid PGN 129025
286 //-----------------------------
287 Nmea2000Msg n2k_msg_129025(static_cast<uint64_t>(129025));
288 listener_N2K_129025.Listen(n2k_msg_129025, this, EVT_N2K_129025);
289 Bind(EVT_N2K_129025, [&](ObservedEvt ev) {
290 HandleN2K_129025(UnpackEvtPointer<Nmea2000Msg>(ev));
291 });
292
293 // COG SOG rapid PGN 129026
294 //-----------------------------
295 Nmea2000Msg n2k_msg_129026(static_cast<uint64_t>(129026));
296 listener_N2K_129026.Listen(n2k_msg_129026, this, EVT_N2K_129026);
297 Bind(EVT_N2K_129026, [&](ObservedEvt ev) {
298 HandleN2K_129026(UnpackEvtPointer<Nmea2000Msg>(ev));
299 });
300
301 // Heading rapid PGN 127250
302 //-----------------------------
303 Nmea2000Msg n2k_msg_127250(static_cast<uint64_t>(127250));
304 listener_N2K_127250.Listen(n2k_msg_127250, this, EVT_N2K_127250);
305 Bind(EVT_N2K_127250, [&](ObservedEvt ev) {
306 HandleN2K_127250(UnpackEvtPointer<Nmea2000Msg>(ev));
307 });
308
309 // GNSS Satellites in View PGN 129540
310 //-----------------------------
311 Nmea2000Msg n2k_msg_129540(static_cast<uint64_t>(129540));
312 listener_N2K_129540.Listen(n2k_msg_129540, this, EVT_N2K_129540);
313 Bind(EVT_N2K_129540, [&](ObservedEvt ev) {
314 HandleN2K_129540(UnpackEvtPointer<Nmea2000Msg>(ev));
315 });
316
317
318 // NMEA0183
319 // RMC
320 Nmea0183Msg n0183_msg_RMC("RMC");
321 listener_N0183_RMC.Listen(n0183_msg_RMC, this, EVT_N0183_RMC);
322
323 Bind(EVT_N0183_RMC, [&](ObservedEvt ev) {
324 HandleN0183_RMC(UnpackEvtPointer<Nmea0183Msg>(ev));
325 });
326
327 // HDT
328 Nmea0183Msg n0183_msg_HDT("HDT");
329 listener_N0183_HDT.Listen(n0183_msg_HDT, this, EVT_N0183_HDT);
330
331 Bind(EVT_N0183_HDT, [&](ObservedEvt ev) {
332 HandleN0183_HDT(UnpackEvtPointer<Nmea0183Msg>(ev));
333 });
334
335 // HDG
336 Nmea0183Msg n0183_msg_HDG("HDG");
337 listener_N0183_HDG.Listen(n0183_msg_HDG, this, EVT_N0183_HDG);
338
339 Bind(EVT_N0183_HDG, [&](ObservedEvt ev) {
340 HandleN0183_HDG(UnpackEvtPointer<Nmea0183Msg>(ev));
341 });
342
343 // HDM
344 Nmea0183Msg n0183_msg_HDM("HDM");
345 listener_N0183_HDM.Listen(n0183_msg_HDM, this, EVT_N0183_HDM);
346
347 Bind(EVT_N0183_HDM, [&](ObservedEvt ev) {
348 HandleN0183_HDM(UnpackEvtPointer<Nmea0183Msg>(ev));
349 });
350
351 // VTG
352 Nmea0183Msg n0183_msg_VTG("VTG");
353 listener_N0183_VTG.Listen(n0183_msg_VTG, this, EVT_N0183_VTG);
354
355 Bind(EVT_N0183_VTG, [&](ObservedEvt ev) {
356 HandleN0183_VTG(UnpackEvtPointer<Nmea0183Msg>(ev));
357 });
358
359 // GSV
360 Nmea0183Msg n0183_msg_GSV("GSV");
361 listener_N0183_GSV.Listen(n0183_msg_GSV, this, EVT_N0183_GSV);
362
363 Bind(EVT_N0183_GSV, [&](ObservedEvt ev) {
364 HandleN0183_GSV(UnpackEvtPointer<Nmea0183Msg>(ev));
365 });
366
367 // GGA
368 Nmea0183Msg n0183_msg_GGA("GGA");
369 listener_N0183_GGA.Listen(n0183_msg_GGA, this, EVT_N0183_GGA);
370
371 Bind(EVT_N0183_GGA, [&](ObservedEvt ev) {
372 HandleN0183_GGA(UnpackEvtPointer<Nmea0183Msg>(ev));
373 });
374
375 // GLL
376 Nmea0183Msg n0183_msg_GLL("GLL");
377 listener_N0183_GLL.Listen(n0183_msg_GLL, this, EVT_N0183_GLL);
378
379 Bind(EVT_N0183_GLL, [&](ObservedEvt ev) {
380 HandleN0183_GLL(UnpackEvtPointer<Nmea0183Msg>(ev));
381 });
382
383 // AIVDO
384 Nmea0183Msg n0183_msg_AIVDO("AIVDO");
385 listener_N0183_AIVDO.Listen(n0183_msg_AIVDO, this, EVT_N0183_AIVDO);
386
387 Bind(EVT_N0183_AIVDO, [&](ObservedEvt ev) {
388 HandleN0183_AIVDO(UnpackEvtPointer<Nmea0183Msg>(ev));
389 });
390
391 // SignalK
392 SignalkMsg sk_msg;
393 listener_SignalK.Listen(sk_msg, this, EVT_SIGNALK);
394
395 Bind(EVT_SIGNALK, [&](ObservedEvt ev) {
396 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
397 });
398
399}
400
401void CommBridge::OnDriverStateChange(){
402
403 // Reset all active priority states
404 PresetPriorityContainers();
405
406}
407
408std::string CommBridge::GetPriorityMap(std::unordered_map<std::string, int> &map){
409
410 #define MAX_SOURCES 10
411 std::string sa[MAX_SOURCES];
412 std::string result;
413
414 for (auto& it: map) {
415 if ((it.second >= 0) && (it.second < MAX_SOURCES))
416 sa[it.second] = it.first;
417 }
418
419 //build the packed string result
420 for (int i=0 ; i < MAX_SOURCES ; i++){
421 if (sa[i].size()) {
422 result += sa[i];
423 result += "|";
424 }
425 }
426
427 return result;
428}
429
430
431
432std::vector<std::string> CommBridge::GetPriorityMaps(){
433
434 std::vector<std::string> result;
435
436 result.push_back(GetPriorityMap(priority_map_position));
437 result.push_back(GetPriorityMap(priority_map_velocity));
438 result.push_back(GetPriorityMap(priority_map_heading));
439 result.push_back(GetPriorityMap(priority_map_variation));
440 result.push_back(GetPriorityMap(priority_map_satellites));
441
442
443 return result;
444}
445
446void CommBridge::ApplyPriorityMap(std::unordered_map<std::string, int>& priority_map, wxString &new_prio, int category){
447 priority_map.clear();
448 wxStringTokenizer tk(new_prio, "|");
449 int index = 0;
450 while (tk.HasMoreTokens()) {
451 wxString entry = tk.GetNextToken();
452 std::string s_entry(entry.c_str());
453 priority_map[s_entry] = index;
454 index++;
455 }
456}
457
458
459void CommBridge::ApplyPriorityMaps(std::vector<std::string> new_maps){
460
461 wxString new_prio_string;
462
463 new_prio_string = wxString( new_maps[0].c_str());
464 ApplyPriorityMap(priority_map_position, new_prio_string, 0);
465
466 new_prio_string = wxString( new_maps[1].c_str());
467 ApplyPriorityMap(priority_map_velocity, new_prio_string, 1);
468
469 new_prio_string = wxString( new_maps[2].c_str());
470 ApplyPriorityMap(priority_map_heading, new_prio_string, 2);
471
472 new_prio_string = wxString( new_maps[3].c_str());
473 ApplyPriorityMap(priority_map_variation, new_prio_string, 3);
474
475 new_prio_string = wxString( new_maps[4].c_str());
476 ApplyPriorityMap(priority_map_satellites, new_prio_string, 4);
477}
478
479void CommBridge::PresetPriorityContainer(PriorityContainer &pc,
480 const std::unordered_map<std::string, int> &priority_map){
481 // Extract some info from the preloaded map
482 // Find the key corresponding to priority 0, the highest
483 std::string key0;
484 for (auto& it: priority_map) {
485 if (it.second == 0)
486 key0 = it.first;
487 }
488
489 wxString this_key(key0.c_str());
490 wxStringTokenizer tkz(this_key, _T(";"));
491 wxString wxs_this_source = tkz.GetNextToken();
492 std::string source = wxs_this_source.ToStdString();
493 wxString wxs_this_identifier = tkz.GetNextToken();
494 std::string this_identifier = wxs_this_identifier.ToStdString();
495
496 wxStringTokenizer tka(wxs_this_source, _T(":"));
497 tka.GetNextToken();
498 int source_address = atoi(tka.GetNextToken().ToStdString().c_str());
499
500 pc.active_priority = 0;
501 pc.active_source = source;
502 pc.active_identifier = this_identifier;
503 pc.active_source_address = source_address;
504}
505
506
507void CommBridge::PresetPriorityContainers(){
508 PresetPriorityContainer(active_priority_position, priority_map_position);
509 PresetPriorityContainer(active_priority_velocity, priority_map_velocity);
510 PresetPriorityContainer(active_priority_heading, priority_map_heading);
511 PresetPriorityContainer(active_priority_variation, priority_map_variation);
512 PresetPriorityContainer(active_priority_satellites, priority_map_satellites);
513}
514
515
516bool CommBridge::HandleN2K_129029(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
517
518 std::vector<unsigned char> v = n2k_msg->payload;
519
520 // extract and verify PGN
521 uint64_t pgn = 0;
522 unsigned char *c = (unsigned char *)&pgn;
523 *c++ = v.at(3);
524 *c++ = v.at(4);
525 *c++ = v.at(5);
526
527 NavData temp_data;
528 ClearNavData(temp_data);
529
530 if (!m_decoder.DecodePGN129029(v, temp_data))
531 return false;
532
533 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)){
534 if (EvalPriority(n2k_msg, active_priority_position, priority_map_position)) {
535 gLat = temp_data.gLat;
536 gLon = temp_data.gLon;
537 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
538 }
539 }
540
541 if (temp_data.n_satellites >= 0){
542 if (EvalPriority(n2k_msg, active_priority_satellites, priority_map_satellites)) {
543 g_SatsInView = temp_data.n_satellites;
544 g_bSatValid = true;
545 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
546 }
547 }
548
549 SendBasicNavdata();
550 return true;
551}
552
553bool CommBridge::HandleN2K_129025(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
554
555 std::vector<unsigned char> v = n2k_msg->payload;
556
557 NavData temp_data;
558 ClearNavData(temp_data);
559
560 if (!m_decoder.DecodePGN129025(v, temp_data))
561 return false;
562
563 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)){
564 if (EvalPriority(n2k_msg, active_priority_position, priority_map_position)) {
565 gLat = temp_data.gLat;
566 gLon = temp_data.gLon;
567 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
568 }
569 }
570 //FIXME (dave) How to notify user of errors?
571 else{
572 }
573
574 SendBasicNavdata();
575 return true;
576}
577
578bool CommBridge::HandleN2K_129026(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
579
580 std::vector<unsigned char> v = n2k_msg->payload;
581
582 NavData temp_data;
583 ClearNavData(temp_data);
584
585 if (!m_decoder.DecodePGN129026(v, temp_data))
586 return false;
587
588 if (!N2kIsNA(temp_data.gSog)){ // gCog as reported by net may be NaN, but OK
589 if (EvalPriority(n2k_msg, active_priority_velocity, priority_map_velocity)) {
590 gSog = MS2KNOTS(temp_data.gSog);
591 if (N2kIsNA(temp_data.gCog))
592 gCog = NAN;
593 else
594 gCog = GeodesicRadToDeg(temp_data.gCog);
595 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
596 }
597 }
598 else{
599 }
600
601 SendBasicNavdata();
602 return true;
603}
604
605bool CommBridge::HandleN2K_127250(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
606
607 std::vector<unsigned char> v = n2k_msg->payload;
608
609 NavData temp_data;
610 ClearNavData(temp_data);
611
612 if (!m_decoder.DecodePGN127250(v, temp_data))
613 return false;
614
615 if (!N2kIsNA(temp_data.gVar)){
616 if (EvalPriority(n2k_msg, active_priority_variation, priority_map_variation)) {
617 gVar = GeodesicRadToDeg(temp_data.gVar);
618 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
619 }
620 }
621
622 if (!N2kIsNA(temp_data.gHdt)){
623 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
624 gHdt = GeodesicRadToDeg(temp_data.gHdt);
625 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
626 }
627 }
628
629 if (!N2kIsNA(temp_data.gHdm)){
630 gHdm = GeodesicRadToDeg(temp_data.gHdm);
631 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
632 MakeHDTFromHDM();
633 if(!std::isnan(gHdt))
634 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
635 }
636 }
637
638 SendBasicNavdata();
639 return true;
640}
641
642bool CommBridge::HandleN2K_129540(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
643
644 std::vector<unsigned char> v = n2k_msg->payload;
645
646 NavData temp_data;
647 ClearNavData(temp_data);
648
649 if (!m_decoder.DecodePGN129540(v, temp_data))
650 return false;
651
652 if (temp_data.n_satellites >= 0){
653 if (EvalPriority(n2k_msg, active_priority_satellites, priority_map_satellites)) {
654 g_SatsInView = temp_data.n_satellites;
655 g_bSatValid = true;
656 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
657 }
658 }
659
660 return true;
661}
662
663bool CommBridge::HandleN0183_RMC(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
664 std::string str = n0183_msg->payload;
665
666 NavData temp_data;
667 ClearNavData(temp_data);
668
669 if (!m_decoder.DecodeRMC(str, temp_data))
670 return false;
671
672 if (EvalPriority(n0183_msg, active_priority_position, priority_map_position)) {
673 gLat = temp_data.gLat;
674 gLon = temp_data.gLon;
675 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
676 }
677
678 if (EvalPriority(n0183_msg, active_priority_velocity, priority_map_velocity)) {
679 gSog = temp_data.gSog;
680 gCog = temp_data.gCog;
681 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
682 }
683
684 if (!std::isnan(temp_data.gVar)){
685 if (EvalPriority(n0183_msg, active_priority_variation, priority_map_variation)) {
686 gVar = temp_data.gVar;
687 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
688 }
689 }
690
691 SendBasicNavdata();
692 return true;
693}
694
695bool CommBridge::HandleN0183_HDT(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
696 std::string str = n0183_msg->payload;
697 NavData temp_data;
698 ClearNavData(temp_data);
699
700 if (!m_decoder.DecodeHDT(str, temp_data))
701 return false;
702
703 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
704 gHdt = temp_data.gHdt;
705 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
706 }
707
708
709 SendBasicNavdata();
710 return true;
711}
712
713bool CommBridge::HandleN0183_HDG(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
714 std::string str = n0183_msg->payload;
715 NavData temp_data;
716 ClearNavData(temp_data);
717
718 if (!m_decoder.DecodeHDG(str, temp_data)) return false;
719
720 bool bHDM = false;
721 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
722 gHdm = temp_data.gHdm;
723 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
724 bHDM = true;
725 }
726
727 if (!std::isnan(temp_data.gVar)){
728 if (EvalPriority(n0183_msg, active_priority_variation, priority_map_variation)) {
729 gVar = temp_data.gVar;
730 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
731 }
732 }
733
734 if (bHDM)
735 MakeHDTFromHDM();
736
737 SendBasicNavdata();
738 return true;
739}
740
741bool CommBridge::HandleN0183_HDM(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
742 std::string str = n0183_msg->payload;
743 NavData temp_data;
744 ClearNavData(temp_data);
745
746 if (!m_decoder.DecodeHDM(str, temp_data)) return false;
747
748 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
749 gHdm = temp_data.gHdm;
750 MakeHDTFromHDM();
751 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
752 }
753
754 SendBasicNavdata();
755 return true;
756}
757
758bool CommBridge::HandleN0183_VTG(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
759 std::string str = n0183_msg->payload;
760 NavData temp_data;
761 ClearNavData(temp_data);
762
763 if (!m_decoder.DecodeVTG(str, temp_data)) return false;
764
765 if (EvalPriority(n0183_msg, active_priority_velocity, priority_map_velocity)) {
766 gSog = temp_data.gSog;
767 gCog = temp_data.gCog;
768 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
769 }
770
771 SendBasicNavdata();
772 return true;
773}
774
775bool CommBridge::HandleN0183_GSV(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
776 std::string str = n0183_msg->payload;
777 NavData temp_data;
778 ClearNavData(temp_data);
779
780 if (!m_decoder.DecodeGSV(str, temp_data)) return false;
781
782 if (EvalPriority(n0183_msg, active_priority_satellites, priority_map_satellites)) {
783 if (temp_data.n_satellites >= 0){
784 g_SatsInView = temp_data.n_satellites;
785 g_bSatValid = true;
786
787 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
788 }
789 }
790
791 SendBasicNavdata();
792 return true;
793}
794
795bool CommBridge::HandleN0183_GGA(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
796 std::string str = n0183_msg->payload;
797 NavData temp_data;
798 ClearNavData(temp_data);
799
800 if (!m_decoder.DecodeGGA(str, temp_data)) return false;
801
802 if (EvalPriority(n0183_msg, active_priority_position, priority_map_position)) {
803 gLat = temp_data.gLat;
804 gLon = temp_data.gLon;
805 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
806 }
807
808 if (EvalPriority(n0183_msg, active_priority_satellites, priority_map_satellites)) {
809 if (temp_data.n_satellites >= 0){
810 g_SatsInView = temp_data.n_satellites;
811 g_bSatValid = true;
812
813 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
814 }
815 }
816
817 SendBasicNavdata();
818 return true;
819}
820
821bool CommBridge::HandleN0183_GLL(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
822 std::string str = n0183_msg->payload;
823 NavData temp_data;
824 ClearNavData(temp_data);
825
826 if (!m_decoder.DecodeGLL(str, temp_data)) return false;
827
828 if (EvalPriority(n0183_msg, active_priority_position, priority_map_position)) {
829 gLat = temp_data.gLat;
830 gLon = temp_data.gLon;
831 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
832 }
833
834 SendBasicNavdata();
835 return true;
836}
837
838bool CommBridge::HandleN0183_AIVDO(
839 std::shared_ptr<const Nmea0183Msg> n0183_msg) {
840 std::string str = n0183_msg->payload;
841
842 GenericPosDatEx gpd;
843 wxString sentence(str.c_str());
844
845 AisError nerr = AIS_GENERIC_ERROR;
846 nerr = DecodeSingleVDO(sentence, &gpd);
847
848 if (nerr == AIS_NoError) {
849
850 if (!std::isnan(gpd.kLat) && !std::isnan(gpd.kLon)){
851 if (EvalPriority(n0183_msg, active_priority_position, priority_map_position)) {
852 gLat = gpd.kLat;
853 gLon = gpd.kLon;
854 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
855 }
856 }
857
858 if (!std::isnan(gpd.kCog) && !std::isnan(gpd.kSog)){
859 if (EvalPriority(n0183_msg, active_priority_velocity, priority_map_velocity)) {
860 gSog = gpd.kSog;
861 gCog = gpd.kCog;
862 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
863 }
864 }
865
866 if (!std::isnan(gpd.kHdt)) {
867 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
868 gHdt = gpd.kHdt;
869 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
870 }
871 }
872
873 SendBasicNavdata();
874 }
875 return true;
876}
877
878bool CommBridge::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg){
879 std::string str = sK_msg->raw_message;
880
881 // Here we ignore messages involving contexts other than ownship
882 if (sK_msg->context_self != sK_msg->context)
883 return false;
884
885 g_ownshipMMSI_SK = sK_msg->context_self;
886
887 NavData temp_data;
888 ClearNavData(temp_data);
889
890 if (!m_decoder.DecodeSignalK(str, temp_data)) return false;
891
892
893 if (!std::isnan(temp_data.gLat) && !std::isnan(temp_data.gLon)){
894 if (EvalPriority(sK_msg, active_priority_position, priority_map_position)) {
895 gLat = temp_data.gLat;
896 gLon = temp_data.gLon;
897 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
898 }
899 }
900
901 if (!std::isnan(temp_data.gSog)){
902 if (EvalPriority(sK_msg, active_priority_velocity, priority_map_velocity)) {
903 gSog = temp_data.gSog;
904 if((gSog > 0) && !std::isnan(temp_data.gCog))
905 gCog = temp_data.gCog;
906 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
907 }
908 }
909
910 if (!std::isnan(temp_data.gHdt)){
911 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
912 gHdt = temp_data.gHdt;
913 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
914 }
915 }
916
917 if (!std::isnan(temp_data.gHdm)){
918 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
919 gHdm = temp_data.gHdm;
920 MakeHDTFromHDM();
921 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
922 }
923 }
924
925 if (!std::isnan(temp_data.gVar)){
926 if (EvalPriority(sK_msg, active_priority_variation, priority_map_variation)) {
927 gVar = temp_data.gVar;
928 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
929 }
930 }
931
932 if (temp_data.n_satellites > 0){
933 if (EvalPriority(sK_msg, active_priority_satellites, priority_map_satellites)) {
934 g_SatsInView = temp_data.n_satellites;
935 g_bSatValid = true;
936
937 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
938 }
939 }
940
941 SendBasicNavdata();
942 return true;
943}
944
945
946void CommBridge::InitializePriorityContainers(){
947 active_priority_position.active_priority = 0;
948 active_priority_velocity.active_priority = 0;
949 active_priority_heading.active_priority = 0;
950 active_priority_variation.active_priority = 0;
951 active_priority_satellites.active_priority = 0;
952
953 active_priority_position.active_source.clear();
954 active_priority_velocity.active_source.clear();
955 active_priority_heading.active_source.clear();
956 active_priority_variation.active_source.clear();
957 active_priority_satellites.active_source.clear();
958
959 active_priority_position.active_identifier.clear();
960 active_priority_velocity.active_identifier.clear();
961 active_priority_heading.active_identifier.clear();
962 active_priority_variation.active_identifier.clear();
963 active_priority_satellites.active_identifier.clear();
964
965 active_priority_position.pcclass = "position";
966 active_priority_velocity.pcclass = "velocity";
967 active_priority_heading.pcclass = "heading";
968 active_priority_variation.pcclass = "variation";
969 active_priority_satellites.pcclass = "satellites";
970
971 active_priority_position.active_source_address = -1;
972 active_priority_velocity.active_source_address = -1;
973 active_priority_heading.active_source_address = -1;
974 active_priority_variation.active_source_address = -1;
975 active_priority_satellites.active_source_address = -1;
976
977 active_priority_void.active_priority = -1;
978
979 }
980
981void CommBridge::ClearPriorityMaps(){
982 priority_map_position.clear();
983 priority_map_velocity.clear();
984 priority_map_heading.clear();
985 priority_map_variation.clear();
986 priority_map_satellites.clear();
987}
988
989PriorityContainer& CommBridge::GetPriorityContainer(const std::string category){
990 if (!category.compare("position"))
991 return active_priority_position;
992 else if (!category.compare("velocity"))
993 return active_priority_velocity;
994 else if (!category.compare("heading"))
995 return active_priority_heading;
996 else if (!category.compare("variation"))
997 return active_priority_variation;
998 else if (!category.compare("satellites"))
999 return active_priority_satellites;
1000 else
1001 return active_priority_void;
1002}
1003
1004void CommBridge::UpdateAndApplyMaps(std::vector<std::string> new_maps){
1005 ApplyPriorityMaps(new_maps);
1006 SaveConfig();
1007 PresetPriorityContainers();
1008}
1009
1010
1011bool CommBridge::LoadConfig( void )
1012{
1013 if( TheBaseConfig() ) {
1014 TheBaseConfig()->SetPath("/Settings/CommPriority");
1015
1016 std::vector<std::string> new_maps;
1017 std::string s_prio;
1018 wxString pri_string;
1019
1020 TheBaseConfig()->Read("PriorityPosition", &pri_string );
1021 s_prio = std::string(pri_string.c_str());
1022 new_maps.push_back(s_prio);
1023
1024 TheBaseConfig()->Read("PriorityVelocity", &pri_string );
1025 s_prio = std::string(pri_string.c_str());
1026 new_maps.push_back(s_prio);
1027
1028 TheBaseConfig()->Read("PriorityHeading", &pri_string );
1029 s_prio = std::string(pri_string.c_str());
1030 new_maps.push_back(s_prio);
1031
1032 TheBaseConfig()->Read("PriorityVariation", &pri_string );
1033 s_prio = std::string(pri_string.c_str());
1034 new_maps.push_back(s_prio);
1035
1036 TheBaseConfig()->Read("PrioritySatellites", &pri_string );
1037 s_prio = std::string(pri_string.c_str());
1038 new_maps.push_back(s_prio);
1039
1040 ApplyPriorityMaps(new_maps);
1041 }
1042 return true;
1043}
1044
1045bool CommBridge::SaveConfig( void )
1046{
1047 if( TheBaseConfig() ) {
1048 TheBaseConfig()->SetPath("/Settings/CommPriority");
1049
1050 wxString pri_string;
1051 pri_string = wxString(GetPriorityMap(priority_map_position).c_str());
1052 TheBaseConfig()->Write( "PriorityPosition", pri_string );
1053
1054 pri_string = wxString(GetPriorityMap(priority_map_velocity).c_str());
1055 TheBaseConfig()->Write( "PriorityVelocity", pri_string );
1056
1057 pri_string = wxString(GetPriorityMap(priority_map_heading).c_str());
1058 TheBaseConfig()->Write( "PriorityHeading", pri_string );
1059
1060 pri_string = wxString(GetPriorityMap(priority_map_variation).c_str());
1061 TheBaseConfig()->Write( "PriorityVariation", pri_string );
1062
1063 pri_string = wxString(GetPriorityMap(priority_map_satellites).c_str());
1064 TheBaseConfig()->Write( "PrioritySatellites", pri_string );
1065 }
1066
1067 return true;
1068}
1069
1070
1071std::string CommBridge::GetPriorityKey(std::shared_ptr <const NavMsg> msg){
1072 std::string source = msg->source->to_string();
1073 std::string listener_key = msg->key();
1074
1075
1076 std::string this_identifier;
1077 std::string this_address("0");
1078 if(msg->bus == NavAddr::Bus::N0183){
1079 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
1080 if (msg_0183){
1081 this_identifier = msg_0183->talker;
1082 this_identifier += msg_0183->type;
1083 }
1084 }
1085 else if(msg->bus == NavAddr::Bus::N2000){
1086 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
1087 if (msg_n2k){
1088 this_identifier = msg_n2k->PGN.to_string();
1089 unsigned char n_source = msg_n2k->payload.at(7);
1090 char ss[4];
1091 sprintf(ss,"%d",n_source);
1092 this_address = std::string(ss);
1093 }
1094 }
1095 else if(msg->bus == NavAddr::Bus::Signalk){
1096 auto msg_sk = std::dynamic_pointer_cast<const SignalkMsg>(msg);
1097 if (msg_sk){
1098 this_identifier = "signalK";
1099 }
1100 }
1101
1102
1103 return source + ":" + this_address + ";" + this_identifier;
1104
1105}
1106
1107bool CommBridge::EvalPriority(std::shared_ptr <const NavMsg> msg,
1108 PriorityContainer& active_priority,
1109 std::unordered_map<std::string, int>& priority_map) {
1110
1111 std::string this_key = GetPriorityKey(msg);
1112 if (debug_priority) printf("This Key: %s\n", this_key.c_str());
1113
1114 // Pull some identifiers from the unique key
1115 wxStringTokenizer tkz(this_key, _T(";"));
1116 wxString wxs_this_source = tkz.GetNextToken();
1117 std::string source = wxs_this_source.ToStdString();
1118 wxString wxs_this_identifier = tkz.GetNextToken();
1119 std::string this_identifier = wxs_this_identifier.ToStdString();
1120
1121 wxStringTokenizer tka(wxs_this_source, _T(":"));
1122 tka.GetNextToken();
1123 int source_address = atoi(tka.GetNextToken().ToStdString().c_str());
1124
1125 // Fetch the established priority for the message
1126 int this_priority;
1127
1128 auto it = priority_map.find(this_key);
1129 if (it == priority_map.end()) {
1130 // Not found, so make it default highest priority
1131 priority_map[this_key] = 0;
1132 }
1133
1134 this_priority = priority_map[this_key];
1135
1136 for (auto it = priority_map.begin(); it != priority_map.end(); it++) {
1137 if (debug_priority) printf(" priority_map: %s %d\n", it->first.c_str(), it->second);
1138 }
1139
1140 //Incoming message priority lower than currently active priority?
1141 // If so, drop the message
1142 if (this_priority > active_priority.active_priority){
1143 if (debug_priority) printf(" Drop low priority: %s %d %d \n", source.c_str(), this_priority,
1144 active_priority.active_priority);
1145 return false;
1146 }
1147
1148 // A channel returning, after being watchdogged out.
1149 if (this_priority < active_priority.active_priority){
1150 active_priority.active_priority = this_priority;
1151 active_priority.active_source = source;
1152 active_priority.active_identifier = this_identifier;
1153 active_priority.active_source_address = source_address;
1154
1155 if (debug_priority) printf(" Restoring high priority: %s %d\n", source.c_str(), this_priority);
1156 return true;
1157 }
1158
1159
1160 // Do we see two sources with the same priority?
1161 // If so, we take the first one, and deprioritize this one.
1162
1163 if (active_priority.active_source.size()){
1164
1165 if (debug_priority) printf("source: %s\n", source.c_str());
1166 if (debug_priority) printf("active_source: %s\n", active_priority.active_source.c_str());
1167
1168 if (source.compare(active_priority.active_source) != 0){
1169
1170 // Auto adjust the priority of the this message down
1171 //First, find the lowest priority in use in this map
1172 int lowest_priority = -10; // safe enough
1173 for (auto it = priority_map.begin(); it != priority_map.end(); it++) {
1174 if (it->second > lowest_priority)
1175 lowest_priority = it->second;
1176 }
1177
1178 priority_map[this_key] = lowest_priority + 1;
1179 if (debug_priority) printf(" Lowering priority A: %s :%d\n", source.c_str(), priority_map[this_key]);
1180 return false;
1181 }
1182 }
1183
1184 // For N0183 message, has the Mnemonic (id) changed?
1185 // Example: RMC and AIVDO from same source.
1186
1187
1188 if(msg->bus == NavAddr::Bus::N0183){
1189 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
1190 if (msg_0183){
1191 if (active_priority.active_identifier.size()){
1192
1193 if (debug_priority) printf("this_identifier: %s\n", this_identifier.c_str());
1194 if (debug_priority) printf("active_priority.active_identifier: %s\n", active_priority.active_identifier.c_str());
1195
1196 if (this_identifier.compare(active_priority.active_identifier) != 0){
1197 // if necessary, auto adjust the priority of the this message down
1198 //and drop it
1199 if (priority_map[this_key] == active_priority.active_priority){
1200 int lowest_priority = -10; // safe enough
1201 for (auto it = priority_map.begin(); it != priority_map.end(); it++) {
1202 if (it->second > lowest_priority)
1203 lowest_priority = it->second;
1204 }
1205
1206 priority_map[this_key] = lowest_priority + 1;
1207 if (debug_priority) printf(" Lowering priority B: %s :%d\n", source.c_str(), priority_map[this_key]);
1208 }
1209
1210 return false;
1211 }
1212 }
1213 }
1214 }
1215
1216 // Similar for n2k PGN...
1217
1218 else if(msg->bus == NavAddr::Bus::N2000){
1219 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
1220 if (msg_n2k){
1221 if (active_priority.active_identifier.size()){
1222 if (this_identifier.compare(active_priority.active_identifier) != 0){
1223 // if necessary, auto adjust the priority of the this message down
1224 //and drop it
1225 if (priority_map[this_key] == active_priority.active_priority){
1226 int lowest_priority = -10; // safe enough
1227 for (auto it = priority_map.begin(); it != priority_map.end(); it++) {
1228 if (it->second > lowest_priority)
1229 lowest_priority = it->second;
1230 }
1231
1232 priority_map[this_key] = lowest_priority + 1;
1233 if (debug_priority) printf(" Lowering priority: %s :%d\n", source.c_str(), priority_map[this_key]);
1234 }
1235
1236 return false;
1237 }
1238 }
1239 }
1240 }
1241
1242
1243 // Update the records
1244 active_priority.active_source = source;
1245 active_priority.active_identifier = this_identifier;
1246 active_priority.active_source_address = source_address;
1247 if (debug_priority) printf(" Accepting high priority: %s %d\n", source.c_str(), this_priority);
1248
1249 return true;
1250}
void Notify(std::shared_ptr< const AppMsg > msg)
Send message to everyone listening to given message type.
Handle most incoming messages and make them available for other parties.
Definition: comm_bridge.h:69
A regular Nmea0183 message.
Definition: comm_navmsg.h:241
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
Definition: comm_navmsg.h:217
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Definition: observable.cpp:98
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1615
A parsed, raw SignalK message.
Definition: comm_navmsg.h:283
Actual own ship state.
Definition: comm_decoder.h:42
Data for selected source from multiple candidates.
Definition: comm_bridge.h:43