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 == "HDG")
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()
254 : wxEvtHandler(),
255 // every 60 minutes, reduced after first position Rx
256 active_priority_position("position"),
257 active_priority_velocity("velocity"),
258 active_priority_heading("heading"),
259 active_priority_variation("variation"),
260 active_priority_satellites("satellites"),
261 active_priority_void("", -1),
262 m_n_log_watchdog_period(3600),
263 m_last_position_priority(0) {
264 Bind(wxEVT_TIMER, [&](wxTimerEvent&) { OnWatchdogTimer(); });
265}
266CommBridge::~CommBridge() = default;
267
268bool CommBridge::Initialize() {
269 m_log_callbacks = GetLogCallbacks();
270 ClearPriorityMaps();
271
272 LoadConfig();
273 PresetPriorityContainers();
274
275 // Clear the watchdogs
276 PresetWatchdogs();
277
278 m_watchdog_timer.SetOwner(this, WATCHDOG_TIMER);
279 m_watchdog_timer.Start(1000, wxTIMER_CONTINUOUS);
280
281 InitCommListeners();
282
283 // Initialize a listener for driver state changes
284 m_driver_change_lstnr.Init(
285 CommDriverRegistry::GetInstance().evt_driverlist_change,
286 [&](const wxCommandEvent& ev) { OnDriverStateChange(); });
287
288 return true;
289}
290
291void CommBridge::PresetWatchdogs() {
292 m_watchdogs.position_watchdog =
293 20; // A bit longer watchdog for startup latency.
294 m_watchdogs.velocity_watchdog = 20;
295 m_watchdogs.variation_watchdog = 20;
296 m_watchdogs.heading_watchdog = 20;
297 m_watchdogs.satellite_watchdog = 20;
298}
299
300void CommBridge::OnWatchdogTimer() {
301 // Update and check watchdog timer for GPS data source
302 m_watchdogs.position_watchdog--;
303 if (m_watchdogs.position_watchdog <= 0) {
304 if (m_watchdogs.position_watchdog % 5 == 0) {
305 // Send AppMsg telling of watchdog expiry
306 auto msg = std::make_shared<GPSWatchdogMsg>(
307 GPSWatchdogMsg::WDSource::position, m_watchdogs.position_watchdog);
308 auto& msgbus = AppMsgBus::GetInstance();
309 LogAppMsg(msg, "watchdog", m_log_callbacks);
310 msgbus.Notify(std::move(msg));
311
312 if (m_watchdogs.position_watchdog % m_n_log_watchdog_period == 0) {
313 wxString logmsg;
314 logmsg.Printf(" ***GPS Watchdog timeout at Lat:%g Lon: %g", gLat,
315 gLon);
316 wxLogMessage(logmsg);
317 }
318 }
319
320 gSog = NAN;
321 gCog = NAN;
322 gRmcDate.Empty();
323 gRmcTime.Empty();
324 active_priority_position.recent_active_time = -1;
325
326 // Are there any other lower priority sources?
327 // If so, adopt that one.
328 if (IsNextLowerPriorityAvailable(priority_map_position,
329 active_priority_position)) {
330 SelectNextLowerPriority(priority_map_position, active_priority_position);
331 }
332 }
333
334 // Update and check watchdog timer for SOG/COG data source
335 m_watchdogs.velocity_watchdog--;
336 if (m_watchdogs.velocity_watchdog <= 0) {
337 gSog = NAN;
338 gCog = NAN;
339 active_priority_velocity.recent_active_time = -1;
340
341 if (g_nNMEADebug && (m_watchdogs.velocity_watchdog == 0))
342 wxLogMessage(" ***Velocity Watchdog timeout...");
343 if (m_watchdogs.velocity_watchdog % 5 == 0) {
344 // Send AppMsg telling of watchdog expiry
345 auto msg = std::make_shared<GPSWatchdogMsg>(
346 GPSWatchdogMsg::WDSource::velocity, m_watchdogs.velocity_watchdog);
347 auto& msgbus = AppMsgBus::GetInstance();
348 msgbus.Notify(std::move(msg));
349 }
350 // Are there any other lower priority sources?
351 // If so, adopt that one.
352 SelectNextLowerPriority(priority_map_velocity, active_priority_velocity);
353 }
354
355 // Update and check watchdog timer for True Heading data source
356 m_watchdogs.heading_watchdog--;
357 if (m_watchdogs.heading_watchdog <= 0) {
358 gHdt = NAN;
359 active_priority_heading.recent_active_time = -1;
360 if (g_nNMEADebug && (m_watchdogs.heading_watchdog == 0))
361 wxLogMessage(" ***HDT Watchdog timeout...");
362
363 // Are there any other lower priority sources?
364 // If so, adopt that one.
365 SelectNextLowerPriority(priority_map_heading, active_priority_heading);
366 }
367
368 // Update and check watchdog timer for Magnetic Variation data source
369 m_watchdogs.variation_watchdog--;
370 if (m_watchdogs.variation_watchdog <= 0) {
371 g_bVAR_Rx = false;
372 active_priority_variation.recent_active_time = -1;
373
374 if (g_nNMEADebug && (m_watchdogs.variation_watchdog == 0))
375 wxLogMessage(" ***VAR Watchdog timeout...");
376
377 // Are there any other lower priority sources?
378 // If so, adopt that one.
379 SelectNextLowerPriority(priority_map_variation, active_priority_variation);
380 }
381
382 // Update and check watchdog timer for GSV, GGA and SignalK (Satellite data)
383 m_watchdogs.satellite_watchdog--;
384 if (m_watchdogs.satellite_watchdog <= 0) {
385 g_bSatValid = false;
386 g_SatsInView = 0;
387 g_priSats = 99;
388 active_priority_satellites.recent_active_time = -1;
389
390 if (g_nNMEADebug && (m_watchdogs.satellite_watchdog == 0))
391 wxLogMessage(" ***SAT Watchdog timeout...");
392
393 // Are there any other lower priority sources?
394 // If so, adopt that one.
395 SelectNextLowerPriority(priority_map_satellites,
396 active_priority_satellites);
397 }
398}
399
400void CommBridge::MakeHDTFromHDM() {
401 // Here is the one place we try to create gHdt from gHdm and gVar,
402
403 if (!std::isnan(gHdm)) {
404 // Set gVar if needed from manual entry. gVar will be overwritten if
405 // WMM plugin is available
406 if (std::isnan(gVar) && (g_UserVar != 0.0)) gVar = g_UserVar;
407 gHdt = gHdm + gVar;
408 if (!std::isnan(gHdt)) {
409 if (gHdt < 0)
410 gHdt += 360.0;
411 else if (gHdt >= 360)
412 gHdt -= 360.0;
413
414 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
415 }
416 }
417}
418
419void CommBridge::InitCommListeners() {
420 // Initialize the comm listeners
421
422 // GNSS Position Data PGN 129029
423 m_n2k_129029_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129029)),
424 [&](const ObservedEvt& ev) {
425 HandleN2K_129029(UnpackEvtPointer<Nmea2000Msg>(ev));
426 });
427
428 // Position rapid PGN 129025
429 m_n2k_129025_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129025)),
430 [&](const ObservedEvt& ev) {
431 HandleN2K_129025(UnpackEvtPointer<Nmea2000Msg>(ev));
432 });
433
434 // COG SOG rapid PGN 129026
435 m_n2k_129026_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129026)),
436 [&](const ObservedEvt& ev) {
437 HandleN2K_129026(UnpackEvtPointer<Nmea2000Msg>(ev));
438 });
439
440 // Heading rapid PGN 127250
441 m_n2k_127250_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(127250)),
442 [&](const ObservedEvt& ev) {
443 HandleN2K_127250(UnpackEvtPointer<Nmea2000Msg>(ev));
444 });
445
446 // GNSS Satellites in View PGN 129540
447 m_n2k_129540_lstnr.Init(Nmea2000Msg(static_cast<uint64_t>(129540)),
448 [&](const ObservedEvt& ev) {
449 HandleN2K_129540(UnpackEvtPointer<Nmea2000Msg>(ev));
450 });
451
452 // NMEA0183
453 // RMC
454 Nmea0183Msg n0183_msg_RMC("RMC");
455 m_n0183_rmc_lstnr.Init(Nmea0183Msg("RMC"), [&](const ObservedEvt& ev) {
456 HandleN0183_RMC(UnpackEvtPointer<Nmea0183Msg>(ev));
457 });
458
459 // THS
460 m_n0183_ths_lstnr.Init(Nmea0183Msg("THS"), [&](const ObservedEvt& ev) {
461 HandleN0183_THS(UnpackEvtPointer<Nmea0183Msg>(ev));
462 });
463
464 // HDT
465 m_n0183_hdt_lstnr.Init(Nmea0183Msg("HDT"), [&](const ObservedEvt& ev) {
466 HandleN0183_HDT(UnpackEvtPointer<Nmea0183Msg>(ev));
467 });
468
469 // HDG
470 m_n0183_hdg_lstnr.Init(Nmea0183Msg("HDG"), [&](const ObservedEvt& ev) {
471 HandleN0183_HDG(UnpackEvtPointer<Nmea0183Msg>(ev));
472 });
473
474 // HDM
475 m_n0183_hdm_lstnr.Init(Nmea0183Msg("HDM"), [&](const ObservedEvt& ev) {
476 HandleN0183_HDM(UnpackEvtPointer<Nmea0183Msg>(ev));
477 });
478
479 // VTG
480 m_n0183_vtg_lstnr.Init(Nmea0183Msg("VTG"), [&](const ObservedEvt& ev) {
481 HandleN0183_VTG(UnpackEvtPointer<Nmea0183Msg>(ev));
482 });
483
484 // GSV
485 m_n0183_gsv_lstnr.Init(Nmea0183Msg("GSV"), [&](const ObservedEvt& ev) {
486 HandleN0183_GSV(UnpackEvtPointer<Nmea0183Msg>(ev));
487 });
488
489 // GGA
490 m_n0183_gga_lstnr.Init(Nmea0183Msg("GGA"), [&](const ObservedEvt& ev) {
491 HandleN0183_GGA(UnpackEvtPointer<Nmea0183Msg>(ev));
492 });
493
494 // GLL
495 Nmea0183Msg n0183_msg_GLL("GLL");
496 m_n0183_gll_lstnr.Init(Nmea0183Msg("GLL"), [&](const ObservedEvt& ev) {
497 HandleN0183_GLL(UnpackEvtPointer<Nmea0183Msg>(ev));
498 });
499
500 // AIVDO
501 m_n0183_aivdo_lstnr.Init(Nmea0183Msg("AIVDO"), [&](const ObservedEvt& ev) {
502 HandleN0183_AIVDO(UnpackEvtPointer<Nmea0183Msg>(ev));
503 });
504
505 // SignalK
506 m_signal_k_lstnr.Init(SignalkMsg(), [&](const ObservedEvt& ev) {
507 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
508 });
509}
510
511void CommBridge::OnDriverStateChange() {
512 // Reset all active priority states
513 PresetPriorityContainers();
514}
515
516std::vector<string> CommBridge::GetPriorityMaps() const {
517 std::vector<string> result;
518 result.push_back(GetPriorityMap(priority_map_position));
519 result.push_back(GetPriorityMap(priority_map_velocity));
520 result.push_back(GetPriorityMap(priority_map_heading));
521 result.push_back(GetPriorityMap(priority_map_variation));
522 result.push_back(GetPriorityMap(priority_map_satellites));
523 return result;
524}
525
526void CommBridge::ApplyPriorityMaps(const std::vector<string>& new_maps) {
527 wxString new_prio_string = wxString(new_maps[0].c_str());
528 ApplyPriorityMap(priority_map_position, new_prio_string, 0);
529
530 new_prio_string = wxString(new_maps[1].c_str());
531 ApplyPriorityMap(priority_map_velocity, new_prio_string, 1);
532
533 new_prio_string = wxString(new_maps[2].c_str());
534 ApplyPriorityMap(priority_map_heading, new_prio_string, 2);
535
536 new_prio_string = wxString(new_maps[3].c_str());
537 ApplyPriorityMap(priority_map_variation, new_prio_string, 3);
538
539 new_prio_string = wxString(new_maps[4].c_str());
540 ApplyPriorityMap(priority_map_satellites, new_prio_string, 4);
541}
542
543void CommBridge::PresetPriorityContainers() {
544 PresetPriorityContainer(active_priority_position, priority_map_position);
545 PresetPriorityContainer(active_priority_velocity, priority_map_velocity);
546 PresetPriorityContainer(active_priority_heading, priority_map_heading);
547 PresetPriorityContainer(active_priority_variation, priority_map_variation);
548 PresetPriorityContainer(active_priority_satellites, priority_map_satellites);
549}
550
551bool CommBridge::HandleN2K_129029(const N2000MsgPtr& n2k_msg) {
552 std::vector<unsigned char> v = n2k_msg->payload;
553
554 // extract and verify PGN
555 NavData temp_data;
556 ClearNavData(temp_data);
557
558 if (!m_decoder.DecodePGN129029(v, temp_data)) return false;
559
560 int valid_flag = 0;
561 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
562 if (EvalPriority(n2k_msg, active_priority_position,
563 priority_map_position)) {
564 gLat = temp_data.gLat;
565 gLon = temp_data.gLon;
566 valid_flag += POS_UPDATE;
567 valid_flag += POS_VALID;
568 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
569 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
570 }
571 }
572
573 if (temp_data.n_satellites >= 0) {
574 if (EvalPriority(n2k_msg, active_priority_satellites,
575 priority_map_satellites)) {
576 g_SatsInView = temp_data.n_satellites;
577 g_bSatValid = true;
578 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
579 }
580 }
581
582 SendBasicNavdata(valid_flag, m_log_callbacks);
583 return true;
584}
585
586bool CommBridge::HandleN2K_129025(const N2000MsgPtr& n2k_msg) {
587 std::vector<unsigned char> v = n2k_msg->payload;
588
589 NavData temp_data;
590 ClearNavData(temp_data);
591
592 if (!m_decoder.DecodePGN129025(v, temp_data)) return false;
593
594 int valid_flag = 0;
595 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
596 if (EvalPriority(n2k_msg, active_priority_position,
597 priority_map_position)) {
598 gLat = temp_data.gLat;
599 gLon = temp_data.gLon;
600 valid_flag += POS_UPDATE;
601 valid_flag += POS_VALID;
602 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
603 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
604 }
605 }
606 // FIXME (dave) How to notify user of errors?
607 else {
608 }
609
610 SendBasicNavdata(valid_flag, m_log_callbacks);
611 return true;
612}
613
614bool CommBridge::HandleN2K_129026(const N2000MsgPtr& n2k_msg) {
615 std::vector<unsigned char> v = n2k_msg->payload;
616
617 NavData temp_data;
618 ClearNavData(temp_data);
619
620 if (!m_decoder.DecodePGN129026(v, temp_data)) return false;
621
622 int valid_flag = 0;
623 if (!N2kIsNA(temp_data.gSog)) { // gCog as reported by net may be NaN, but OK
624 if (EvalPriority(n2k_msg, active_priority_velocity,
625 priority_map_velocity)) {
626 gSog = MS2KNOTS(temp_data.gSog);
627 valid_flag += SOG_UPDATE;
628
629 if (N2kIsNA(temp_data.gCog))
630 gCog = NAN;
631 else
632 gCog = GeodesicRadToDeg(temp_data.gCog);
633 valid_flag += COG_UPDATE;
634 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
635 }
636 } else {
637 }
638
639 SendBasicNavdata(valid_flag, m_log_callbacks);
640 return true;
641}
642
643bool CommBridge::HandleN2K_127250(const N2000MsgPtr& n2k_msg) {
644 std::vector<unsigned char> v = n2k_msg->payload;
645
646 NavData temp_data;
647 ClearNavData(temp_data);
648
649 if (!m_decoder.DecodePGN127250(v, temp_data)) return false;
650
651 int valid_flag = 0;
652 if (!N2kIsNA(temp_data.gVar)) {
653 if (EvalPriority(n2k_msg, active_priority_variation,
654 priority_map_variation)) {
655 gVar = GeodesicRadToDeg(temp_data.gVar);
656 valid_flag += VAR_UPDATE;
657 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
658 }
659 }
660
661 if (!N2kIsNA(temp_data.gHdt)) {
662 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
663 gHdt = GeodesicRadToDeg(temp_data.gHdt);
664 valid_flag += HDT_UPDATE;
665 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
666 }
667 }
668
669 if (!N2kIsNA(temp_data.gHdm)) {
670 gHdm = GeodesicRadToDeg(temp_data.gHdm);
671 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
672 MakeHDTFromHDM();
673 valid_flag += HDT_UPDATE;
674 if (!std::isnan(gHdt))
675 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
676 }
677 }
678
679 SendBasicNavdata(valid_flag, m_log_callbacks);
680 return true;
681}
682
683bool CommBridge::HandleN2K_129540(const N2000MsgPtr& n2k_msg) {
684 std::vector<unsigned char> v = n2k_msg->payload;
685
686 NavData temp_data;
687 ClearNavData(temp_data);
688
689 if (!m_decoder.DecodePGN129540(v, temp_data)) return false;
690
691 if (temp_data.n_satellites >= 0) {
692 if (EvalPriority(n2k_msg, active_priority_satellites,
693 priority_map_satellites)) {
694 g_SatsInView = temp_data.n_satellites;
695 g_bSatValid = true;
696 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
697 }
698 }
699
700 return true;
701}
702
703bool CommBridge::HandleN0183_RMC(const N0183MsgPtr& n0183_msg) {
704 string str = n0183_msg->payload;
705
706 NavData temp_data;
707 ClearNavData(temp_data);
708
709 bool is_valid = true;
710 if (!m_decoder.DecodeRMC(str, temp_data)) is_valid = false;
711
712 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
713
714 int valid_flag = 0;
715 if (EvalPriority(n0183_msg, active_priority_position,
716 priority_map_position)) {
717 if (is_valid) {
718 gLat = temp_data.gLat;
719 gLon = temp_data.gLon;
720 valid_flag += POS_VALID;
721 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
722 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
723 }
724 valid_flag += POS_UPDATE;
725 }
726
727 if (EvalPriority(n0183_msg, active_priority_velocity,
728 priority_map_velocity)) {
729 if (is_valid) {
730 gSog = temp_data.gSog;
731 valid_flag += SOG_UPDATE;
732 gCog = temp_data.gCog;
733 valid_flag += COG_UPDATE;
734 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
735 }
736 }
737
738 if (!std::isnan(temp_data.gVar)) {
739 if (EvalPriority(n0183_msg, active_priority_variation,
740 priority_map_variation)) {
741 if (is_valid) {
742 gVar = temp_data.gVar;
743 valid_flag += VAR_UPDATE;
744 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
745 }
746 }
747 }
748
749 // Special case
750 // If no source of satellite count is available (i.e. from GSV or GGA)
751 // Set an inferred satellite count of "3" if RMC position data is valid.
752 // This ensures that the nSats value sent downstream to plugins
753 // will properly reflect a valid position fix, even in the absence of GGA/GSV
754 if ((valid_flag & POS_VALID) == POS_VALID) {
755 if (!g_bSatValid) {
756 if (g_SatsInView < 4) {
757 g_SatsInView = 3;
758 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
759 }
760 }
761 }
762
763 SendBasicNavdata(valid_flag, m_log_callbacks);
764 return true;
765}
766
767bool CommBridge::HandleN0183_THS(const N0183MsgPtr& n0183_msg) {
768 string str = n0183_msg->payload;
769 NavData temp_data;
770 ClearNavData(temp_data);
771
772 if (!m_decoder.DecodeTHS(str, temp_data)) return false;
773
774 int valid_flag = 0;
775 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
776 gHdt = temp_data.gHdt;
777 valid_flag += HDT_UPDATE;
778 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
779 }
780
781 SendBasicNavdata(valid_flag, m_log_callbacks);
782 return true;
783}
784
785bool CommBridge::HandleN0183_HDT(const N0183MsgPtr& n0183_msg) {
786 string str = n0183_msg->payload;
787 NavData temp_data;
788 ClearNavData(temp_data);
789
790 if (!m_decoder.DecodeHDT(str, temp_data)) return false;
791
792 int valid_flag = 0;
793 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
794 gHdt = temp_data.gHdt;
795 valid_flag += HDT_UPDATE;
796 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
797 }
798
799 SendBasicNavdata(valid_flag, m_log_callbacks);
800 return true;
801}
802
803bool CommBridge::HandleN0183_HDG(const N0183MsgPtr& n0183_msg) {
804 string str = n0183_msg->payload;
805 NavData temp_data;
806 ClearNavData(temp_data);
807
808 if (!m_decoder.DecodeHDG(str, temp_data)) return false;
809
810 int valid_flag = 0;
811
812 bool bHDM = false;
813 if (!std::isnan(temp_data.gHdm)) {
814 if (EvalPriority(n0183_msg, active_priority_heading,
815 priority_map_heading)) {
816 gHdm = temp_data.gHdm;
817 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
818 bHDM = true;
819 }
820 }
821
822 if (!std::isnan(temp_data.gVar)) {
823 if (EvalPriority(n0183_msg, active_priority_variation,
824 priority_map_variation)) {
825 gVar = temp_data.gVar;
826 valid_flag += VAR_UPDATE;
827 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
828 }
829 }
830
831 if (bHDM) MakeHDTFromHDM();
832
833 SendBasicNavdata(valid_flag, m_log_callbacks);
834 return true;
835}
836
837bool CommBridge::HandleN0183_HDM(const N0183MsgPtr& n0183_msg) {
838 string str = n0183_msg->payload;
839 NavData temp_data;
840 ClearNavData(temp_data);
841
842 if (!m_decoder.DecodeHDM(str, temp_data)) return false;
843
844 int valid_flag = 0;
845 if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
846 gHdm = temp_data.gHdm;
847 MakeHDTFromHDM();
848 valid_flag += HDT_UPDATE;
849 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
850 }
851
852 SendBasicNavdata(valid_flag, m_log_callbacks);
853 return true;
854}
855
856bool CommBridge::HandleN0183_VTG(const N0183MsgPtr& n0183_msg) {
857 string str = n0183_msg->payload;
858 NavData temp_data;
859 ClearNavData(temp_data);
860
861 if (!m_decoder.DecodeVTG(str, temp_data)) return false;
862
863 int valid_flag = 0;
864 if (EvalPriority(n0183_msg, active_priority_velocity,
865 priority_map_velocity)) {
866 gSog = temp_data.gSog;
867 valid_flag += SOG_UPDATE;
868 gCog = temp_data.gCog;
869 valid_flag += COG_UPDATE;
870 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
871 }
872
873 SendBasicNavdata(valid_flag, m_log_callbacks);
874 return true;
875}
876
877bool CommBridge::HandleN0183_GSV(const N0183MsgPtr& n0183_msg) {
878 string str = n0183_msg->payload;
879 NavData temp_data;
880 ClearNavData(temp_data);
881
882 if (!m_decoder.DecodeGSV(str, temp_data)) return false;
883
884 int valid_flag = 0;
885 if (EvalPriority(n0183_msg, active_priority_satellites,
886 priority_map_satellites)) {
887 if (temp_data.n_satellites >= 0) {
888 g_SatsInView = temp_data.n_satellites;
889 g_bSatValid = true;
890
891 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
892 }
893 }
894
895 SendBasicNavdata(valid_flag, m_log_callbacks);
896 return true;
897}
898
899bool CommBridge::HandleN0183_GGA(const N0183MsgPtr& n0183_msg) {
900 string str = n0183_msg->payload;
901 NavData temp_data;
902 ClearNavData(temp_data);
903
904 bool is_valid = true;
905 if (!m_decoder.DecodeGGA(str, temp_data)) is_valid = false;
906
907 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
908
909 int valid_flag = 0;
910 if (EvalPriority(n0183_msg, active_priority_position,
911 priority_map_position)) {
912 if (is_valid) {
913 gLat = temp_data.gLat;
914 gLon = temp_data.gLon;
915 valid_flag += POS_VALID;
916 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
917 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
918 }
919 valid_flag += POS_UPDATE;
920 }
921
922 if (EvalPriority(n0183_msg, active_priority_satellites,
923 priority_map_satellites)) {
924 if (is_valid) {
925 if (temp_data.n_satellites >= 0) {
926 g_SatsInView = temp_data.n_satellites;
927 g_bSatValid = true;
928
929 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
930 }
931 }
932 }
933
934 SendBasicNavdata(valid_flag, m_log_callbacks);
935 return true;
936}
937
938bool CommBridge::HandleN0183_GLL(const N0183MsgPtr& n0183_msg) {
939 string str = n0183_msg->payload;
940 NavData temp_data;
941 ClearNavData(temp_data);
942
943 bool bvalid = true;
944 if (!m_decoder.DecodeGLL(str, temp_data)) bvalid = false;
945
946 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
947
948 int valid_flag = 0;
949 if (EvalPriority(n0183_msg, active_priority_position,
950 priority_map_position)) {
951 if (bvalid) {
952 gLat = temp_data.gLat;
953 gLon = temp_data.gLon;
954 valid_flag += POS_VALID;
955 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
956 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
957 }
958 valid_flag += POS_UPDATE;
959 }
960
961 SendBasicNavdata(valid_flag, m_log_callbacks);
962 return true;
963}
964
965bool CommBridge::HandleN0183_AIVDO(const N0183MsgPtr& n0183_msg) {
966 string str = n0183_msg->payload;
967
968 GenericPosDatEx gpd;
969 wxString sentence(str.c_str());
970
971 AisError ais_error = AIS_GENERIC_ERROR;
972 ais_error = DecodeSingleVDO(sentence, &gpd);
973
974 if (ais_error == AIS_NoError) {
975 int valid_flag = 0;
976 if (!std::isnan(gpd.kLat) && !std::isnan(gpd.kLon)) {
977 if (EvalPriority(n0183_msg, active_priority_position,
978 priority_map_position)) {
979 gLat = gpd.kLat;
980 gLon = gpd.kLon;
981 valid_flag += POS_UPDATE;
982 valid_flag += POS_VALID;
983 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
984 m_n_log_watchdog_period =
985 N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
986 }
987 }
988
989 if (!std::isnan(gpd.kCog) && !std::isnan(gpd.kSog)) {
990 if (EvalPriority(n0183_msg, active_priority_velocity,
991 priority_map_velocity)) {
992 gSog = gpd.kSog;
993 valid_flag += SOG_UPDATE;
994 gCog = gpd.kCog;
995 valid_flag += COG_UPDATE;
996 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
997 }
998 }
999
1000 if (!std::isnan(gpd.kHdt)) {
1001 if (EvalPriority(n0183_msg, active_priority_heading,
1002 priority_map_heading)) {
1003 gHdt = gpd.kHdt;
1004 valid_flag += HDT_UPDATE;
1005 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1006 }
1007 }
1008
1009 SendBasicNavdata(valid_flag, m_log_callbacks);
1010 }
1011 return true;
1012}
1013
1014bool CommBridge::HandleSignalK(const SignalKMsgPtr& sK_msg) {
1015 string str = sK_msg->raw_message;
1016
1017 // Ignore messages involving contexts other than ownship, but do log them
1018 if (sK_msg->context_self != sK_msg->context) {
1019 g_pMUX->LogInputMessage(sK_msg, true, false);
1020 return false;
1021 }
1022
1023 g_ownshipMMSI_SK = sK_msg->context_self;
1024
1025 NavData temp_data;
1026 ClearNavData(temp_data);
1027
1028 if (!m_decoder.DecodeSignalK(str, temp_data)) return false;
1029
1030 int valid_flag = 0;
1031
1032 if (!std::isnan(temp_data.gLat) && !std::isnan(temp_data.gLon)) {
1033 if (EvalPriority(sK_msg, active_priority_position, priority_map_position)) {
1034 gLat = temp_data.gLat;
1035 gLon = temp_data.gLon;
1036 valid_flag += POS_UPDATE;
1037 valid_flag += POS_VALID;
1038 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
1039 m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
1040 }
1041 }
1042
1043 if (!std::isnan(temp_data.gSog)) {
1044 if (EvalPriority(sK_msg, active_priority_velocity, priority_map_velocity)) {
1045 gSog = temp_data.gSog;
1046 valid_flag += SOG_UPDATE;
1047 if ((gSog > 0.05) && !std::isnan(temp_data.gCog)) {
1048 gCog = temp_data.gCog;
1049 valid_flag += COG_UPDATE;
1050 }
1051 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
1052 }
1053 }
1054
1055 if (!std::isnan(temp_data.gHdt)) {
1056 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
1057 gHdt = temp_data.gHdt;
1058 valid_flag += HDT_UPDATE;
1059 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1060 }
1061 }
1062
1063 if (!std::isnan(temp_data.gHdm)) {
1064 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
1065 gHdm = temp_data.gHdm;
1066 MakeHDTFromHDM();
1067 valid_flag += HDT_UPDATE;
1068 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1069 }
1070 }
1071
1072 if (!std::isnan(temp_data.gVar)) {
1073 if (EvalPriority(sK_msg, active_priority_variation,
1074 priority_map_variation)) {
1075 gVar = temp_data.gVar;
1076 valid_flag += VAR_UPDATE;
1077 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
1078 }
1079 }
1080
1081 if (temp_data.n_satellites > 0) {
1082 if (EvalPriority(sK_msg, active_priority_satellites,
1083 priority_map_satellites)) {
1084 g_SatsInView = temp_data.n_satellites;
1085 g_bSatValid = true;
1086 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
1087 }
1088 }
1089
1090 if (g_pMUX && g_pMUX->IsLogActive())
1091 g_pMUX->LogInputMessage(sK_msg, false, false);
1092
1093 SendBasicNavdata(valid_flag, m_log_callbacks);
1094 return true;
1095}
1096
1097void CommBridge::ClearPriorityMaps() {
1098 priority_map_position.clear();
1099 priority_map_velocity.clear();
1100 priority_map_heading.clear();
1101 priority_map_variation.clear();
1102 priority_map_satellites.clear();
1103}
1104
1105PriorityContainer& CommBridge::GetPriorityContainer(const string& category) {
1106 if (category == "position")
1107 return active_priority_position;
1108 else if (category == "velocity")
1109 return active_priority_velocity;
1110 else if (category == "heading")
1111 return active_priority_heading;
1112 else if (category == "variation")
1113 return active_priority_variation;
1114 else if (category == "satellites")
1115 return active_priority_satellites;
1116 else
1117 return active_priority_void;
1118}
1119
1120void CommBridge::UpdateAndApplyMaps(const std::vector<string>& new_maps) {
1121 ApplyPriorityMaps(new_maps);
1122 SaveConfig();
1123 PresetPriorityContainers();
1124}
1125
1126bool CommBridge::LoadConfig() {
1127 if (TheBaseConfig()) {
1128 TheBaseConfig()->SetPath("/Settings/CommPriority");
1129
1130 std::vector<string> new_maps;
1131 wxString pri_string;
1132
1133 TheBaseConfig()->Read("PriorityPosition", &pri_string);
1134 string s_prio = string(pri_string.c_str());
1135 new_maps.push_back(s_prio);
1136
1137 TheBaseConfig()->Read("PriorityVelocity", &pri_string);
1138 s_prio = string(pri_string.c_str());
1139 new_maps.push_back(s_prio);
1140
1141 TheBaseConfig()->Read("PriorityHeading", &pri_string);
1142 s_prio = string(pri_string.c_str());
1143 new_maps.push_back(s_prio);
1144
1145 TheBaseConfig()->Read("PriorityVariation", &pri_string);
1146 s_prio = string(pri_string.c_str());
1147 new_maps.push_back(s_prio);
1148
1149 TheBaseConfig()->Read("PrioritySatellites", &pri_string);
1150 s_prio = string(pri_string.c_str());
1151 new_maps.push_back(s_prio);
1152
1153 ApplyPriorityMaps(new_maps);
1154 }
1155 return true;
1156}
1157
1158bool CommBridge::SaveConfig() const {
1159 if (TheBaseConfig()) {
1160 TheBaseConfig()->SetPath("/Settings/CommPriority");
1161
1162 wxString pri_string =
1163 wxString(GetPriorityMap(priority_map_position).c_str());
1164 TheBaseConfig()->Write("PriorityPosition", pri_string);
1165
1166 pri_string = wxString(GetPriorityMap(priority_map_velocity).c_str());
1167 TheBaseConfig()->Write("PriorityVelocity", pri_string);
1168
1169 pri_string = wxString(GetPriorityMap(priority_map_heading).c_str());
1170 TheBaseConfig()->Write("PriorityHeading", pri_string);
1171
1172 pri_string = wxString(GetPriorityMap(priority_map_variation).c_str());
1173 TheBaseConfig()->Write("PriorityVariation", pri_string);
1174
1175 pri_string = wxString(GetPriorityMap(priority_map_satellites).c_str());
1176 TheBaseConfig()->Write("PrioritySatellites", pri_string);
1177 }
1178 return true;
1179}
1180
1181bool CommBridge::EvalPriority(const NavMsgPtr& msg,
1182 PriorityContainer& active_priority,
1183 PriorityMap& priority_map) {
1184 string this_key = GetPriorityKey(msg);
1185 if (debug_priority) printf("This Key: %s\n", this_key.c_str());
1186
1187 // Pull some identifiers from the unique key
1188 wxStringTokenizer tkz(this_key, ";");
1189 wxString wxs_this_source = tkz.GetNextToken();
1190 string source = wxs_this_source.ToStdString();
1191 wxString wxs_this_identifier = tkz.GetNextToken();
1192 string this_identifier = wxs_this_identifier.ToStdString();
1193
1194 wxStringTokenizer tka(wxs_this_source, ":");
1195 tka.GetNextToken();
1196 std::stringstream ss;
1197 ss << tka.GetNextToken();
1198 int source_address;
1199 ss >> source_address;
1200
1201 // Special case priority value linkage for N0183 messages:
1202 // If this is a "velocity" record, ensure that a "position"
1203 // report has been accepted from the same source before accepting the
1204 // velocity record.
1205 // This ensures that the data source is fully initialized, and is reporting
1206 // valid, sensible velocity data.
1207 if (msg->bus == NavAddr::Bus::N0183) {
1208 if (!strncmp(active_priority.prio_class.c_str(), "velocity", 8)) {
1209 bool pos_ok = false;
1210 if (!strcmp(active_priority_position.active_source.c_str(),
1211 source.c_str())) {
1212 if (active_priority_position.recent_active_time != -1) {
1213 pos_ok = true;
1214 }
1215 }
1216 if (!pos_ok) return false;
1217 }
1218 }
1219
1220 // Fetch the established priority for the message
1221 int this_priority;
1222
1223 auto it = priority_map.find(this_key);
1224 if (it == priority_map.end()) {
1225 // Not found, so make it default the lowest priority
1226 size_t n = priority_map.size();
1227 priority_map[this_key] = static_cast<int>(n);
1228 }
1229
1230 this_priority = priority_map[this_key];
1231
1232 if (debug_priority) {
1233 for (const auto& jt : priority_map) {
1234 printf(" priority_map: %s %d\n", jt.first.c_str(),
1235 jt.second);
1236 }
1237 }
1238
1239 // Incoming message priority lower than currently active priority?
1240 // If so, drop the message
1241 if (this_priority > active_priority.active_priority) {
1242 if (debug_priority)
1243 printf(" Drop low priority: %s %d %d \n", source.c_str(),
1244 this_priority, active_priority.active_priority);
1245 return false;
1246 }
1247
1248 // A channel returning, after watchdog time out.
1249 if (this_priority < active_priority.active_priority) {
1250 active_priority.active_priority = this_priority;
1251 active_priority.active_source = source;
1252 active_priority.active_identifier = this_identifier;
1253 active_priority.active_source_address = source_address;
1254 wxDateTime now = wxDateTime::Now();
1255 active_priority.recent_active_time = now.GetTicks();
1256
1257 if (debug_priority)
1258 printf(" Restoring high priority: %s %d\n", source.c_str(),
1259 this_priority);
1260 return true;
1261 }
1262
1263 // Do we see two sources with the same priority?
1264 // If so, we take the first one, and deprioritize this one.
1265
1266 if (!active_priority.active_source.empty()) {
1267 if (debug_priority) printf("source: %s\n", source.c_str());
1268 if (debug_priority)
1269 printf("active_source: %s\n", active_priority.active_source.c_str());
1270
1271 if (source != active_priority.active_source) {
1272 // Auto adjust the priority of these this message down
1273 // First, find the lowest priority in use in this map
1274 int lowest_priority = -10; // safe enough
1275 for (const auto& jt : priority_map) {
1276 if (jt.second > lowest_priority) lowest_priority = jt.second;
1277 }
1278
1279 priority_map[this_key] = lowest_priority + 1;
1280 if (debug_priority)
1281 printf(" Lowering priority A: %s :%d\n", source.c_str(),
1282 priority_map[this_key]);
1283 return false;
1284 }
1285 }
1286
1287 // For N0183 message, has the Mnemonic (id) changed?
1288 // Example: RMC and AIVDO from same source.
1289
1290 if (msg->bus == NavAddr::Bus::N0183) {
1291 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
1292 if (msg_0183) {
1293 if (!active_priority.active_identifier.empty()) {
1294 if (debug_priority)
1295 printf("this_identifier: %s\n", this_identifier.c_str());
1296 if (debug_priority)
1297 printf("active_priority.active_identifier: %s\n",
1298 active_priority.active_identifier.c_str());
1299
1300 if (this_identifier != active_priority.active_identifier) {
1301 // if necessary, auto adjust the priority of this message down
1302 // and drop it
1303 if (priority_map[this_key] == active_priority.active_priority) {
1304 int lowest_priority = -10; // safe enough
1305 for (const auto& jt : priority_map) {
1306 if (jt.second > lowest_priority) lowest_priority = jt.second;
1307 }
1308
1309 priority_map[this_key] = lowest_priority + 1;
1310 if (debug_priority)
1311 printf(" Lowering priority B: %s :%d\n", source.c_str(),
1312 priority_map[this_key]);
1313 }
1314
1315 return false;
1316 }
1317 }
1318 }
1319 }
1320
1321 // Similar for n2k PGN...
1322
1323 else if (msg->bus == NavAddr::Bus::N2000) {
1324 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
1325 if (msg_n2k) {
1326 if (!active_priority.active_identifier.empty()) {
1327 if (this_identifier != active_priority.active_identifier) {
1328 // if necessary, auto adjust the priority of this message down
1329 // and drop it
1330 if (priority_map[this_key] == active_priority.active_priority) {
1331 int lowest_priority = -10; // safe enough
1332 for (const auto& jt : priority_map) {
1333 if (jt.second > lowest_priority) lowest_priority = jt.second;
1334 }
1335
1336 priority_map[this_key] = lowest_priority + 1;
1337 if (debug_priority)
1338 printf(" Lowering priority: %s :%d\n", source.c_str(),
1339 priority_map[this_key]);
1340 }
1341
1342 return false;
1343 }
1344 }
1345 }
1346 }
1347
1348 // Update the records
1349 active_priority.active_source = source;
1350 active_priority.active_identifier = this_identifier;
1351 active_priority.active_source_address = source_address;
1352 wxDateTime now = wxDateTime::Now();
1353 active_priority.recent_active_time = now.GetTicks();
1354 if (debug_priority)
1355 printf(" Accepting high priority: %s %d\n", source.c_str(), this_priority);
1356
1357 if (active_priority.prio_class == "position") {
1358 if (this_priority != m_last_position_priority) {
1359 m_last_position_priority = this_priority;
1360
1361 wxString msg_;
1362 msg_.Printf(_("GNSS position fix priority shift:") + " %s\n %s \n -> %s",
1363 this_identifier.c_str(), m_last_position_source.c_str(),
1364 source.c_str());
1365 auto& noteman = NotificationManager::GetInstance();
1366 noteman.AddNotification(NotificationSeverity::kInformational,
1367 msg_.ToStdString());
1368 }
1369 m_last_position_source = source;
1370 }
1371
1372 return true;
1373}
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.
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