OpenCPN Partial API docs
Loading...
Searching...
No Matches
comm_bridge.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose:
5 * Author: David Register, Alec Leamas
6 *
7 ***************************************************************************
8 * Copyright (C) 2022 by David Register, Alec Leamas *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26// For compilers that support precompilation, includes "wx.h".
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif // precompiled headers
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"
39#include "model/comm_appmsg_bus.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// comm event definitions
54wxDEFINE_EVENT(EVT_N2K_129029, ObservedEvt);
55wxDEFINE_EVENT(EVT_N2K_129025, ObservedEvt);
56wxDEFINE_EVENT(EVT_N2K_129026, ObservedEvt);
57wxDEFINE_EVENT(EVT_N2K_127250, ObservedEvt);
58wxDEFINE_EVENT(EVT_N2K_129540, ObservedEvt);
59
60wxDEFINE_EVENT(EVT_N0183_RMC, ObservedEvt);
61wxDEFINE_EVENT(EVT_N0183_HDT, ObservedEvt);
62wxDEFINE_EVENT(EVT_N0183_HDG, ObservedEvt);
63wxDEFINE_EVENT(EVT_N0183_HDM, ObservedEvt);
64wxDEFINE_EVENT(EVT_N0183_VTG, ObservedEvt);
65wxDEFINE_EVENT(EVT_N0183_GSV, ObservedEvt);
66wxDEFINE_EVENT(EVT_N0183_GGA, ObservedEvt);
67wxDEFINE_EVENT(EVT_N0183_GLL, ObservedEvt);
68wxDEFINE_EVENT(EVT_N0183_AIVDO, ObservedEvt);
69
70wxDEFINE_EVENT(EVT_DRIVER_CHANGE, wxCommandEvent);
71
72wxDEFINE_EVENT(EVT_SIGNALK, ObservedEvt);
73
74#define N_ACTIVE_LOG_WATCHDOG 300
75
76extern Multiplexer* g_pMUX;
77
78bool debug_priority = 0;
79
80void ClearNavData(NavData& d) {
81 d.gLat = NAN;
82 d.gLon = NAN;
83 d.gSog = NAN;
84 d.gCog = NAN;
85 d.gHdt = NAN;
86 d.gHdm = NAN;
87 d.gVar = NAN;
88 d.n_satellites = -1;
89 d.SID = 0;
90}
91static NmeaLog* GetDataMonitor() {
92 auto w = wxWindow::FindWindowByName(kDataMonitorWindowName);
93 return dynamic_cast<NmeaLog*>(w);
94}
95static BridgeLogCallbacks GetLogCallbacks() {
96 BridgeLogCallbacks log_callbacks;
97 log_callbacks.log_is_active = [&]() {
98 auto log = GetDataMonitor();
99 return log && log->IsVisible();
100 };
101 log_callbacks.log_message = [&](Logline ll) {
102 NmeaLog* monitor = GetDataMonitor();
103 if (monitor && monitor->IsVisible()) monitor->Add(ll);
104 };
105 return log_callbacks;
106}
107
108class AppNavMsg : public NavMsg {
109public:
110 AppNavMsg(const std::shared_ptr<const AppMsg>& msg, const std::string& name)
111 : NavMsg(NavAddr::Bus::AppMsg,
112 std::make_shared<const NavAddrPlugin>("AppMsg")),
113 m_to_string(msg->to_string()),
114 m_name(name) {}
115
116 std::string to_string() const override { return m_to_string; }
117
118 std::string key() const override { return "appmsg::" + m_name; }
119
120 const std::string m_to_string;
121 const std::string m_name;
122};
123
124static void LogAppMsg(const std::shared_ptr<const AppMsg>& msg,
125 const std::string& name,
126 const BridgeLogCallbacks& log_cb) {
127 if (!log_cb.log_is_active()) return;
128 auto navmsg = std::make_shared<AppNavMsg>(msg, "basic-navdata");
129 NavmsgStatus ns;
130 Logline ll(navmsg, ns);
131 log_cb.log_message(ll);
132}
133
138static void SendBasicNavdata(int vflag, BridgeLogCallbacks log_callbacks) {
139 auto msg = std::make_shared<BasicNavDataMsg>(
140 gLat, gLon, gSog, gCog, gVar, gHdt, vflag, wxDateTime::Now().GetTicks());
141 clock_gettime(CLOCK_MONOTONIC, &msg->set_time);
142 LogAppMsg(msg, "basic-navdata", log_callbacks);
143 AppMsgBus::GetInstance().Notify(std::move(msg));
144}
145
146static inline double GeodesicRadToDeg(double rads) {
147 return rads * 180.0 / M_PI;
148}
149
150static inline double MS2KNOTS(double ms) { return ms * 1.9438444924406; }
151
152// CommBridge implementation
153
154BEGIN_EVENT_TABLE(CommBridge, wxEvtHandler)
155EVT_TIMER(WATCHDOG_TIMER, CommBridge::OnWatchdogTimer)
156END_EVENT_TABLE()
157
159
160CommBridge::~CommBridge() {}
161
162bool CommBridge::Initialize() {
163 m_log_callbacks = GetLogCallbacks();
164 InitializePriorityContainers();
165 ClearPriorityMaps();
166
167 LoadConfig();
168 PresetPriorityContainers();
169
170 // Clear the watchdogs
171 PresetWatchdogs();
172
173 m_watchdog_timer.SetOwner(this, WATCHDOG_TIMER);
174 m_watchdog_timer.Start(1000, wxTIMER_CONTINUOUS);
175 n_LogWatchdogPeriod = 3600; // every 60 minutes,
176 // reduced after first position Rx
177
178 // Initialize the comm listeners
179 InitCommListeners();
180
181 // Initialize a listener for driver state changes
182 driver_change_listener.Listen(
183 CommDriverRegistry::GetInstance().evt_driverlist_change.key, this,
184 EVT_DRIVER_CHANGE);
185 Bind(EVT_DRIVER_CHANGE, [&](wxCommandEvent ev) { OnDriverStateChange(); });
186
187 return true;
188}
189
190void CommBridge::PresetWatchdogs() {
191 m_watchdogs.position_watchdog =
192 20; // A bit longer watchdog for startup latency.
193 m_watchdogs.velocity_watchdog = 20;
194 m_watchdogs.variation_watchdog = 20;
195 m_watchdogs.heading_watchdog = 20;
196 m_watchdogs.satellite_watchdog = 20;
197}
198
199bool CommBridge::IsNextLowerPriorityAvailable(
200 const std::unordered_map<std::string, int>& map, PriorityContainer& pc) {
201 int best_prio = 100;
202 for (auto it = map.begin(); it != map.end(); it++) {
203 if (it->second > pc.active_priority) {
204 best_prio = wxMin(best_prio, it->second);
205 }
206 }
207 return best_prio != pc.active_priority;
208}
209
210void CommBridge::SelectNextLowerPriority(
211 const std::unordered_map<std::string, int>& map, PriorityContainer& pc) {
212 int best_prio = 100;
213 for (auto it = map.begin(); it != map.end(); it++) {
214 if (it->second > pc.active_priority) {
215 best_prio = wxMin(best_prio, it->second);
216 }
217 }
218
219 pc.active_priority = best_prio;
220 pc.active_source.clear();
221 pc.active_identifier.clear();
222}
223
224void CommBridge::OnWatchdogTimer(wxTimerEvent& event) {
225 // Update and check watchdog timer for GPS data source
226 m_watchdogs.position_watchdog--;
227 if (m_watchdogs.position_watchdog <= 0) {
228 if (m_watchdogs.position_watchdog % 5 == 0) {
229 // Send AppMsg telling of watchdog expiry
230 auto msg = std::make_shared<GPSWatchdogMsg>(
231 GPSWatchdogMsg::WDSource::position, m_watchdogs.position_watchdog);
232 auto& msgbus = AppMsgBus::GetInstance();
233 LogAppMsg(msg, "watchdog", m_log_callbacks);
234 msgbus.Notify(std::move(msg));
235
236 if (m_watchdogs.position_watchdog % n_LogWatchdogPeriod == 0) {
237 wxString logmsg;
238 logmsg.Printf(_T(" ***GPS Watchdog timeout at Lat:%g Lon: %g"),
239 gLat, gLon);
240 wxLogMessage(logmsg);
241 }
242 }
243
244 gSog = NAN;
245 gCog = NAN;
246 gRmcDate.Empty();
247 gRmcTime.Empty();
248 active_priority_position.recent_active_time = -1;
249
250 // Are there any other lower priority sources?
251 // If so, adopt that one.
252 if (IsNextLowerPriorityAvailable(priority_map_position,
253 active_priority_position)) {
254 SelectNextLowerPriority(priority_map_position, active_priority_position);
255 }
256 }
257
258 // Update and check watchdog timer for SOG/COG data source
259 m_watchdogs.velocity_watchdog--;
260 if (m_watchdogs.velocity_watchdog <= 0) {
261 gSog = NAN;
262 gCog = NAN;
263 active_priority_velocity.recent_active_time = -1;
264
265 if (g_nNMEADebug && (m_watchdogs.velocity_watchdog == 0))
266 wxLogMessage(_T(" ***Velocity Watchdog timeout..."));
267 if (m_watchdogs.velocity_watchdog % 5 == 0) {
268 // Send AppMsg telling of watchdog expiry
269 auto msg = std::make_shared<GPSWatchdogMsg>(
270 GPSWatchdogMsg::WDSource::velocity, m_watchdogs.velocity_watchdog);
271 auto& msgbus = AppMsgBus::GetInstance();
272 msgbus.Notify(std::move(msg));
273 }
274 // Are there any other lower priority sources?
275 // If so, adopt that one.
276 SelectNextLowerPriority(priority_map_velocity, active_priority_velocity);
277 }
278
279 // Update and check watchdog timer for True Heading data source
280 m_watchdogs.heading_watchdog--;
281 if (m_watchdogs.heading_watchdog <= 0) {
282 gHdt = NAN;
283 active_priority_heading.recent_active_time = -1;
284 if (g_nNMEADebug && (m_watchdogs.heading_watchdog == 0))
285 wxLogMessage(_T(" ***HDT Watchdog timeout..."));
286
287 // Are there any other lower priority sources?
288 // If so, adopt that one.
289 SelectNextLowerPriority(priority_map_heading, active_priority_heading);
290 }
291
292 // Update and check watchdog timer for Magnetic Variation data source
293 m_watchdogs.variation_watchdog--;
294 if (m_watchdogs.variation_watchdog <= 0) {
295 g_bVAR_Rx = false;
296 active_priority_variation.recent_active_time = -1;
297
298 if (g_nNMEADebug && (m_watchdogs.variation_watchdog == 0))
299 wxLogMessage(_T(" ***VAR Watchdog timeout..."));
300
301 // Are there any other lower priority sources?
302 // If so, adopt that one.
303 SelectNextLowerPriority(priority_map_variation, active_priority_variation);
304 }
305
306 // Update and check watchdog timer for GSV, GGA and SignalK (Satellite data)
307 m_watchdogs.satellite_watchdog--;
308 if (m_watchdogs.satellite_watchdog <= 0) {
309 g_bSatValid = false;
310 g_SatsInView = 0;
311 g_priSats = 99;
312 active_priority_satellites.recent_active_time = -1;
313
314 if (g_nNMEADebug && (m_watchdogs.satellite_watchdog == 0))
315 wxLogMessage(_T(" ***SAT Watchdog timeout..."));
316
317 // Are there any other lower priority sources?
318 // If so, adopt that one.
319 SelectNextLowerPriority(priority_map_satellites,
320 active_priority_satellites);
321 }
322}
323
324void CommBridge::MakeHDTFromHDM() {
325 // Here is the one place we try to create gHdt from gHdm and gVar,
326
327 if (!std::isnan(gHdm)) {
328 // Set gVar if needed from manual entry. gVar will be overwritten if
329 // WMM plugin is available
330 if (std::isnan(gVar) && (g_UserVar != 0.0)) gVar = g_UserVar;
331 gHdt = gHdm + gVar;
332 if (!std::isnan(gHdt)) {
333 if (gHdt < 0)
334 gHdt += 360.0;
335 else if (gHdt >= 360)
336 gHdt -= 360.0;
337
338 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
339 }
340 }
341}
342
343void CommBridge::InitCommListeners() {
344 // Initialize the comm listeners
345 auto& msgbus = NavMsgBus::GetInstance();
346
347 // GNSS Position Data PGN 129029
348 //----------------------------------
349 Nmea2000Msg n2k_msg_129029(static_cast<uint64_t>(129029));
350 listener_N2K_129029.Listen(n2k_msg_129029, this, EVT_N2K_129029);
351
352 Bind(EVT_N2K_129029, [&](ObservedEvt ev) {
353 HandleN2K_129029(UnpackEvtPointer<Nmea2000Msg>(ev));
354 });
355
356 // Position rapid PGN 129025
357 //-----------------------------
358 Nmea2000Msg n2k_msg_129025(static_cast<uint64_t>(129025));
359 listener_N2K_129025.Listen(n2k_msg_129025, this, EVT_N2K_129025);
360 Bind(EVT_N2K_129025, [&](ObservedEvt ev) {
361 HandleN2K_129025(UnpackEvtPointer<Nmea2000Msg>(ev));
362 });
363
364 // COG SOG rapid PGN 129026
365 //-----------------------------
366 Nmea2000Msg n2k_msg_129026(static_cast<uint64_t>(129026));
367 listener_N2K_129026.Listen(n2k_msg_129026, this, EVT_N2K_129026);
368 Bind(EVT_N2K_129026, [&](ObservedEvt ev) {
369 HandleN2K_129026(UnpackEvtPointer<Nmea2000Msg>(ev));
370 });
371
372 // Heading rapid PGN 127250
373 //-----------------------------
374 Nmea2000Msg n2k_msg_127250(static_cast<uint64_t>(127250));
375 listener_N2K_127250.Listen(n2k_msg_127250, this, EVT_N2K_127250);
376 Bind(EVT_N2K_127250, [&](ObservedEvt ev) {
377 HandleN2K_127250(UnpackEvtPointer<Nmea2000Msg>(ev));
378 });
379
380 // GNSS Satellites in View PGN 129540
381 //-----------------------------
382 Nmea2000Msg n2k_msg_129540(static_cast<uint64_t>(129540));
383 listener_N2K_129540.Listen(n2k_msg_129540, this, EVT_N2K_129540);
384 Bind(EVT_N2K_129540, [&](ObservedEvt ev) {
385 HandleN2K_129540(UnpackEvtPointer<Nmea2000Msg>(ev));
386 });
387
388 // NMEA0183
389 // RMC
390 Nmea0183Msg n0183_msg_RMC("RMC");
391 listener_N0183_RMC.Listen(n0183_msg_RMC, this, EVT_N0183_RMC);
392
393 Bind(EVT_N0183_RMC, [&](ObservedEvt ev) {
394 HandleN0183_RMC(UnpackEvtPointer<Nmea0183Msg>(ev));
395 });
396
397 // HDT
398 Nmea0183Msg n0183_msg_HDT("HDT");
399 listener_N0183_HDT.Listen(n0183_msg_HDT, this, EVT_N0183_HDT);
400
401 Bind(EVT_N0183_HDT, [&](ObservedEvt ev) {
402 HandleN0183_HDT(UnpackEvtPointer<Nmea0183Msg>(ev));
403 });
404
405 // HDG
406 Nmea0183Msg n0183_msg_HDG("HDG");
407 listener_N0183_HDG.Listen(n0183_msg_HDG, this, EVT_N0183_HDG);
408
409 Bind(EVT_N0183_HDG, [&](ObservedEvt ev) {
410 HandleN0183_HDG(UnpackEvtPointer<Nmea0183Msg>(ev));
411 });
412
413 // HDM
414 Nmea0183Msg n0183_msg_HDM("HDM");
415 listener_N0183_HDM.Listen(n0183_msg_HDM, this, EVT_N0183_HDM);
416
417 Bind(EVT_N0183_HDM, [&](ObservedEvt ev) {
418 HandleN0183_HDM(UnpackEvtPointer<Nmea0183Msg>(ev));
419 });
420
421 // VTG
422 Nmea0183Msg n0183_msg_VTG("VTG");
423 listener_N0183_VTG.Listen(n0183_msg_VTG, this, EVT_N0183_VTG);
424
425 Bind(EVT_N0183_VTG, [&](ObservedEvt ev) {
426 HandleN0183_VTG(UnpackEvtPointer<Nmea0183Msg>(ev));
427 });
428
429 // GSV
430 Nmea0183Msg n0183_msg_GSV("GSV");
431 listener_N0183_GSV.Listen(n0183_msg_GSV, this, EVT_N0183_GSV);
432
433 Bind(EVT_N0183_GSV, [&](ObservedEvt ev) {
434 HandleN0183_GSV(UnpackEvtPointer<Nmea0183Msg>(ev));
435 });
436
437 // GGA
438 Nmea0183Msg n0183_msg_GGA("GGA");
439 listener_N0183_GGA.Listen(n0183_msg_GGA, this, EVT_N0183_GGA);
440
441 Bind(EVT_N0183_GGA, [&](ObservedEvt ev) {
442 HandleN0183_GGA(UnpackEvtPointer<Nmea0183Msg>(ev));
443 });
444
445 // GLL
446 Nmea0183Msg n0183_msg_GLL("GLL");
447 listener_N0183_GLL.Listen(n0183_msg_GLL, this, EVT_N0183_GLL);
448
449 Bind(EVT_N0183_GLL, [&](ObservedEvt ev) {
450 HandleN0183_GLL(UnpackEvtPointer<Nmea0183Msg>(ev));
451 });
452
453 // AIVDO
454 Nmea0183Msg n0183_msg_AIVDO("AIVDO");
455 listener_N0183_AIVDO.Listen(n0183_msg_AIVDO, this, EVT_N0183_AIVDO);
456
457 Bind(EVT_N0183_AIVDO, [&](ObservedEvt ev) {
458 HandleN0183_AIVDO(UnpackEvtPointer<Nmea0183Msg>(ev));
459 });
460
461 // SignalK
462 SignalkMsg sk_msg;
463 listener_SignalK.Listen(sk_msg, this, EVT_SIGNALK);
464
465 Bind(EVT_SIGNALK, [&](ObservedEvt ev) {
466 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
467 });
468}
469
470void CommBridge::OnDriverStateChange() {
471 // Reset all active priority states
472 PresetPriorityContainers();
473}
474
475std::string CommBridge::GetPriorityMap(
476 std::unordered_map<std::string, int>& map) {
477#define MAX_SOURCES 10
478 std::string sa[MAX_SOURCES];
479 std::string result;
480
481 for (auto& it : map) {
482 if ((it.second >= 0) && (it.second < MAX_SOURCES)) sa[it.second] = it.first;
483 }
484
485 // build the packed string result
486 for (int i = 0; i < MAX_SOURCES; i++) {
487 if (sa[i].size()) {
488 result += sa[i];
489 result += "|";
490 }
491 }
492
493 return result;
494}
495
496std::vector<std::string> CommBridge::GetPriorityMaps() {
497 std::vector<std::string> result;
498
499 result.push_back(GetPriorityMap(priority_map_position));
500 result.push_back(GetPriorityMap(priority_map_velocity));
501 result.push_back(GetPriorityMap(priority_map_heading));
502 result.push_back(GetPriorityMap(priority_map_variation));
503 result.push_back(GetPriorityMap(priority_map_satellites));
504
505 return result;
506}
507
508void CommBridge::ApplyPriorityMap(
509 std::unordered_map<std::string, int>& priority_map, wxString& new_prio,
510 int category) {
511 priority_map.clear();
512 wxStringTokenizer tk(new_prio, "|");
513 int index = 0;
514 while (tk.HasMoreTokens()) {
515 wxString entry = tk.GetNextToken();
516 std::string s_entry(entry.c_str());
517 priority_map[s_entry] = index;
518 index++;
519 }
520}
521
522void CommBridge::ApplyPriorityMaps(std::vector<std::string> new_maps) {
523 wxString new_prio_string;
524
525 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::PresetPriorityContainer(
543 const std::unordered_map<std::string, int>& priority_map) {
544 // Extract some info from the preloaded map
545 // Find the key corresponding to priority 0, the highest
546 std::string key0;
547 for (auto& it : priority_map) {
548 if (it.second == 0) key0 = it.first;
549 }
550
551 wxString this_key(key0.c_str());
552 wxStringTokenizer tkz(this_key, _T(";"));
553 wxString wxs_this_source = tkz.GetNextToken();
554 std::string source = wxs_this_source.ToStdString();
555 wxString wxs_this_identifier = tkz.GetNextToken();
556 std::string this_identifier = wxs_this_identifier.ToStdString();
557
558 wxStringTokenizer tka(wxs_this_source, _T(":"));
559 tka.GetNextToken();
560 int source_address = atoi(tka.GetNextToken().ToStdString().c_str());
561
562 pc.active_priority = 0;
563 pc.active_source = source;
564 pc.active_identifier = this_identifier;
565 pc.active_source_address = source_address;
566 pc.recent_active_time = -1;
567}
568
569void CommBridge::PresetPriorityContainers() {
570 PresetPriorityContainer(active_priority_position, priority_map_position);
571 PresetPriorityContainer(active_priority_velocity, priority_map_velocity);
572 PresetPriorityContainer(active_priority_heading, priority_map_heading);
573 PresetPriorityContainer(active_priority_variation, priority_map_variation);
574 PresetPriorityContainer(active_priority_satellites, priority_map_satellites);
575}
576
577bool CommBridge::HandleN2K_129029(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
578 std::vector<unsigned char> v = n2k_msg->payload;
579
580 // extract and verify PGN
581 uint64_t pgn = 0;
582 unsigned char* c = (unsigned char*)&pgn;
583 *c++ = v.at(3);
584 *c++ = v.at(4);
585 *c++ = v.at(5);
586
587 NavData temp_data;
588 ClearNavData(temp_data);
589
590 if (!m_decoder.DecodePGN129029(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 n_LogWatchdogPeriod = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
602 }
603 }
604
605 if (temp_data.n_satellites >= 0) {
606 if (EvalPriority(n2k_msg, active_priority_satellites,
607 priority_map_satellites)) {
608 g_SatsInView = temp_data.n_satellites;
609 g_bSatValid = true;
610 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
611 }
612 }
613
614 SendBasicNavdata(valid_flag, m_log_callbacks);
615 return true;
616}
617
618bool CommBridge::HandleN2K_129025(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
619 std::vector<unsigned char> v = n2k_msg->payload;
620
621 NavData temp_data;
622 ClearNavData(temp_data);
623
624 if (!m_decoder.DecodePGN129025(v, temp_data)) return false;
625
626 int valid_flag = 0;
627 if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
628 if (EvalPriority(n2k_msg, active_priority_position,
629 priority_map_position)) {
630 gLat = temp_data.gLat;
631 gLon = temp_data.gLon;
632 valid_flag += POS_UPDATE;
633 valid_flag += POS_VALID;
634 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
635 n_LogWatchdogPeriod = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
636 }
637 }
638 // FIXME (dave) How to notify user of errors?
639 else {
640 }
641
642 SendBasicNavdata(valid_flag, m_log_callbacks);
643 return true;
644}
645
646bool CommBridge::HandleN2K_129026(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
647 std::vector<unsigned char> v = n2k_msg->payload;
648
649 NavData temp_data;
650 ClearNavData(temp_data);
651
652 if (!m_decoder.DecodePGN129026(v, temp_data)) return false;
653
654 int valid_flag = 0;
655 if (!N2kIsNA(temp_data.gSog)) { // gCog as reported by net may be NaN, but OK
656 if (EvalPriority(n2k_msg, active_priority_velocity,
657 priority_map_velocity)) {
658 gSog = MS2KNOTS(temp_data.gSog);
659 valid_flag += SOG_UPDATE;
660
661 if (N2kIsNA(temp_data.gCog))
662 gCog = NAN;
663 else
664 gCog = GeodesicRadToDeg(temp_data.gCog);
665 valid_flag += COG_UPDATE;
666 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
667 }
668 } else {
669 }
670
671 SendBasicNavdata(valid_flag, m_log_callbacks);
672 return true;
673}
674
675bool CommBridge::HandleN2K_127250(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
676 std::vector<unsigned char> v = n2k_msg->payload;
677
678 NavData temp_data;
679 ClearNavData(temp_data);
680
681 if (!m_decoder.DecodePGN127250(v, temp_data)) return false;
682
683 int valid_flag = 0;
684 if (!N2kIsNA(temp_data.gVar)) {
685 if (EvalPriority(n2k_msg, active_priority_variation,
686 priority_map_variation)) {
687 gVar = GeodesicRadToDeg(temp_data.gVar);
688 valid_flag += VAR_UPDATE;
689 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
690 }
691 }
692
693 if (!N2kIsNA(temp_data.gHdt)) {
694 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
695 gHdt = GeodesicRadToDeg(temp_data.gHdt);
696 valid_flag += HDT_UPDATE;
697 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
698 }
699 }
700
701 if (!N2kIsNA(temp_data.gHdm)) {
702 gHdm = GeodesicRadToDeg(temp_data.gHdm);
703 if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
704 MakeHDTFromHDM();
705 valid_flag += HDT_UPDATE;
706 if (!std::isnan(gHdt))
707 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
708 }
709 }
710
711 SendBasicNavdata(valid_flag, m_log_callbacks);
712 return true;
713}
714
715bool CommBridge::HandleN2K_129540(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
716 std::vector<unsigned char> v = n2k_msg->payload;
717
718 NavData temp_data;
719 ClearNavData(temp_data);
720
721 if (!m_decoder.DecodePGN129540(v, temp_data)) return false;
722
723 int valid_flag = 0;
724 if (temp_data.n_satellites >= 0) {
725 if (EvalPriority(n2k_msg, active_priority_satellites,
726 priority_map_satellites)) {
727 g_SatsInView = temp_data.n_satellites;
728 g_bSatValid = true;
729 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
730 }
731 }
732
733 return true;
734}
735
736bool CommBridge::HandleN0183_RMC(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
737 std::string str = n0183_msg->payload;
738
739 NavData temp_data;
740 ClearNavData(temp_data);
741
742 bool bvalid = true;
743 if (!m_decoder.DecodeRMC(str, temp_data)) bvalid = false;
744
745 if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) return false;
746
747 int valid_flag = 0;
748 if (EvalPriority(n0183_msg, active_priority_position,
749 priority_map_position)) {
750 if (bvalid) {
751 gLat = temp_data.gLat;
752 gLon = temp_data.gLon;
753 valid_flag += POS_VALID;
754 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
755 n_LogWatchdogPeriod = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
756 }
757 valid_flag += POS_UPDATE;
758 }
759
760 if (EvalPriority(n0183_msg, active_priority_velocity,
761 priority_map_velocity)) {
762 if (bvalid) {
763 gSog = temp_data.gSog;
764 valid_flag += SOG_UPDATE;
765 gCog = temp_data.gCog;
766 valid_flag += COG_UPDATE;
767 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
768 }
769 }
770
771 if (!std::isnan(temp_data.gVar)) {
772 if (EvalPriority(n0183_msg, active_priority_variation,
773 priority_map_variation)) {
774 if (bvalid) {
775 gVar = temp_data.gVar;
776 valid_flag += VAR_UPDATE;
777 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
778 }
779 }
780 }
781
782 SendBasicNavdata(valid_flag, m_log_callbacks);
783 return true;
784}
785
786bool CommBridge::HandleN0183_HDT(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
787 std::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(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
805 std::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(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
836 std::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(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
855 std::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(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
876 std::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(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
898 std::string str = n0183_msg->payload;
899 NavData temp_data;
900 ClearNavData(temp_data);
901
902 bool bvalid = true;
903 if (!m_decoder.DecodeGGA(str, temp_data)) bvalid = 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 (bvalid) {
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 n_LogWatchdogPeriod = 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 (bvalid) {
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(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
937 std::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 n_LogWatchdogPeriod = 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
964 std::shared_ptr<const Nmea0183Msg> n0183_msg) {
965 std::string str = n0183_msg->payload;
966
967 GenericPosDatEx gpd;
968 wxString sentence(str.c_str());
969
970 int valid_flag = 0;
971
972 AisError nerr = AIS_GENERIC_ERROR;
973 nerr = DecodeSingleVDO(sentence, &gpd);
974
975 if (nerr == AIS_NoError) {
976 if (!std::isnan(gpd.kLat) && !std::isnan(gpd.kLon)) {
977 if (EvalPriority(n0183_msg, active_priority_position,
978 priority_map_position)) {
979 gLat = gpd.kLat;
980 gLon = gpd.kLon;
981 valid_flag += POS_UPDATE;
982 valid_flag += POS_VALID;
983 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
984 n_LogWatchdogPeriod = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
985 }
986 }
987
988 if (!std::isnan(gpd.kCog) && !std::isnan(gpd.kSog)) {
989 if (EvalPriority(n0183_msg, active_priority_velocity,
990 priority_map_velocity)) {
991 gSog = gpd.kSog;
992 valid_flag += SOG_UPDATE;
993 gCog = gpd.kCog;
994 valid_flag += COG_UPDATE;
995 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
996 }
997 }
998
999 if (!std::isnan(gpd.kHdt)) {
1000 if (EvalPriority(n0183_msg, active_priority_heading,
1001 priority_map_heading)) {
1002 gHdt = gpd.kHdt;
1003 valid_flag += HDT_UPDATE;
1004 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1005 }
1006 }
1007
1008 SendBasicNavdata(valid_flag, m_log_callbacks);
1009 }
1010 return true;
1011}
1012
1013bool CommBridge::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg) {
1014 std::string str = sK_msg->raw_message;
1015
1016 // Here we ignore messages involving contexts other than ownship
1017 if (sK_msg->context_self != sK_msg->context) return false;
1018
1019 g_ownshipMMSI_SK = sK_msg->context_self;
1020
1021 NavData temp_data;
1022 ClearNavData(temp_data);
1023
1024 if (!m_decoder.DecodeSignalK(str, temp_data)) return false;
1025
1026 int valid_flag = 0;
1027
1028 if (!std::isnan(temp_data.gLat) && !std::isnan(temp_data.gLon)) {
1029 if (EvalPriority(sK_msg, active_priority_position, priority_map_position)) {
1030 gLat = temp_data.gLat;
1031 gLon = temp_data.gLon;
1032 valid_flag += POS_UPDATE;
1033 valid_flag += POS_VALID;
1034 m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
1035 n_LogWatchdogPeriod = N_ACTIVE_LOG_WATCHDOG; // allow faster dog log
1036 }
1037 }
1038
1039 if (!std::isnan(temp_data.gSog)) {
1040 if (EvalPriority(sK_msg, active_priority_velocity, priority_map_velocity)) {
1041 gSog = temp_data.gSog;
1042 valid_flag += SOG_UPDATE;
1043 if ((gSog > 0.05) && !std::isnan(temp_data.gCog)) {
1044 gCog = temp_data.gCog;
1045 valid_flag += COG_UPDATE;
1046 }
1047 m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
1048 }
1049 }
1050
1051 if (!std::isnan(temp_data.gHdt)) {
1052 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
1053 gHdt = temp_data.gHdt;
1054 valid_flag += HDT_UPDATE;
1055 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1056 }
1057 }
1058
1059 if (!std::isnan(temp_data.gHdm)) {
1060 if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
1061 gHdm = temp_data.gHdm;
1062 MakeHDTFromHDM();
1063 valid_flag += HDT_UPDATE;
1064 m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
1065 }
1066 }
1067
1068 if (!std::isnan(temp_data.gVar)) {
1069 if (EvalPriority(sK_msg, active_priority_variation,
1070 priority_map_variation)) {
1071 gVar = temp_data.gVar;
1072 valid_flag += VAR_UPDATE;
1073 m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
1074 }
1075 }
1076
1077 bool sat_update = false;
1078 if (temp_data.n_satellites > 0) {
1079 if (EvalPriority(sK_msg, active_priority_satellites,
1080 priority_map_satellites)) {
1081 g_SatsInView = temp_data.n_satellites;
1082 g_bSatValid = true;
1083 sat_update = true;
1084 m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
1085 }
1086 }
1087
1088 if (g_pMUX && g_pMUX->IsLogActive()) {
1089 std::string logmsg = "Self: ";
1090 std::string content;
1091 if (valid_flag & POS_UPDATE) content += "POS;";
1092 if (valid_flag & POS_VALID) content += "POS_Valid;";
1093 if (valid_flag & SOG_UPDATE) content += "SOG;";
1094 if (valid_flag & COG_UPDATE) content += "COG;";
1095 if (valid_flag & HDT_UPDATE) content += "HDT;";
1096 if (valid_flag & VAR_UPDATE) content += "VAR;";
1097 if (sat_update) content += "SAT;";
1098
1099 if (content.empty()) content = "Not used by OCPN, maybe passed to plugins";
1100
1101 logmsg += content;
1102 g_pMUX->LogInputMessage(sK_msg, false, false);
1103 }
1104
1105 SendBasicNavdata(valid_flag, m_log_callbacks);
1106 return true;
1107}
1108
1109void CommBridge::InitializePriorityContainers() {
1110 active_priority_position.active_priority = 0;
1111 active_priority_velocity.active_priority = 0;
1112 active_priority_heading.active_priority = 0;
1113 active_priority_variation.active_priority = 0;
1114 active_priority_satellites.active_priority = 0;
1115
1116 active_priority_position.active_source.clear();
1117 active_priority_velocity.active_source.clear();
1118 active_priority_heading.active_source.clear();
1119 active_priority_variation.active_source.clear();
1120 active_priority_satellites.active_source.clear();
1121
1122 active_priority_position.active_identifier.clear();
1123 active_priority_velocity.active_identifier.clear();
1124 active_priority_heading.active_identifier.clear();
1125 active_priority_variation.active_identifier.clear();
1126 active_priority_satellites.active_identifier.clear();
1127
1128 active_priority_position.pcclass = "position";
1129 active_priority_velocity.pcclass = "velocity";
1130 active_priority_heading.pcclass = "heading";
1131 active_priority_variation.pcclass = "variation";
1132 active_priority_satellites.pcclass = "satellites";
1133
1134 active_priority_position.active_source_address = -1;
1135 active_priority_velocity.active_source_address = -1;
1136 active_priority_heading.active_source_address = -1;
1137 active_priority_variation.active_source_address = -1;
1138 active_priority_satellites.active_source_address = -1;
1139
1140 active_priority_void.active_priority = -1;
1141}
1142
1143void CommBridge::ClearPriorityMaps() {
1144 priority_map_position.clear();
1145 priority_map_velocity.clear();
1146 priority_map_heading.clear();
1147 priority_map_variation.clear();
1148 priority_map_satellites.clear();
1149}
1150
1151PriorityContainer& CommBridge::GetPriorityContainer(
1152 const std::string category) {
1153 if (!category.compare("position"))
1154 return active_priority_position;
1155 else if (!category.compare("velocity"))
1156 return active_priority_velocity;
1157 else if (!category.compare("heading"))
1158 return active_priority_heading;
1159 else if (!category.compare("variation"))
1160 return active_priority_variation;
1161 else if (!category.compare("satellites"))
1162 return active_priority_satellites;
1163 else
1164 return active_priority_void;
1165}
1166
1167void CommBridge::UpdateAndApplyMaps(std::vector<std::string> new_maps) {
1168 ApplyPriorityMaps(new_maps);
1169 SaveConfig();
1170 PresetPriorityContainers();
1171}
1172
1173bool CommBridge::LoadConfig(void) {
1174 if (TheBaseConfig()) {
1175 TheBaseConfig()->SetPath("/Settings/CommPriority");
1176
1177 std::vector<std::string> new_maps;
1178 std::string s_prio;
1179 wxString pri_string;
1180
1181 TheBaseConfig()->Read("PriorityPosition", &pri_string);
1182 s_prio = std::string(pri_string.c_str());
1183 new_maps.push_back(s_prio);
1184
1185 TheBaseConfig()->Read("PriorityVelocity", &pri_string);
1186 s_prio = std::string(pri_string.c_str());
1187 new_maps.push_back(s_prio);
1188
1189 TheBaseConfig()->Read("PriorityHeading", &pri_string);
1190 s_prio = std::string(pri_string.c_str());
1191 new_maps.push_back(s_prio);
1192
1193 TheBaseConfig()->Read("PriorityVariation", &pri_string);
1194 s_prio = std::string(pri_string.c_str());
1195 new_maps.push_back(s_prio);
1196
1197 TheBaseConfig()->Read("PrioritySatellites", &pri_string);
1198 s_prio = std::string(pri_string.c_str());
1199 new_maps.push_back(s_prio);
1200
1201 ApplyPriorityMaps(new_maps);
1202 }
1203 return true;
1204}
1205
1206bool CommBridge::SaveConfig(void) {
1207 if (TheBaseConfig()) {
1208 TheBaseConfig()->SetPath("/Settings/CommPriority");
1209
1210 wxString pri_string;
1211 pri_string = wxString(GetPriorityMap(priority_map_position).c_str());
1212 TheBaseConfig()->Write("PriorityPosition", pri_string);
1213
1214 pri_string = wxString(GetPriorityMap(priority_map_velocity).c_str());
1215 TheBaseConfig()->Write("PriorityVelocity", pri_string);
1216
1217 pri_string = wxString(GetPriorityMap(priority_map_heading).c_str());
1218 TheBaseConfig()->Write("PriorityHeading", pri_string);
1219
1220 pri_string = wxString(GetPriorityMap(priority_map_variation).c_str());
1221 TheBaseConfig()->Write("PriorityVariation", pri_string);
1222
1223 pri_string = wxString(GetPriorityMap(priority_map_satellites).c_str());
1224 TheBaseConfig()->Write("PrioritySatellites", pri_string);
1225 }
1226
1227 return true;
1228}
1229
1230std::string CommBridge::GetPriorityKey(std::shared_ptr<const NavMsg> msg) {
1231 std::string source = msg->source->to_string();
1232 std::string listener_key = msg->key();
1233
1234 std::string this_identifier;
1235 std::string this_address("0");
1236 if (msg->bus == NavAddr::Bus::N0183) {
1237 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
1238 if (msg_0183) {
1239 this_identifier = msg_0183->talker;
1240 this_identifier += msg_0183->type;
1241 }
1242 } else if (msg->bus == NavAddr::Bus::N2000) {
1243 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
1244 if (msg_n2k) {
1245 this_identifier = msg_n2k->PGN.to_string();
1246 unsigned char n_source = msg_n2k->payload.at(7);
1247 char ss[4];
1248 sprintf(ss, "%d", n_source);
1249 this_address = std::string(ss);
1250 }
1251 } else if (msg->bus == NavAddr::Bus::Signalk) {
1252 auto msg_sk = std::dynamic_pointer_cast<const SignalkMsg>(msg);
1253 if (msg_sk) {
1254 auto addr_sk =
1255 std::static_pointer_cast<const NavAddrSignalK>(msg->source);
1256 source = addr_sk->to_string();
1257 this_identifier = "signalK";
1258 this_address = msg->source->iface;
1259 }
1260 }
1261
1262 return source + ":" + this_address + ";" + this_identifier;
1263}
1264
1265bool CommBridge::EvalPriority(
1266 std::shared_ptr<const NavMsg> msg, PriorityContainer& active_priority,
1267 std::unordered_map<std::string, int>& priority_map) {
1268 std::string this_key = GetPriorityKey(msg);
1269 if (debug_priority) printf("This Key: %s\n", this_key.c_str());
1270
1271 // Pull some identifiers from the unique key
1272 wxStringTokenizer tkz(this_key, _T(";"));
1273 wxString wxs_this_source = tkz.GetNextToken();
1274 std::string source = wxs_this_source.ToStdString();
1275 wxString wxs_this_identifier = tkz.GetNextToken();
1276 std::string this_identifier = wxs_this_identifier.ToStdString();
1277
1278 wxStringTokenizer tka(wxs_this_source, _T(":"));
1279 tka.GetNextToken();
1280 int source_address = atoi(tka.GetNextToken().ToStdString().c_str());
1281
1282 // Special case priority value linkage for N0183 messages:
1283 // If this is a "velocity" record, ensure that a "position"
1284 // report has been accepted from the same source before accepting the
1285 // velocity record.
1286 // This ensures that the data source is fully initialized, and is reporting
1287 // valid, sensible velocity data.
1288 if (msg->bus == NavAddr::Bus::N0183) {
1289 if (!strncmp(active_priority.pcclass.c_str(), "velocity", 8)) {
1290 bool pos_ok = false;
1291 if (!strcmp(active_priority_position.active_source.c_str(),
1292 source.c_str())) {
1293 if (active_priority_position.recent_active_time != -1) {
1294 pos_ok = true;
1295 }
1296 }
1297 if (!pos_ok) return false;
1298 }
1299 }
1300
1301 // Fetch the established priority for the message
1302 int this_priority;
1303
1304 auto it = priority_map.find(this_key);
1305 if (it == priority_map.end()) {
1306 // Not found, so make it default the lowest priority
1307 size_t n = priority_map.size();
1308 priority_map[this_key] = n;
1309 }
1310
1311 this_priority = priority_map[this_key];
1312
1313 if (debug_priority) {
1314 for (auto it = priority_map.begin(); it != priority_map.end(); it++)
1315 printf(" priority_map: %s %d\n", it->first.c_str(),
1316 it->second);
1317 }
1318
1319 // Incoming message priority lower than currently active priority?
1320 // If so, drop the message
1321 if (this_priority > active_priority.active_priority) {
1322 if (debug_priority)
1323 printf(" Drop low priority: %s %d %d \n", source.c_str(),
1324 this_priority, active_priority.active_priority);
1325 return false;
1326 }
1327
1328 // A channel returning, after being watchdogged out.
1329 if (this_priority < active_priority.active_priority) {
1330 active_priority.active_priority = this_priority;
1331 active_priority.active_source = source;
1332 active_priority.active_identifier = this_identifier;
1333 active_priority.active_source_address = source_address;
1334 wxDateTime now = wxDateTime::Now();
1335 active_priority.recent_active_time = now.GetTicks();
1336
1337 if (debug_priority)
1338 printf(" Restoring high priority: %s %d\n", source.c_str(),
1339 this_priority);
1340 return true;
1341 }
1342
1343 // Do we see two sources with the same priority?
1344 // If so, we take the first one, and deprioritize this one.
1345
1346 if (active_priority.active_source.size()) {
1347 if (debug_priority) printf("source: %s\n", source.c_str());
1348 if (debug_priority)
1349 printf("active_source: %s\n", active_priority.active_source.c_str());
1350
1351 if (source.compare(active_priority.active_source) != 0) {
1352 // Auto adjust the priority of these this message down
1353 // First, find the lowest priority in use in this map
1354 int lowest_priority = -10; // safe enough
1355 for (auto it = priority_map.begin(); it != priority_map.end(); it++) {
1356 if (it->second > lowest_priority) lowest_priority = it->second;
1357 }
1358
1359 priority_map[this_key] = lowest_priority + 1;
1360 if (debug_priority)
1361 printf(" Lowering priority A: %s :%d\n", source.c_str(),
1362 priority_map[this_key]);
1363 return false;
1364 }
1365 }
1366
1367 // For N0183 message, has the Mnemonic (id) changed?
1368 // Example: RMC and AIVDO from same source.
1369
1370 if (msg->bus == NavAddr::Bus::N0183) {
1371 auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
1372 if (msg_0183) {
1373 if (active_priority.active_identifier.size()) {
1374 if (debug_priority)
1375 printf("this_identifier: %s\n", this_identifier.c_str());
1376 if (debug_priority)
1377 printf("active_priority.active_identifier: %s\n",
1378 active_priority.active_identifier.c_str());
1379
1380 if (this_identifier.compare(active_priority.active_identifier) != 0) {
1381 // if necessary, auto adjust the priority of this message down
1382 // and drop it
1383 if (priority_map[this_key] == active_priority.active_priority) {
1384 int lowest_priority = -10; // safe enough
1385 for (auto it = priority_map.begin(); it != priority_map.end();
1386 it++) {
1387 if (it->second > lowest_priority) lowest_priority = it->second;
1388 }
1389
1390 priority_map[this_key] = lowest_priority + 1;
1391 if (debug_priority)
1392 printf(" Lowering priority B: %s :%d\n", source.c_str(),
1393 priority_map[this_key]);
1394 }
1395
1396 return false;
1397 }
1398 }
1399 }
1400 }
1401
1402 // Similar for n2k PGN...
1403
1404 else if (msg->bus == NavAddr::Bus::N2000) {
1405 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
1406 if (msg_n2k) {
1407 if (active_priority.active_identifier.size()) {
1408 if (this_identifier.compare(active_priority.active_identifier) != 0) {
1409 // if necessary, auto adjust the priority of this message down
1410 // and drop it
1411 if (priority_map[this_key] == active_priority.active_priority) {
1412 int lowest_priority = -10; // safe enough
1413 for (auto it = priority_map.begin(); it != priority_map.end();
1414 it++) {
1415 if (it->second > lowest_priority) lowest_priority = it->second;
1416 }
1417
1418 priority_map[this_key] = lowest_priority + 1;
1419 if (debug_priority)
1420 printf(" Lowering priority: %s :%d\n", source.c_str(),
1421 priority_map[this_key]);
1422 }
1423
1424 return false;
1425 }
1426 }
1427 }
1428 }
1429
1430 // Update the records
1431 active_priority.active_source = source;
1432 active_priority.active_identifier = this_identifier;
1433 active_priority.active_source_address = source_address;
1434 wxDateTime now = wxDateTime::Now();
1435 active_priority.recent_active_time = now.GetTicks();
1436 if (debug_priority)
1437 printf(" Accepting high priority: %s %d\n", source.c_str(), this_priority);
1438
1439 if (active_priority.pcclass == "position") {
1440 if (this_priority != m_last_position_priority) {
1441 m_last_position_priority = this_priority;
1442
1443 wxString msg;
1444 msg.Printf("GNSS position fix priority shift: %s\n %s \n -> %s",
1445 this_identifier.c_str(), m_last_position_source.c_str(),
1446 source.c_str());
1447 auto& noteman = NotificationManager::GetInstance();
1448 noteman.AddNotification(NotificationSeverity::kInformational,
1449 msg.ToStdString());
1450 }
1451 m_last_position_source = source;
1452 }
1453
1454 return true;
1455}
void Notify(const std::shared_ptr< const AppMsg > &msg)
Send message to everyone listening to given message type.
std::string key() const override
Return unique key used by observable to notify/listen.
std::string to_string() const override
Return printable string for logging etc without trailing nl.
bool HandleN0183_AIVDO(std::shared_ptr< const Nmea0183Msg > 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="")
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.
virtual bool IsVisible() const =0
Return true if log is visible i.
virtual void Add(const Logline &l)=0
Add an formatted string to log output.
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Custom event class for OpenCPN's notification system.
A parsed SignalK message over ipv4.
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
Hooks into gui available in model.
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:10