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