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