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