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