OpenCPN Partial API docs
Loading...
Searching...
No Matches
comm_bridge.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2022 by David Register *
3 * Copyright (C) 2022 - 2025 by Alec Leamas *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include <sstream>
26#include <string>
27
28#include <wx/wxprec.h>
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif
32
33#include <wx/event.h>
34#include <wx/string.h>
35#include <wx/tokenzr.h>
36#include <wx/fileconf.h>
37
38#include "model/comm_ais.h"
40#include "model/comm_bridge.h"
43#include "model/comm_vars.h"
44#include "model/config_vars.h"
45#include "model/cutil.h"
46#include "model/gui.h"
47#include "model/idents.h"
48#include "model/ocpn_types.h"
49#include "model/own_ship.h"
50#include "model/multiplexer.h"
52
53#define N_ACTIVE_LOG_WATCHDOG 300
54
55using std::string;
56
57static bool debug_priority = false;
58
59void ClearNavData(NavData& d) {
60 d.gLat = NAN;
61 d.gLon = NAN;
62 d.gSog = NAN;
63 d.gCog = NAN;
64 d.gHdt = NAN;
65 d.gHdm = NAN;
66 d.gVar = NAN;
67 d.n_satellites = -1;
68 d.SID = 0;
69}
70
71static NmeaLog* GetDataMonitor() {
72 auto w = wxWindow::FindWindowByName(kDataMonitorWindowName);
73 return dynamic_cast<NmeaLog*>(w);
74}
75
76static BridgeLogCallbacks GetLogCallbacks() {
77 BridgeLogCallbacks log_callbacks;
78 log_callbacks.log_is_active = [&]() {
79 auto log = GetDataMonitor();
80 return log && log->IsVisible();
81 };
82 log_callbacks.log_message = [&](const Logline& ll) {
83 NmeaLog* monitor = GetDataMonitor();
84 if (monitor && monitor->IsVisible()) monitor->Add(ll);
85 };
86 return log_callbacks;
87}
88
89class AppNavMsg : public NavMsg {
90public:
91 AppNavMsg(const std::shared_ptr<const AppMsg>& msg, const string& name)
92 : NavMsg(NavAddr::Bus::AppMsg,
93 std::make_shared<const NavAddrPlugin>("AppMsg")),
94 m_to_string(msg->to_string()),
95 m_name(name) {}
96
97 [[nodiscard]] string to_string() const override { return m_to_string; }
98
99 [[nodiscard]] string key() const override { return "appmsg::" + m_name; }
100
101 const string m_to_string;
102 const string m_name;
103};
104
105static void LogAppMsg(const std::shared_ptr<const AppMsg>& msg,
106 const string& name, const BridgeLogCallbacks& log_cb) {
107 if (!log_cb.log_is_active()) return;
108 auto navmsg = std::make_shared<AppNavMsg>(msg, "basic-navdata");
109 NavmsgStatus ns;
110 Logline ll(navmsg, ns);
111 log_cb.log_message(ll);
112}
113
118static void SendBasicNavdata(int vflag,
119 const BridgeLogCallbacks& log_callbacks) {
120 auto msg = std::make_shared<BasicNavDataMsg>(
121 gLat, gLon, gSog, gCog, gVar, gHdt, vflag, wxDateTime::Now().GetTicks());
122 clock_gettime(CLOCK_MONOTONIC, &msg->set_time);
123 LogAppMsg(msg, "basic-navdata", log_callbacks);
124 AppMsgBus::GetInstance().Notify(std::move(msg));
125}
126
127static inline double GeodesicRadToDeg(double rads) {
128 return rads * 180.0 / M_PI;
129}
130
131static inline double MS2KNOTS(double ms) { return ms * 1.9438444924406; }
132
133static string GetPriorityKey(const NavMsgPtr& msg) {
134 string key;
135
136 string this_identifier;
137 string this_address("0");
138 if (msg->bus == NavAddr::Bus::N0183) {
139 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
140 if (msg_0183) {
141 string source = msg->source->to_string();
142 if (msg_0183->talker == "WM" && msg_0183->type == "HVD")
143 source = "WMM plugin";
144 this_identifier = msg_0183->talker;
145 this_identifier += msg_0183->type;
146 key = source + ":" + this_address + ";" + this_identifier;
147 }
148 } else if (msg->bus == NavAddr::Bus::N2000) {
149 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
150 if (msg_n2k) {
151 this_identifier = msg_n2k->PGN.to_string();
152 unsigned char n_source = msg_n2k->payload.at(7);
153 wxString km = wxString::Format("N2k device address: %d", n_source);
154 key = km.ToStdString() + " ; " + "PGN: " + this_identifier;
155 }
156 } else if (msg->bus == NavAddr::Bus::Signalk) {
157 auto msg_sk = std::dynamic_pointer_cast<const SignalkMsg>(msg);
158 if (msg_sk) {
159 auto addr_sk =
160 std::static_pointer_cast<const NavAddrSignalK>(msg->source);
161 string source = addr_sk->to_string();
162 key = source; // Simplified, parsing sK for more info is expensive
163 }
164 }
165
166 return key;
167}
168
169static void PresetPriorityContainer(PriorityContainer& pc,
170 const PriorityMap& priority_map) {
171 // Extract some info from the preloaded map
172 // Find the key corresponding to priority 0, the highest
173 string key0;
174 for (const auto& it : priority_map) {
175 if (it.second == 0) key0 = it.first;
176 }
177
178 wxString this_key(key0.c_str());
179 wxStringTokenizer tkz(this_key, ";");
180 string source = tkz.GetNextToken().ToStdString();
181 string this_identifier = tkz.GetNextToken().ToStdString();
182
183 wxStringTokenizer tka(source, ":");
184 tka.GetNextToken();
185 std::stringstream ss;
186 ss << tka.GetNextToken();
187 ss >> pc.active_source_address;
188 pc.active_priority = 0;
189 pc.active_source = source;
190 pc.active_identifier = this_identifier;
191 pc.recent_active_time = -1;
192}
193
194static void ApplyPriorityMap(PriorityMap& priority_map,
195 const wxString& new_prio, int category) {
196 priority_map.clear();
197 wxStringTokenizer tk(new_prio, "|");
198 int index = 0;
199 while (tk.HasMoreTokens()) {
200 wxString entry = tk.GetNextToken();
201 string s_entry(entry.c_str());
202 priority_map[s_entry] = index;
203 index++;
204 }
205}
206
207static string GetPriorityMap(const PriorityMap& map) {
208#define MAX_SOURCES 10
209 string sa[MAX_SOURCES];
210 string result;
211
212 for (auto& it : map) {
213 if ((it.second >= 0) && (it.second < MAX_SOURCES)) sa[it.second] = it.first;
214 }
215
216 // build the packed string result
217 for (int i = 0; i < MAX_SOURCES; i++) {
218 if (!sa[i].empty()) {
219 result += sa[i];
220 result += "|";
221 }
222 }
223
224 return result;
225}
226
227static bool IsNextLowerPriorityAvailable(const PriorityMap& map,
228 const PriorityContainer& pc) {
229 int best_prio = 100;
230 for (auto& it : map) {
231 if (it.second > pc.active_priority) {
232 best_prio = wxMin(best_prio, it.second);
233 }
234 }
235 return best_prio != pc.active_priority;
236}
237
238static void SelectNextLowerPriority(const PriorityMap& map,
239 PriorityContainer& pc) {
240 int best_prio = 100;
241 for (const auto& it : map) {
242 if (it.second > pc.active_priority) {
243 best_prio = wxMin(best_prio, it.second);
244 }
245 }
246 pc.active_priority = best_prio;
247 pc.active_source.clear();
248 pc.active_identifier.clear();
249}
250
251// CommBridge implementation
252
253CommBridge& CommBridge::GetInstance() {
254 static bool is_initialized = false;
255 static CommBridge the_instance;
256 if (!is_initialized) {
257 the_instance.Initialize();
258 is_initialized = true;
259 }
260 return the_instance;
261}
262
263CommBridge::CommBridge()
264 : wxEvtHandler(),
265 // every 60 minutes, reduced after first position Rx
266 active_priority_position("position"),
267 active_priority_velocity("velocity"),
268 active_priority_heading("heading"),
269 active_priority_variation("variation"),
270 active_priority_satellites("satellites"),
271 active_priority_void("", -1),
272 m_n_log_watchdog_period(3600),
273 m_last_position_priority(0) {
274 Bind(wxEVT_TIMER, [&](wxTimerEvent&) { OnWatchdogTimer(); });
275}
276CommBridge::~CommBridge() = default;
277
278bool CommBridge::Initialize() {
279 m_log_callbacks = GetLogCallbacks();
280 ClearPriorityMaps();
281
282 LoadConfig();
283 PresetPriorityContainers();
284
285 // Clear the watchdogs
286 PresetWatchdogs();
287
288 m_watchdog_timer.SetOwner(this, WATCHDOG_TIMER);
289 m_watchdog_timer.Start(1000, wxTIMER_CONTINUOUS);
290
291 InitCommListeners();
292
293 // Initialize a listener for driver state changes
294 m_driver_change_lstnr.Init(
295 CommDriverRegistry::GetInstance().evt_driverlist_change,
296 [&](const wxCommandEvent& ev) { OnDriverStateChange(); });
297
298 return true;
299}
300
301void CommBridge::PresetWatchdogs() {
302 m_watchdogs.position_watchdog =
303 20; // A bit longer watchdog for startup latency.
304 m_watchdogs.velocity_watchdog = 20;
305 m_watchdogs.variation_watchdog = 20;
306 m_watchdogs.heading_watchdog = 20;
307 m_watchdogs.satellite_watchdog = 20;
308}
309
310void CommBridge::OnWatchdogTimer() {
311 // Update and check watchdog timer for GPS data source
312 m_watchdogs.position_watchdog--;
313 if (m_watchdogs.position_watchdog <= 0) {
314 if (m_watchdogs.position_watchdog % 5 == 0) {
315 // Send AppMsg telling of watchdog expiry
316 auto msg = std::make_shared<GPSWatchdogMsg>(
317 GPSWatchdogMsg::WDSource::position, m_watchdogs.position_watchdog);
318 auto& msgbus = AppMsgBus::GetInstance();
319 LogAppMsg(msg, "watchdog", m_log_callbacks);
320 msgbus.Notify(std::move(msg));
321
322 if (m_watchdogs.position_watchdog % m_n_log_watchdog_period == 0) {
323 wxString logmsg;
324 logmsg.Printf(" ***GPS Watchdog timeout at Lat:%g Lon: %g", gLat,
325 gLon);
326 wxLogMessage(logmsg);
327 }
328 }
329
330 gSog = NAN;
331 gCog = NAN;
332 gRmcDate.Empty();
333 gRmcTime.Empty();
334 active_priority_position.recent_active_time = -1;
335
336 // Are there any other lower priority sources?
337 // If so, adopt that one.
338 if (IsNextLowerPriorityAvailable(priority_map_position,
339 active_priority_position)) {
340 SelectNextLowerPriority(priority_map_position, active_priority_position);
341 }
342 }
343
344 // Update and check watchdog timer for SOG/COG data source
345 m_watchdogs.velocity_watchdog--;
346 if (m_watchdogs.velocity_watchdog <= 0) {
347 gSog = NAN;
348 gCog = NAN;
349 active_priority_velocity.recent_active_time = -1;
350
351 if (g_nNMEADebug && (m_watchdogs.velocity_watchdog == 0))
352 wxLogMessage(" ***Velocity Watchdog timeout...");
353 if (m_watchdogs.velocity_watchdog % 5 == 0) {
354 // Send AppMsg telling of watchdog expiry
355 auto msg = std::make_shared<GPSWatchdogMsg>(
356 GPSWatchdogMsg::WDSource::velocity, m_watchdogs.velocity_watchdog);
357 auto& msgbus = AppMsgBus::GetInstance();
358 msgbus.Notify(std::move(msg));
359 }
360 // Are there any other lower priority sources?
361 // If so, adopt that one.
362 SelectNextLowerPriority(priority_map_velocity, active_priority_velocity);
363 }
364
365 // Update and check watchdog timer for True Heading data source
366 m_watchdogs.heading_watchdog--;
367 if (m_watchdogs.heading_watchdog <= 0) {
368 gHdt = NAN;
369 active_priority_heading.recent_active_time = -1;
370 if (g_nNMEADebug && (m_watchdogs.heading_watchdog == 0))
371 wxLogMessage(" ***HDT Watchdog timeout...");
372
373 // Are there any other lower priority sources?
374 // If so, adopt that one.
375 SelectNextLowerPriority(priority_map_heading, active_priority_heading);
376 }
377
378 // Update and check watchdog timer for Magnetic Variation data source
379 m_watchdogs.variation_watchdog--;
380 if (m_watchdogs.variation_watchdog <= 0) {
381 g_bVAR_Rx = false;
382 active_priority_variation.recent_active_time = -1;
383
384 if (g_nNMEADebug && (m_watchdogs.variation_watchdog == 0))
385 wxLogMessage(" ***VAR Watchdog timeout...");
386
387 // Are there any other lower priority sources?
388 // If so, adopt that one.
389 SelectNextLowerPriority(priority_map_variation, active_priority_variation);
390 }
391
392 // Update and check watchdog timer for GSV, GGA and SignalK (Satellite data)
393 m_watchdogs.satellite_watchdog--;
394 if (m_watchdogs.satellite_watchdog <= 0) {
395 g_bSatValid = false;
396 g_SatsInView = 0;
397 g_priSats = 99;
398 active_priority_satellites.recent_active_time = -1;
399
400 if (g_nNMEADebug && (m_watchdogs.satellite_watchdog == 0))
401 wxLogMessage(" ***SAT Watchdog timeout...");
402
403 // Are there any other lower priority sources?
404 // If so, adopt that one.
405 SelectNextLowerPriority(priority_map_satellites,
406 active_priority_satellites);
407 }
408}
409
410void CommBridge::MakeHDTFromHDM() {
411 // Here is the one place we try to create gHdt from gHdm and gVar,
412
413 if (!std::isnan(gHdm)) {
414 // Set gVar if needed from manual entry. gVar will be overwritten if
415 // WMM plugin is available
416 if (std::isnan(gVar) && (g_UserVar != 0.0)) gVar = g_UserVar;
417 gHdt = gHdm + gVar;
418 if (!std::isnan(gHdt)) {
419 if (gHdt < 0)
420 gHdt += 360.0;
421 else if (gHdt >= 360)
422 gHdt -= 360.0;
423
424 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
425 }
426 }
427}
428
429void CommBridge::InitCommListeners() {
430 // Initialize the comm listeners
431
432 // GNSS Position Data PGN 129029
433 m_n2k_129029_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129029)),
434 [&](const ObservedEvt& ev) {
435 HandleN2K_129029(UnpackEvtPointer<Nmea2000Msg>(ev));
436 });
437
438 // Position rapid PGN 129025
439 m_n2k_129025_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129025)),
440 [&](const ObservedEvt& ev) {
441 HandleN2K_129025(UnpackEvtPointer<Nmea2000Msg>(ev));
442 });
443
444 // COG SOG rapid PGN 129026
445 m_n2k_129026_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129026)),
446 [&](const ObservedEvt& ev) {
447 HandleN2K_129026(UnpackEvtPointer<Nmea2000Msg>(ev));
448 });
449
450 // Heading rapid PGN 127250
451 m_n2k_127250_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(127250)),
452 [&](const ObservedEvt& ev) {
453 HandleN2K_127250(UnpackEvtPointer<Nmea2000Msg>(ev));
454 });
455 // Variation PGN 127258
456 m_n2k_127258_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(127258)),
457 [&](const ObservedEvt& ev) {
458 HandleN2K_127258(UnpackEvtPointer<Nmea2000Msg>(ev));
459 });
460
461 // GNSS Satellites in View PGN 129540
462 m_n2k_129540_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129540)),
463 [&](const ObservedEvt& ev) {
464 HandleN2K_129540(UnpackEvtPointer<Nmea2000Msg>(ev));
465 });
466
467 // NMEA0183
468 // RMC
469 Nmea0183Msg n0183_msg_RMC("RMC");
470 m_n0183_rmc_lstnr.Init(Nmea0183Msg("RMC"), [&](const ObservedEvt& ev) {
471 HandleN0183_RMC(UnpackEvtPointer<Nmea0183Msg>(ev));
472 });
473
474 // THS
475 m_n0183_ths_lstnr.Init(Nmea0183Msg("THS"), [&](const ObservedEvt& ev) {
476 HandleN0183_THS(UnpackEvtPointer<Nmea0183Msg>(ev));
477 });
478
479 // HDT
480 m_n0183_hdt_lstnr.Init(Nmea0183Msg("HDT"), [&](const ObservedEvt& ev) {
481 HandleN0183_HDT(UnpackEvtPointer<Nmea0183Msg>(ev));
482 });
483
484 // HDG
485 m_n0183_hdg_lstnr.Init(Nmea0183Msg("HDG"), [&](const ObservedEvt& ev) {
486 HandleN0183_HDG(UnpackEvtPointer<Nmea0183Msg>(ev));
487 });
488
489 // HDM
490 m_n0183_hdm_lstnr.Init(Nmea0183Msg("HDM"), [&](const ObservedEvt& ev) {
491 HandleN0183_HDM(UnpackEvtPointer<Nmea0183Msg>(ev));
492 });
493
494 // HVD
495 m_n0183_hvd_lstnr.Init(Nmea0183Msg("HVD"), [&](const ObservedEvt& ev) {
496 HandleN0183_HVD(UnpackEvtPointer<Nmea0183Msg>(ev));
497 });
498
499 // VTG
500 m_n0183_vtg_lstnr.Init(Nmea0183Msg("VTG"), [&](const ObservedEvt& ev) {
501 HandleN0183_VTG(UnpackEvtPointer<Nmea0183Msg>(ev));
502 });
503
504 // GSV
505 m_n0183_gsv_lstnr.Init(Nmea0183Msg("GSV"), [&](const ObservedEvt& ev) {
506 HandleN0183_GSV(UnpackEvtPointer<Nmea0183Msg>(ev));
507 });
508
509 // GGA
510 m_n0183_gga_lstnr.Init(Nmea0183Msg("GGA"), [&](const ObservedEvt& ev) {
511 HandleN0183_GGA(UnpackEvtPointer<Nmea0183Msg>(ev));
512 });
513
514 // GLL
515 Nmea0183Msg n0183_msg_GLL("GLL");
516 m_n0183_gll_lstnr.Init(Nmea0183Msg("GLL"), [&](const ObservedEvt& ev) {
517 HandleN0183_GLL(UnpackEvtPointer<Nmea0183Msg>(ev));
518 });
519
520 // AIVDO
521 m_n0183_aivdo_lstnr.Init(Nmea0183Msg("AIVDO"), [&](const ObservedEvt& ev) {
522 HandleN0183_AIVDO(UnpackEvtPointer<Nmea0183Msg>(ev));
523 });
524
525 // SignalK
526 m_signal_k_lstnr.Init(SignalkMsg(), [&](const ObservedEvt& ev) {
527 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
528 });
529}
530
531void CommBridge::OnDriverStateChange() {
532 // Reset all active priority states
533 PresetPriorityContainers();
534}
535
536std::vector<string> CommBridge::GetPriorityMaps() const {
537 std::vector<string> result;
538 result.push_back(GetPriorityMap(priority_map_position));
539 result.push_back(GetPriorityMap(priority_map_velocity));
540 result.push_back(GetPriorityMap(priority_map_heading));
541 result.push_back(GetPriorityMap(priority_map_variation));
542 result.push_back(GetPriorityMap(priority_map_satellites));
543 return result;
544}
545
546void CommBridge::ApplyPriorityMaps(const std::vector<string>& new_maps) {
547 wxString new_prio_string = wxString(new_maps[0].c_str());
548 ApplyPriorityMap(priority_map_position, new_prio_string, 0);
549
550 new_prio_string = wxString(new_maps[1].c_str());
551 ApplyPriorityMap(priority_map_velocity, new_prio_string, 1);
552
553 new_prio_string = wxString(new_maps[2].c_str());
554 ApplyPriorityMap(priority_map_heading, new_prio_string, 2);
555
556 new_prio_string = wxString(new_maps[3].c_str());
557 ApplyPriorityMap(priority_map_variation, new_prio_string, 3);
558
559 new_prio_string = wxString(new_maps[4].c_str());
560 ApplyPriorityMap(priority_map_satellites, new_prio_string, 4);
561}
562
563void CommBridge::PresetPriorityContainers() {
564 PresetPriorityContainer(active_priority_position, priority_map_position);
565 PresetPriorityContainer(active_priority_velocity, priority_map_velocity);
566 PresetPriorityContainer(active_priority_heading, priority_map_heading);
567 PresetPriorityContainer(active_priority_variation, priority_map_variation);
568 PresetPriorityContainer(active_priority_satellites, priority_map_satellites);
569}
570
571bool CommBridge::HandleN2K_129029(const N2000MsgPtr& n2k_msg) {
572 std::vector<unsigned char> v = n2k_msg->payload;
573
574 // extract and verify PGN
575 NavData temp_data;
576 ClearNavData(temp_data);
577
578 if (!m_decoder.DecodePGN129029(v, temp_data)) return false;
579
580 int valid_flag = 0;
581 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
582 if (EvalPriority(n2k_msg, active_priority_position,
583 priority_map_position)) {
584 gLat = temp_data.gLat;
585 gLon = temp_data.gLon;
586 valid_flag += POS_UPDATE;
587 valid_flag += POS_VALID;
588 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
589 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
590 }
591 }
592
593 if (temp_data.n_satellites >= 0) {
594 if (EvalPriority(n2k_msg, active_priority_satellites,
595 priority_map_satellites)) {
596 g_SatsInView = temp_data.n_satellites;
597 g_bSatValid = true;
598 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
599 }
600 }
601
602 SendBasicNavdata(valid_flag, m_log_callbacks);
603 return true;
604}
605
606bool CommBridge::HandleN2K_129025(const N2000MsgPtr& n2k_msg) {
607 std::vector<unsigned char> v = n2k_msg->payload;
608
609 NavData temp_data;
610 ClearNavData(temp_data);
611
612 if (!m_decoder.DecodePGN129025(v, temp_data)) return false;
613
614 int valid_flag = 0;
615 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
616 if (EvalPriority(n2k_msg, active_priority_position,
617 priority_map_position)) {
618 gLat = temp_data.gLat;
619 gLon = temp_data.gLon;
620 valid_flag += POS_UPDATE;
621 valid_flag += POS_VALID;
622 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
623 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
624 }
625 }
626 // FIXME (dave) How to notify user of errors?
627 else {
628 }
629
630 SendBasicNavdata(valid_flag, m_log_callbacks);
631 return true;
632}
633
634bool CommBridge::HandleN2K_129026(const N2000MsgPtr& n2k_msg) {
635 std::vector<unsigned char> v = n2k_msg->payload;
636
637 NavData temp_data;
638 ClearNavData(temp_data);
639
640 if (!m_decoder.DecodePGN129026(v, temp_data)) return false;
641
642 int valid_flag = 0;
643 if (!N2kIsNA(temp_data.gSog)) { // gCog as reported by net may be NaN, but OK
644 if (EvalPriority(n2k_msg, active_priority_velocity,
645 priority_map_velocity)) {
646 gSog = MS2KNOTS(temp_data.gSog);
647 valid_flag += SOG_UPDATE;
648
649 if (N2kIsNA(temp_data.gCog))
650 gCog = NAN;
651 else
652 gCog = GeodesicRadToDeg(temp_data.gCog);
653 valid_flag += COG_UPDATE;
654 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
655 }
656 } else {
657 }
658
659 SendBasicNavdata(valid_flag, m_log_callbacks);
660 return true;
661}
662
663bool CommBridge::HandleN2K_127250(const N2000MsgPtr& n2k_msg) {
664 std::vector<unsigned char> v = n2k_msg->payload;
665
666 NavData temp_data;
667 ClearNavData(temp_data);
668
669 if (!m_decoder.DecodePGN127250(v, temp_data)) return false;
670
671 int valid_flag = 0;
672 if (!N2kIsNA(temp_data.gVar)) {
673 if (EvalPriority(n2k_msg, active_priority_variation,
674 priority_map_variation)) {
675 gVar = GeodesicRadToDeg(temp_data.gVar);
676 valid_flag += VAR_UPDATE;
677 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
678 }
679 }
680
681 if (!N2kIsNA(temp_data.gHdt)) {
682 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
683 gHdt = GeodesicRadToDeg(temp_data.gHdt);
684 valid_flag += HDT_UPDATE;
685 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
686 }
687 }
688
689 if (!N2kIsNA(temp_data.gHdm)) {
690 gHdm = GeodesicRadToDeg(temp_data.gHdm);
691 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
692 MakeHDTFromHDM();
693 valid_flag += HDT_UPDATE;
694 if (!std::isnan(gHdt))
695 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
696 }
697 }
698
699 SendBasicNavdata(valid_flag, m_log_callbacks);
700 return true;
701}
702
703bool CommBridge::HandleN2K_127258(const N2000MsgPtr& n2k_msg) {
704 std::vector<unsigned char> v = n2k_msg->payload;
705
706 NavData temp_data;
707 ClearNavData(temp_data);
708
709 if (!m_decoder.DecodePGN127258(v, temp_data)) return false;
710
711 int valid_flag = 0;
712 if (!N2kIsNA(temp_data.gVar)) {
713 if (EvalPriority(n2k_msg, active_priority_variation,
714 priority_map_variation)) {
715 gVar = GeodesicRadToDeg(temp_data.gVar);
716 valid_flag += VAR_UPDATE;
717 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
718 }
719 }
720
721 SendBasicNavdata(valid_flag, m_log_callbacks);
722 return true;
723}
724
725bool CommBridge::HandleN2K_129540(const N2000MsgPtr& n2k_msg) {
726 std::vector<unsigned char> v = n2k_msg->payload;
727
728 NavData temp_data;
729 ClearNavData(temp_data);
730
731 if (!m_decoder.DecodePGN129540(v, temp_data)) return false;
732
733 if (temp_data.n_satellites >= 0) {
734 if (EvalPriority(n2k_msg, active_priority_satellites,
735 priority_map_satellites)) {
736 g_SatsInView = temp_data.n_satellites;
737 g_bSatValid = true;
738 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
739 }
740 }
741
742 return true;
743}
744
745bool CommBridge::HandleN0183_RMC(const N0183MsgPtr& n0183_msg) {
746 string str = n0183_msg->payload;
747
748 NavData temp_data;
749 ClearNavData(temp_data);
750
751 bool is_valid = true;
752 if (!m_decoder.DecodeRMC(str, temp_data)) is_valid = false;
753
754 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
755
756 int valid_flag = 0;
757 if (EvalPriority(n0183_msg, active_priority_position,
758 priority_map_position)) {
759 if (is_valid) {
760 gLat = temp_data.gLat;
761 gLon = temp_data.gLon;
762 valid_flag += POS_VALID;
763 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
764 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
765 }
766 valid_flag += POS_UPDATE;
767 }
768
769 if (EvalPriority(n0183_msg, active_priority_velocity,
770 priority_map_velocity)) {
771 if (is_valid) {
772 gSog = temp_data.gSog;
773 valid_flag += SOG_UPDATE;
774 gCog = temp_data.gCog;
775 valid_flag += COG_UPDATE;
776 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
777 }
778 }
779
780 if (!std::isnan(temp_data.gVar)) {
781 if (EvalPriority(n0183_msg, active_priority_variation,
782 priority_map_variation)) {
783 if (is_valid) {
784 gVar = temp_data.gVar;
785 valid_flag += VAR_UPDATE;
786 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
787 }
788 }
789 }
790
791 // Special case
792 // If no source of satellite count is available (i.e. from GSV or GGA)
793 // Set an inferred satellite count of "3" if RMC position data is valid.
794 // This ensures that the nSats value sent downstream to plugins
795 // will properly reflect a valid position fix, even in the absence of GGA/GSV
796 if ((valid_flag & POS_VALID) == POS_VALID) {
797 if (!g_bSatValid) {
798 if (g_SatsInView < 4) {
799 g_SatsInView = 3;
800 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
801 }
802 }
803 }
804
805 SendBasicNavdata(valid_flag, m_log_callbacks);
806 return true;
807}
808
809bool CommBridge::HandleN0183_THS(const N0183MsgPtr& n0183_msg) {
810 string str = n0183_msg->payload;
811 NavData temp_data;
812 ClearNavData(temp_data);
813
814 if (!m_decoder.DecodeTHS(str, temp_data)) return false;
815
816 int valid_flag = 0;
817 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
818 gHdt = temp_data.gHdt;
819 valid_flag += HDT_UPDATE;
820 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
821 }
822
823 SendBasicNavdata(valid_flag, m_log_callbacks);
824 return true;
825}
826
827bool CommBridge::HandleN0183_HDT(const N0183MsgPtr& n0183_msg) {
828 string str = n0183_msg->payload;
829 NavData temp_data;
830 ClearNavData(temp_data);
831
832 if (!m_decoder.DecodeHDT(str, temp_data)) return false;
833
834 int valid_flag = 0;
835 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
836 gHdt = temp_data.gHdt;
837 valid_flag += HDT_UPDATE;
838 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
839 }
840
841 SendBasicNavdata(valid_flag, m_log_callbacks);
842 return true;
843}
844
845bool CommBridge::HandleN0183_HDG(const N0183MsgPtr& n0183_msg) {
846 string str = n0183_msg->payload;
847 NavData temp_data;
848 ClearNavData(temp_data);
849
850 if (!m_decoder.DecodeHDG(str, temp_data)) return false;
851
852 int valid_flag = 0;
853
854 bool bHDM = false;
855 if (!std::isnan(temp_data.gHdm)) {
856 if (EvalPriority(n0183_msg, active_priority_heading,
857 priority_map_heading)) {
858 gHdm = temp_data.gHdm;
859 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
860 bHDM = true;
861 }
862 }
863
864 if (!std::isnan(temp_data.gVar)) {
865 if (EvalPriority(n0183_msg, active_priority_variation,
866 priority_map_variation)) {
867 gVar = temp_data.gVar;
868 valid_flag += VAR_UPDATE;
869 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
870 }
871 }
872
873 if (bHDM) MakeHDTFromHDM();
874
875 SendBasicNavdata(valid_flag, m_log_callbacks);
876 return true;
877}
878
879bool CommBridge::HandleN0183_HDM(const N0183MsgPtr& n0183_msg) {
880 string str = n0183_msg->payload;
881 NavData temp_data;
882 ClearNavData(temp_data);
883
884 if (!m_decoder.DecodeHDM(str, temp_data)) return false;
885
886 int valid_flag = 0;
887 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
888 gHdm = temp_data.gHdm;
889 MakeHDTFromHDM();
890 valid_flag += HDT_UPDATE;
891 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
892 }
893
894 SendBasicNavdata(valid_flag, m_log_callbacks);
895 return true;
896}
897
898bool CommBridge::HandleN0183_HVD(const N0183MsgPtr& n0183_msg) {
899 string str = n0183_msg->payload;
900 NavData temp_data;
901 ClearNavData(temp_data);
902
903 if (!m_decoder.DecodeHVD(str, temp_data)) return false;
904
905 int valid_flag = 0;
906
907 if (!std::isnan(temp_data.gVar)) {
908 if (EvalPriority(n0183_msg, active_priority_variation,
909 priority_map_variation)) {
910 gVar = temp_data.gVar;
911 valid_flag += VAR_UPDATE;
912 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
913 }
914 }
915
916 SendBasicNavdata(valid_flag, m_log_callbacks);
917 return true;
918}
919
920bool CommBridge::HandleN0183_VTG(const N0183MsgPtr& n0183_msg) {
921 string str = n0183_msg->payload;
922 NavData temp_data;
923 ClearNavData(temp_data);
924
925 if (!m_decoder.DecodeVTG(str, temp_data)) return false;
926
927 int valid_flag = 0;
928 if (EvalPriority(n0183_msg, active_priority_velocity,
929 priority_map_velocity)) {
930 gSog = temp_data.gSog;
931 valid_flag += SOG_UPDATE;
932 gCog = temp_data.gCog;
933 valid_flag += COG_UPDATE;
934 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
935 }
936
937 SendBasicNavdata(valid_flag, m_log_callbacks);
938 return true;
939}
940
941bool CommBridge::HandleN0183_GSV(const N0183MsgPtr& n0183_msg) {
942 string str = n0183_msg->payload;
943 NavData temp_data;
944 ClearNavData(temp_data);
945
946 if (!m_decoder.DecodeGSV(str, temp_data)) return false;
947
948 int valid_flag = 0;
949 if (EvalPriority(n0183_msg, active_priority_satellites,
950 priority_map_satellites)) {
951 if (temp_data.n_satellites >= 0) {
952 g_SatsInView = temp_data.n_satellites;
953 g_bSatValid = true;
954
955 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
956 }
957 }
958
959 SendBasicNavdata(valid_flag, m_log_callbacks);
960 return true;
961}
962
963bool CommBridge::HandleN0183_GGA(const N0183MsgPtr& n0183_msg) {
964 string str = n0183_msg->payload;
965 NavData temp_data;
966 ClearNavData(temp_data);
967
968 bool is_valid = true;
969 if (!m_decoder.DecodeGGA(str, temp_data)) is_valid = false;
970
971 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
972
973 int valid_flag = 0;
974 if (EvalPriority(n0183_msg, active_priority_position,
975 priority_map_position)) {
976 if (is_valid) {
977 gLat = temp_data.gLat;
978 gLon = temp_data.gLon;
979 valid_flag += POS_VALID;
980 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
981 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
982 }
983 valid_flag += POS_UPDATE;
984 }
985
986 if (EvalPriority(n0183_msg, active_priority_satellites,
987 priority_map_satellites)) {
988 if (is_valid) {
989 if (temp_data.n_satellites >= 0) {
990 g_SatsInView = temp_data.n_satellites;
991 g_bSatValid = true;
992
993 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
994 }
995 }
996 }
997
998 SendBasicNavdata(valid_flag, m_log_callbacks);
999 return true;
1000}
1001
1002bool CommBridge::HandleN0183_GLL(const N0183MsgPtr& n0183_msg) {
1003 string str = n0183_msg->payload;
1004 NavData temp_data;
1005 ClearNavData(temp_data);
1006
1007 bool bvalid = true;
1008 if (!m_decoder.DecodeGLL(str, temp_data)) bvalid = false;
1009
1010 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
1011
1012 int valid_flag = 0;
1013 if (EvalPriority(n0183_msg, active_priority_position,
1014 priority_map_position)) {
1015 if (bvalid) {
1016 gLat = temp_data.gLat;
1017 gLon = temp_data.gLon;
1018 valid_flag += POS_VALID;
1019 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
1020 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
1021 }
1022 valid_flag += POS_UPDATE;
1023 }
1024
1025 SendBasicNavdata(valid_flag, m_log_callbacks);
1026 return true;
1027}
1028
1029bool CommBridge::HandleN0183_AIVDO(const N0183MsgPtr& n0183_msg) {
1030 string str = n0183_msg->payload;
1031
1032 GenericPosDatEx gpd;
1033 wxString sentence(str.c_str());
1034
1035 AisError ais_error = AIS_GENERIC_ERROR;
1036 ais_error = DecodeSingleVDO(sentence, &gpd);
1037
1038 if (ais_error == AIS_NoError) {
1039 int valid_flag = 0;
1040 if (!std::isnan(gpd.kLat) && !std::isnan(gpd.kLon)) {
1041 if (EvalPriority(n0183_msg, active_priority_position,
1042 priority_map_position)) {
1043 gLat = gpd.kLat;
1044 gLon = gpd.kLon;
1045 valid_flag += POS_UPDATE;
1046 valid_flag += POS_VALID;
1047 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
1048 m_n_log_watchdog_period =
1049 N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
1050 }
1051 }
1052
1053 if (!std::isnan(gpd.kCog) && !std::isnan(gpd.kSog)) {
1054 if (EvalPriority(n0183_msg, active_priority_velocity,
1055 priority_map_velocity)) {
1056 gSog = gpd.kSog;
1057 valid_flag += SOG_UPDATE;
1058 gCog = gpd.kCog;
1059 valid_flag += COG_UPDATE;
1060 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
1061 }
1062 }
1063
1064 if (!std::isnan(gpd.kHdt)) {
1065 if (EvalPriority(n0183_msg, active_priority_heading,
1066 priority_map_heading)) {
1067 gHdt = gpd.kHdt;
1068 valid_flag += HDT_UPDATE;
1069 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1070 }
1071 }
1072
1073 SendBasicNavdata(valid_flag, m_log_callbacks);
1074 }
1075 return true;
1076}
1077
1078bool CommBridge::HandleSignalK(const SignalKMsgPtr& sK_msg) {
1079 string str = sK_msg->raw_message;
1080
1081 // Ignore messages involving contexts other than ownship, but do log them
1082 if (sK_msg->context_self != sK_msg->context) {
1083 g_pMUX->LogInputMessage(sK_msg, true, false);
1084 return false;
1085 }
1086
1087 g_ownshipMMSI_SK = sK_msg->context_self;
1088
1089 NavData temp_data;
1090 ClearNavData(temp_data);
1091
1092 if (!m_decoder.DecodeSignalK(str, temp_data)) return false;
1093
1094 int valid_flag = 0;
1095
1096 if (!std::isnan(temp_data.gLat) && !std::isnan(temp_data.gLon)) {
1097 if (EvalPriority(sK_msg, active_priority_position, priority_map_position)) {
1098 gLat = temp_data.gLat;
1099 gLon = temp_data.gLon;
1100 valid_flag += POS_UPDATE;
1101 valid_flag += POS_VALID;
1102 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
1103 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
1104 }
1105 }
1106
1107 if (!std::isnan(temp_data.gSog)) {
1108 if (EvalPriority(sK_msg, active_priority_velocity, priority_map_velocity)) {
1109 gSog = temp_data.gSog;
1110 valid_flag += SOG_UPDATE;
1111 if ((gSog > 0.05) && !std::isnan(temp_data.gCog)) {
1112 gCog = temp_data.gCog;
1113 valid_flag += COG_UPDATE;
1114 }
1115 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
1116 }
1117 }
1118
1119 if (!std::isnan(temp_data.gHdt)) {
1120 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
1121 gHdt = temp_data.gHdt;
1122 valid_flag += HDT_UPDATE;
1123 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1124 }
1125 }
1126
1127 if (!std::isnan(temp_data.gHdm)) {
1128 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
1129 gHdm = temp_data.gHdm;
1130 MakeHDTFromHDM();
1131 valid_flag += HDT_UPDATE;
1132 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1133 }
1134 }
1135
1136 if (!std::isnan(temp_data.gVar)) {
1137 if (EvalPriority(sK_msg, active_priority_variation,
1138 priority_map_variation)) {
1139 gVar = temp_data.gVar;
1140 valid_flag += VAR_UPDATE;
1141 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
1142 }
1143 }
1144
1145 if (temp_data.n_satellites > 0) {
1146 if (EvalPriority(sK_msg, active_priority_satellites,
1147 priority_map_satellites)) {
1148 g_SatsInView = temp_data.n_satellites;
1149 g_bSatValid = true;
1150 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
1151 }
1152 }
1153
1154 if (g_pMUX && g_pMUX->IsLogActive())
1155 g_pMUX->LogInputMessage(sK_msg, false, false);
1156
1157 SendBasicNavdata(valid_flag, m_log_callbacks);
1158 return true;
1159}
1160
1161void CommBridge::ClearPriorityMaps() {
1162 priority_map_position.clear();
1163 priority_map_velocity.clear();
1164 priority_map_heading.clear();
1165 priority_map_variation.clear();
1166 priority_map_satellites.clear();
1167}
1168
1169PriorityContainer& CommBridge::GetPriorityContainer(const string& category) {
1170 if (category == "position")
1171 return active_priority_position;
1172 else if (category == "velocity")
1173 return active_priority_velocity;
1174 else if (category == "heading")
1175 return active_priority_heading;
1176 else if (category == "variation")
1177 return active_priority_variation;
1178 else if (category == "satellites")
1179 return active_priority_satellites;
1180 else
1181 return active_priority_void;
1182}
1183
1184void CommBridge::UpdateAndApplyMaps(const std::vector<string>& new_maps) {
1185 ApplyPriorityMaps(new_maps);
1186 SaveConfig();
1187 PresetPriorityContainers();
1188}
1189
1190bool CommBridge::LoadConfig() {
1191 if (TheBaseConfig()) {
1192 TheBaseConfig()->SetPath("/Settings/CommPriority");
1193
1194 std::vector<string> new_maps;
1195 wxString pri_string;
1196
1197 TheBaseConfig()->Read("PriorityPosition", &pri_string);
1198 string s_prio = string(pri_string.c_str());
1199 new_maps.push_back(s_prio);
1200
1201 TheBaseConfig()->Read("PriorityVelocity", &pri_string);
1202 s_prio = string(pri_string.c_str());
1203 new_maps.push_back(s_prio);
1204
1205 TheBaseConfig()->Read("PriorityHeading", &pri_string);
1206 s_prio = string(pri_string.c_str());
1207 new_maps.push_back(s_prio);
1208
1209 TheBaseConfig()->Read("PriorityVariation", &pri_string);
1210 s_prio = string(pri_string.c_str());
1211 new_maps.push_back(s_prio);
1212
1213 TheBaseConfig()->Read("PrioritySatellites", &pri_string);
1214 s_prio = string(pri_string.c_str());
1215 new_maps.push_back(s_prio);
1216
1217 ApplyPriorityMaps(new_maps);
1218 }
1219 return true;
1220}
1221
1222bool CommBridge::SaveConfig() const {
1223 if (TheBaseConfig()) {
1224 TheBaseConfig()->SetPath("/Settings/CommPriority");
1225
1226 wxString pri_string =
1227 wxString(GetPriorityMap(priority_map_position).c_str());
1228 TheBaseConfig()->Write("PriorityPosition", pri_string);
1229
1230 pri_string = wxString(GetPriorityMap(priority_map_velocity).c_str());
1231 TheBaseConfig()->Write("PriorityVelocity", pri_string);
1232
1233 pri_string = wxString(GetPriorityMap(priority_map_heading).c_str());
1234 TheBaseConfig()->Write("PriorityHeading", pri_string);
1235
1236 pri_string = wxString(GetPriorityMap(priority_map_variation).c_str());
1237 TheBaseConfig()->Write("PriorityVariation", pri_string);
1238
1239 pri_string = wxString(GetPriorityMap(priority_map_satellites).c_str());
1240 TheBaseConfig()->Write("PrioritySatellites", pri_string);
1241 }
1242 return true;
1243}
1244
1245bool CommBridge::EvalPriority(const NavMsgPtr& msg,
1246 PriorityContainer& active_priority,
1247 PriorityMap& priority_map) {
1248 string this_key = GetPriorityKey(msg);
1249 if (debug_priority) printf("This Key: %s\n", this_key.c_str());
1250
1251 // Pull some identifiers from the unique key
1252 wxStringTokenizer tkz(this_key, ";");
1253 wxString wxs_this_source = tkz.GetNextToken();
1254 string source = wxs_this_source.ToStdString();
1255 wxString wxs_this_identifier = tkz.GetNextToken();
1256 string this_identifier = wxs_this_identifier.ToStdString();
1257
1258 wxStringTokenizer tka(wxs_this_source, ":");
1259 tka.GetNextToken();
1260 std::stringstream ss;
1261 ss << tka.GetNextToken();
1262 int source_address;
1263 ss >> source_address;
1264
1265 // Special case priority value linkage for N0183 messages:
1266 // If this is a "velocity" record, ensure that a "position"
1267 // report has been accepted from the same source before accepting the
1268 // velocity record.
1269 // This ensures that the data source is fully initialized, and is reporting
1270 // valid, sensible velocity data.
1271 if (msg->bus == NavAddr::Bus::N0183) {
1272 if (!strncmp(active_priority.prio_class.c_str(), "velocity", 8)) {
1273 bool pos_ok = false;
1274 if (!strcmp(active_priority_position.active_source.c_str(),
1275 source.c_str())) {
1276 if (active_priority_position.recent_active_time != -1) {
1277 pos_ok = true;
1278 }
1279 }
1280 if (!pos_ok) return false;
1281 }
1282 }
1283
1284 // Fetch the established priority for the message
1285 int this_priority;
1286
1287 auto it = priority_map.find(this_key);
1288 if (it == priority_map.end()) {
1289 // Not found, so make it default the lowest priority
1290 size_t n = priority_map.size();
1291 priority_map[this_key] = static_cast<int>(n);
1292 }
1293
1294 this_priority = priority_map[this_key];
1295
1296 if (debug_priority) {
1297 for (const auto& jt : priority_map) {
1298 printf(" priority_map: %s %d\n", jt.first.c_str(),
1299 jt.second);
1300 }
1301 }
1302
1303 // Incoming message priority lower than currently active priority?
1304 // If so, drop the message
1305 if (this_priority > active_priority.active_priority) {
1306 if (debug_priority)
1307 printf(" Drop low priority: %s %d %d \n", source.c_str(),
1308 this_priority, active_priority.active_priority);
1309 return false;
1310 }
1311
1312 // A channel returning, after watchdog time out.
1313 if (this_priority < active_priority.active_priority) {
1314 active_priority.active_priority = this_priority;
1315 active_priority.active_source = source;
1316 active_priority.active_identifier = this_identifier;
1317 active_priority.active_source_address = source_address;
1318 wxDateTime now = wxDateTime::Now();
1319 active_priority.recent_active_time = now.GetTicks();
1320
1321 if (debug_priority)
1322 printf(" Restoring high priority: %s %d\n", source.c_str(),
1323 this_priority);
1324 return true;
1325 }
1326
1327 // Do we see two sources with the same priority?
1328 // If so, we take the first one, and deprioritize this one.
1329
1330 if (!active_priority.active_source.empty()) {
1331 if (debug_priority) printf("source: %s\n", source.c_str());
1332 if (debug_priority)
1333 printf("active_source: %s\n", active_priority.active_source.c_str());
1334
1335 if (source != active_priority.active_source) {
1336 // Auto adjust the priority of these this message down
1337 // First, find the lowest priority in use in this map
1338 int lowest_priority = -10; // safe enough
1339 for (const auto& jt : priority_map) {
1340 if (jt.second > lowest_priority) lowest_priority = jt.second;
1341 }
1342
1343 priority_map[this_key] = lowest_priority + 1;
1344 if (debug_priority)
1345 printf(" Lowering priority A: %s :%d\n", source.c_str(),
1346 priority_map[this_key]);
1347 return false;
1348 }
1349 }
1350
1351 // For N0183 message, has the Mnemonic (id) changed?
1352 // Example: RMC and AIVDO from same source.
1353
1354 if (msg->bus == NavAddr::Bus::N0183) {
1355 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
1356 if (msg_0183) {
1357 if (!active_priority.active_identifier.empty()) {
1358 if (debug_priority)
1359 printf("this_identifier: %s\n", this_identifier.c_str());
1360 if (debug_priority)
1361 printf("active_priority.active_identifier: %s\n",
1362 active_priority.active_identifier.c_str());
1363
1364 if (this_identifier != active_priority.active_identifier) {
1365 // if necessary, auto adjust the priority of this message down
1366 // and drop it
1367 if (priority_map[this_key] == active_priority.active_priority) {
1368 int lowest_priority = -10; // safe enough
1369 for (const auto& jt : priority_map) {
1370 if (jt.second > lowest_priority) lowest_priority = jt.second;
1371 }
1372
1373 priority_map[this_key] = lowest_priority + 1;
1374 if (debug_priority)
1375 printf(" Lowering priority B: %s :%d\n", source.c_str(),
1376 priority_map[this_key]);
1377 }
1378
1379 return false;
1380 }
1381 }
1382 }
1383 }
1384
1385 // Similar for n2k PGN...
1386
1387 else if (msg->bus == NavAddr::Bus::N2000) {
1388 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
1389 if (msg_n2k) {
1390 if (!active_priority.active_identifier.empty()) {
1391 if (this_identifier != active_priority.active_identifier) {
1392 // if necessary, auto adjust the priority of this message down
1393 // and drop it
1394 if (priority_map[this_key] == active_priority.active_priority) {
1395 int lowest_priority = -10; // safe enough
1396 for (const auto& jt : priority_map) {
1397 if (jt.second > lowest_priority) lowest_priority = jt.second;
1398 }
1399
1400 priority_map[this_key] = lowest_priority + 1;
1401 if (debug_priority)
1402 printf(" Lowering priority: %s :%d\n", source.c_str(),
1403 priority_map[this_key]);
1404 }
1405
1406 return false;
1407 }
1408 }
1409 }
1410 }
1411
1412 // Update the records
1413 active_priority.active_source = source;
1414 active_priority.active_identifier = this_identifier;
1415 active_priority.active_source_address = source_address;
1416 wxDateTime now = wxDateTime::Now();
1417 active_priority.recent_active_time = now.GetTicks();
1418 if (debug_priority)
1419 printf(" Accepting high priority: %s %d\n", source.c_str(), this_priority);
1420
1421 if (active_priority.prio_class == "position") {
1422 if (this_priority != m_last_position_priority) {
1423 m_last_position_priority = this_priority;
1424
1425 wxString msg_;
1426 msg_.Printf(_("GNSS position fix priority shift:") + " %s\n %s \n -> %s",
1427 this_identifier.c_str(), m_last_position_source.c_str(),
1428 source.c_str());
1429 auto& noteman = NotificationManager::GetInstance();
1430 noteman.AddNotification(NotificationSeverity::kInformational,
1431 msg_.ToStdString());
1432 }
1433 m_last_position_source = source;
1434 }
1435
1436 return true;
1437}
void Notify(const std::shared_ptr< const AppMsg > &msg)
Send message to everyone listening to given message type.
string key() const override
Return unique key used by observable to notify/listen.
string to_string() const override
Return printable string for logging etc without trailing nl.
Process incoming messages.
Definition comm_bridge.h:89
bool HandleN0183_AIVDO(const N0183MsgPtr &n0183_msg)
Processes NMEA 0183 AIVDO sentences containing own vessel's AIS data.
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString &error_msg="") const
Logs an input message with context information.
Actual data sent between application and transport layer.
Representation of message status as determined by the multiplexer.
A regular Nmea0183 message.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
NMEA Log Interface.
Definition nmea_log.h:70
virtual bool IsVisible() const =0
Return true if log is visible i.e., if it's any point using Add().
virtual void Add(const Logline &l)=0
Add a formatted string to log output.
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
Definition observable.h:295
Custom event class for OpenCPN's notification system.
A parsed SignalK message over ipv4.
AIS decoding functions.
Decoded messages send/receive support.
CommBridge class and helpers.
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
bool g_bSatValid
Indicates valid GNSS reception status based on satellite visibility and successful parsing of NMEA018...
Definition comm_vars.cpp:32
Variables maintained by comm stack, read-only access for others.
Global variables stored in configuration file.
Extern C linked utilities.
Hooks into gui available in model.
GUI constant definitions.
Multiplexer class and helpers.
User notifications manager.
Navigation data types.
double gHdm
Magnetic heading in degrees (0-359.99).
Definition own_ship.cpp:31
double gVar
Magnetic variation in degrees.
Definition own_ship.cpp:32
double gHdt
True heading in degrees (0-359.99).
Definition own_ship.cpp:30
double gLat
Vessel's current latitude in decimal degrees.
Definition own_ship.cpp:26
double gCog
Course over ground in degrees (0-359.99).
Definition own_ship.cpp:28
double gSog
Speed over ground in knots.
Definition own_ship.cpp:29
double gLon
Vessel's current longitude in decimal degrees.
Definition own_ship.cpp:27
Position, course, speed, etc.
A generic position and navigation data structure.
Definition ocpn_types.h:68
double kCog
Course over ground in degrees.
Definition ocpn_types.h:86
double kHdt
True heading in degrees.
Definition ocpn_types.h:111
double kLat
Latitude in decimal degrees.
Definition ocpn_types.h:75
double kSog
Speed over ground in knots.
Definition ocpn_types.h:92
double kLon
Longitude in decimal degrees.
Definition ocpn_types.h:83
Item in the log window.
Definition nmea_log.h:32