35#include <wx/tokenzr.h> 
   36#include <wx/fileconf.h> 
   53#define N_ACTIVE_LOG_WATCHDOG 300 
   57static bool debug_priority = 
false;
 
   71static NmeaLog* GetDataMonitor() {
 
   72  auto w = wxWindow::FindWindowByName(kDataMonitorWindowName);
 
   73  return dynamic_cast<NmeaLog*
>(w);
 
   78  log_callbacks.log_is_active = [&]() {
 
   79    auto log = GetDataMonitor();
 
   80    return log && log->IsVisible();
 
   82  log_callbacks.log_message = [&](
const Logline& ll) {
 
   83    NmeaLog* monitor = GetDataMonitor();
 
   91  AppNavMsg(
const std::shared_ptr<const AppMsg>& msg, 
const string& name)
 
   92      : 
NavMsg(NavAddr::Bus::AppMsg,
 
   93               std::make_shared<const NavAddrPlugin>(
"AppMsg")),
 
   94        m_to_string(msg->to_string()),
 
   97  [[nodiscard]] 
string to_string()
 const override { 
return m_to_string; }
 
   99  [[nodiscard]] 
string key()
 const override { 
return "appmsg::" + m_name; }
 
  101  const string m_to_string;
 
 
  105static void LogAppMsg(
const std::shared_ptr<const AppMsg>& msg,
 
  107  if (!log_cb.log_is_active()) 
return;
 
  108  auto navmsg = std::make_shared<AppNavMsg>(msg, 
"basic-navdata");
 
  111  log_cb.log_message(ll);
 
  118static void SendBasicNavdata(
int vflag,
 
  120  auto msg = std::make_shared<BasicNavDataMsg>(
 
  122  clock_gettime(CLOCK_MONOTONIC, &msg->set_time);
 
  123  LogAppMsg(msg, 
"basic-navdata", log_callbacks);
 
  124  AppMsgBus::GetInstance().
Notify(std::move(msg));
 
  127static inline double GeodesicRadToDeg(
double rads) {
 
  128  return rads * 180.0 / M_PI;
 
  131static inline double MS2KNOTS(
double ms) { 
return ms * 1.9438444924406; }
 
  133static string GetPriorityKey(
const NavMsgPtr& msg) {
 
  136  string this_identifier;
 
  137  string this_address(
"0");
 
  138  if (msg->bus == NavAddr::Bus::N0183) {
 
  139    auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
 
  141      string source = msg->source->to_string();
 
  142      this_identifier = msg_0183->talker;
 
  143      this_identifier += msg_0183->type;
 
  144      key = source + 
":" + this_address + 
";" + this_identifier;
 
  146  } 
else if (msg->bus == NavAddr::Bus::N2000) {
 
  147    auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
 
  149      this_identifier = msg_n2k->PGN.to_string();
 
  150      unsigned char n_source = msg_n2k->payload.at(7);
 
  151      wxString km = wxString::Format(
"N2k device address: %d", n_source);
 
  152      key = km.ToStdString() + 
" ; " + 
"PGN: " + this_identifier;
 
  154  } 
else if (msg->bus == NavAddr::Bus::Signalk) {
 
  155    auto msg_sk = std::dynamic_pointer_cast<const SignalkMsg>(msg);
 
  158          std::static_pointer_cast<const NavAddrSignalK>(msg->source);
 
  159      string source = addr_sk->to_string();
 
  168                                    const PriorityMap& priority_map) {
 
  172  for (
const auto& it : priority_map) {
 
  173    if (it.second == 0) key0 = it.first;
 
  176  wxString this_key(key0.c_str());
 
  177  wxStringTokenizer tkz(this_key, 
";");
 
  178  string source = tkz.GetNextToken().ToStdString();
 
  179  string this_identifier = tkz.GetNextToken().ToStdString();
 
  181  wxStringTokenizer tka(source, 
":");
 
  183  std::stringstream ss;
 
  184  ss << tka.GetNextToken();
 
  185  ss >> pc.active_source_address;
 
  186  pc.active_priority = 0;
 
  187  pc.active_source = source;
 
  188  pc.active_identifier = this_identifier;
 
  189  pc.recent_active_time = -1;
 
  192static void ApplyPriorityMap(PriorityMap& priority_map,
 
  193                             const wxString& new_prio, 
int category) {
 
  194  priority_map.clear();
 
  195  wxStringTokenizer tk(new_prio, 
"|");
 
  197  while (tk.HasMoreTokens()) {
 
  198    wxString entry = tk.GetNextToken();
 
  199    string s_entry(entry.c_str());
 
  200    priority_map[s_entry] = index;
 
  205static string GetPriorityMap(
const PriorityMap& map) {
 
  206#define MAX_SOURCES 10 
  207  string sa[MAX_SOURCES];
 
  210  for (
auto& it : map) {
 
  211    if ((it.second >= 0) && (it.second < MAX_SOURCES)) sa[it.second] = it.first;
 
  215  for (
int i = 0; i < MAX_SOURCES; i++) {
 
  216    if (!sa[i].empty()) {
 
  225static bool IsNextLowerPriorityAvailable(
const PriorityMap& map,
 
  228  for (
auto& it : map) {
 
  229    if (it.second > pc.active_priority) {
 
  230      best_prio = wxMin(best_prio, it.second);
 
  233  return best_prio != pc.active_priority;
 
  236static void SelectNextLowerPriority(
const PriorityMap& map,
 
  239  for (
const auto& it : map) {
 
  240    if (it.second > pc.active_priority) {
 
  241      best_prio = wxMin(best_prio, it.second);
 
  244  pc.active_priority = best_prio;
 
  245  pc.active_source.clear();
 
  246  pc.active_identifier.clear();
 
  251CommBridge::CommBridge()
 
  254      active_priority_position(
"position"),
 
  255      active_priority_velocity(
"velocity"),
 
  256      active_priority_heading(
"heading"),
 
  257      active_priority_variation(
"variation"),
 
  258      active_priority_satellites(
"satellites"),
 
  259      active_priority_void(
"", -1),
 
  260      m_n_log_watchdog_period(3600),
 
  261      m_last_position_priority(0) {
 
  262  Bind(wxEVT_TIMER, [&](wxTimerEvent&) { OnWatchdogTimer(); });
 
  264CommBridge::~CommBridge() = 
default;
 
  266bool CommBridge::Initialize() {
 
  267  m_log_callbacks = GetLogCallbacks();
 
  271  PresetPriorityContainers();
 
  276  m_watchdog_timer.SetOwner(
this, WATCHDOG_TIMER);
 
  277  m_watchdog_timer.Start(1000, wxTIMER_CONTINUOUS);
 
  282  m_driver_change_lstnr.
Init(
 
  283      CommDriverRegistry::GetInstance().evt_driverlist_change,
 
  284      [&](
const wxCommandEvent& ev) { OnDriverStateChange(); });
 
  289void CommBridge::PresetWatchdogs() {
 
  290  m_watchdogs.position_watchdog =
 
  292  m_watchdogs.velocity_watchdog = 20;
 
  293  m_watchdogs.variation_watchdog = 20;
 
  294  m_watchdogs.heading_watchdog = 20;
 
  295  m_watchdogs.satellite_watchdog = 20;
 
  298void CommBridge::OnWatchdogTimer() {
 
  300  m_watchdogs.position_watchdog--;
 
  301  if (m_watchdogs.position_watchdog <= 0) {
 
  302    if (m_watchdogs.position_watchdog % 5 == 0) {
 
  304      auto msg = std::make_shared<GPSWatchdogMsg>(
 
  305          GPSWatchdogMsg::WDSource::position, m_watchdogs.position_watchdog);
 
  306      auto& msgbus = AppMsgBus::GetInstance();
 
  307      LogAppMsg(msg, 
"watchdog", m_log_callbacks);
 
  308      msgbus.Notify(std::move(msg));
 
  310      if (m_watchdogs.position_watchdog % m_n_log_watchdog_period == 0) {
 
  312        logmsg.Printf(
"   ***GPS Watchdog timeout at Lat:%g   Lon: %g", 
gLat,
 
  314        wxLogMessage(logmsg);
 
  322    active_priority_position.recent_active_time = -1;
 
  326    if (IsNextLowerPriorityAvailable(priority_map_position,
 
  327                                     active_priority_position)) {
 
  328      SelectNextLowerPriority(priority_map_position, active_priority_position);
 
  333  m_watchdogs.velocity_watchdog--;
 
  334  if (m_watchdogs.velocity_watchdog <= 0) {
 
  337    active_priority_velocity.recent_active_time = -1;
 
  339    if (g_nNMEADebug && (m_watchdogs.velocity_watchdog == 0))
 
  340      wxLogMessage(
"   ***Velocity Watchdog timeout...");
 
  341    if (m_watchdogs.velocity_watchdog % 5 == 0) {
 
  343      auto msg = std::make_shared<GPSWatchdogMsg>(
 
  344          GPSWatchdogMsg::WDSource::velocity, m_watchdogs.velocity_watchdog);
 
  345      auto& msgbus = AppMsgBus::GetInstance();
 
  346      msgbus.Notify(std::move(msg));
 
  350    SelectNextLowerPriority(priority_map_velocity, active_priority_velocity);
 
  354  m_watchdogs.heading_watchdog--;
 
  355  if (m_watchdogs.heading_watchdog <= 0) {
 
  357    active_priority_heading.recent_active_time = -1;
 
  358    if (g_nNMEADebug && (m_watchdogs.heading_watchdog == 0))
 
  359      wxLogMessage(
"   ***HDT Watchdog timeout...");
 
  363    SelectNextLowerPriority(priority_map_heading, active_priority_heading);
 
  367  m_watchdogs.variation_watchdog--;
 
  368  if (m_watchdogs.variation_watchdog <= 0) {
 
  370    active_priority_variation.recent_active_time = -1;
 
  372    if (g_nNMEADebug && (m_watchdogs.variation_watchdog == 0))
 
  373      wxLogMessage(
"   ***VAR Watchdog timeout...");
 
  377    SelectNextLowerPriority(priority_map_variation, active_priority_variation);
 
  381  m_watchdogs.satellite_watchdog--;
 
  382  if (m_watchdogs.satellite_watchdog <= 0) {
 
  386    active_priority_satellites.recent_active_time = -1;
 
  388    if (g_nNMEADebug && (m_watchdogs.satellite_watchdog == 0))
 
  389      wxLogMessage(
"   ***SAT Watchdog timeout...");
 
  393    SelectNextLowerPriority(priority_map_satellites,
 
  394                            active_priority_satellites);
 
  398void CommBridge::MakeHDTFromHDM() {
 
  401  if (!std::isnan(
gHdm)) {
 
  404    if (std::isnan(
gVar) && (g_UserVar != 0.0)) 
gVar = g_UserVar;
 
  406    if (!std::isnan(
gHdt)) {
 
  409      else if (
gHdt >= 360)
 
  412      m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  417void CommBridge::InitCommListeners() {
 
  423                            HandleN2K_129029(UnpackEvtPointer<Nmea2000Msg>(ev));
 
  429                            HandleN2K_129025(UnpackEvtPointer<Nmea2000Msg>(ev));
 
  435                            HandleN2K_129026(UnpackEvtPointer<Nmea2000Msg>(ev));
 
  441                            HandleN2K_127250(UnpackEvtPointer<Nmea2000Msg>(ev));
 
  447                            HandleN2K_129540(UnpackEvtPointer<Nmea2000Msg>(ev));
 
  454    HandleN0183_RMC(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  459    HandleN0183_THS(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  464    HandleN0183_HDT(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  469    HandleN0183_HDG(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  474    HandleN0183_HDM(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  479    HandleN0183_VTG(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  484    HandleN0183_GSV(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  489    HandleN0183_GGA(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  495    HandleN0183_GLL(UnpackEvtPointer<Nmea0183Msg>(ev));
 
  505    HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
 
  509void CommBridge::OnDriverStateChange() {
 
  511  PresetPriorityContainers();
 
  514std::vector<string> CommBridge::GetPriorityMaps()
 const {
 
  515  std::vector<string> result;
 
  516  result.push_back(GetPriorityMap(priority_map_position));
 
  517  result.push_back(GetPriorityMap(priority_map_velocity));
 
  518  result.push_back(GetPriorityMap(priority_map_heading));
 
  519  result.push_back(GetPriorityMap(priority_map_variation));
 
  520  result.push_back(GetPriorityMap(priority_map_satellites));
 
  524void CommBridge::ApplyPriorityMaps(
const std::vector<string>& new_maps) {
 
  525  wxString new_prio_string = wxString(new_maps[0].c_str());
 
  526  ApplyPriorityMap(priority_map_position, new_prio_string, 0);
 
  528  new_prio_string = wxString(new_maps[1].c_str());
 
  529  ApplyPriorityMap(priority_map_velocity, new_prio_string, 1);
 
  531  new_prio_string = wxString(new_maps[2].c_str());
 
  532  ApplyPriorityMap(priority_map_heading, new_prio_string, 2);
 
  534  new_prio_string = wxString(new_maps[3].c_str());
 
  535  ApplyPriorityMap(priority_map_variation, new_prio_string, 3);
 
  537  new_prio_string = wxString(new_maps[4].c_str());
 
  538  ApplyPriorityMap(priority_map_satellites, new_prio_string, 4);
 
  541void CommBridge::PresetPriorityContainers() {
 
  542  PresetPriorityContainer(active_priority_position, priority_map_position);
 
  543  PresetPriorityContainer(active_priority_velocity, priority_map_velocity);
 
  544  PresetPriorityContainer(active_priority_heading, priority_map_heading);
 
  545  PresetPriorityContainer(active_priority_variation, priority_map_variation);
 
  546  PresetPriorityContainer(active_priority_satellites, priority_map_satellites);
 
  549bool CommBridge::HandleN2K_129029(
const N2000MsgPtr& n2k_msg) {
 
  550  std::vector<unsigned char> v = n2k_msg->payload;
 
  554  ClearNavData(temp_data);
 
  556  if (!m_decoder.DecodePGN129029(v, temp_data)) 
return false;
 
  559  if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
 
  560    if (EvalPriority(n2k_msg, active_priority_position,
 
  561                     priority_map_position)) {
 
  562      gLat = temp_data.gLat;
 
  563      gLon = temp_data.gLon;
 
  564      valid_flag += POS_UPDATE;
 
  565      valid_flag += POS_VALID;
 
  566      m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
  567      m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG;  
 
  571  if (temp_data.n_satellites >= 0) {
 
  572    if (EvalPriority(n2k_msg, active_priority_satellites,
 
  573                     priority_map_satellites)) {
 
  574      g_SatsInView = temp_data.n_satellites;
 
  576      m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
 
  580  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  584bool CommBridge::HandleN2K_129025(
const N2000MsgPtr& n2k_msg) {
 
  585  std::vector<unsigned char> v = n2k_msg->payload;
 
  588  ClearNavData(temp_data);
 
  590  if (!m_decoder.DecodePGN129025(v, temp_data)) 
return false;
 
  593  if (!N2kIsNA(temp_data.gLat) && !N2kIsNA(temp_data.gLon)) {
 
  594    if (EvalPriority(n2k_msg, active_priority_position,
 
  595                     priority_map_position)) {
 
  596      gLat = temp_data.gLat;
 
  597      gLon = temp_data.gLon;
 
  598      valid_flag += POS_UPDATE;
 
  599      valid_flag += POS_VALID;
 
  600      m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
  601      m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG;  
 
  608  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  612bool CommBridge::HandleN2K_129026(
const N2000MsgPtr& n2k_msg) {
 
  613  std::vector<unsigned char> v = n2k_msg->payload;
 
  616  ClearNavData(temp_data);
 
  618  if (!m_decoder.DecodePGN129026(v, temp_data)) 
return false;
 
  621  if (!N2kIsNA(temp_data.gSog)) {  
 
  622    if (EvalPriority(n2k_msg, active_priority_velocity,
 
  623                     priority_map_velocity)) {
 
  624      gSog = MS2KNOTS(temp_data.gSog);
 
  625      valid_flag += SOG_UPDATE;
 
  627      if (N2kIsNA(temp_data.gCog))
 
  630        gCog = GeodesicRadToDeg(temp_data.gCog);
 
  631      valid_flag += COG_UPDATE;
 
  632      m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
 
  637  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  641bool CommBridge::HandleN2K_127250(
const N2000MsgPtr& n2k_msg) {
 
  642  std::vector<unsigned char> v = n2k_msg->payload;
 
  645  ClearNavData(temp_data);
 
  647  if (!m_decoder.DecodePGN127250(v, temp_data)) 
return false;
 
  650  if (!N2kIsNA(temp_data.gVar)) {
 
  651    if (EvalPriority(n2k_msg, active_priority_variation,
 
  652                     priority_map_variation)) {
 
  653      gVar = GeodesicRadToDeg(temp_data.gVar);
 
  654      valid_flag += VAR_UPDATE;
 
  655      m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
 
  659  if (!N2kIsNA(temp_data.gHdt)) {
 
  660    if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
 
  661      gHdt = GeodesicRadToDeg(temp_data.gHdt);
 
  662      valid_flag += HDT_UPDATE;
 
  663      m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  667  if (!N2kIsNA(temp_data.gHdm)) {
 
  668    gHdm = GeodesicRadToDeg(temp_data.gHdm);
 
  669    if (EvalPriority(n2k_msg, active_priority_heading, priority_map_heading)) {
 
  671      valid_flag += HDT_UPDATE;
 
  672      if (!std::isnan(
gHdt))
 
  673        m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  677  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  681bool CommBridge::HandleN2K_129540(
const N2000MsgPtr& n2k_msg) {
 
  682  std::vector<unsigned char> v = n2k_msg->payload;
 
  685  ClearNavData(temp_data);
 
  687  if (!m_decoder.DecodePGN129540(v, temp_data)) 
return false;
 
  689  if (temp_data.n_satellites >= 0) {
 
  690    if (EvalPriority(n2k_msg, active_priority_satellites,
 
  691                     priority_map_satellites)) {
 
  692      g_SatsInView = temp_data.n_satellites;
 
  694      m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
 
  701bool CommBridge::HandleN0183_RMC(
const N0183MsgPtr& n0183_msg) {
 
  702  string str = n0183_msg->payload;
 
  705  ClearNavData(temp_data);
 
  707  bool is_valid = 
true;
 
  708  if (!m_decoder.DecodeRMC(str, temp_data)) is_valid = 
false;
 
  710  if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) 
return false;
 
  713  if (EvalPriority(n0183_msg, active_priority_position,
 
  714                   priority_map_position)) {
 
  716      gLat = temp_data.gLat;
 
  717      gLon = temp_data.gLon;
 
  718      valid_flag += POS_VALID;
 
  719      m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
  720      m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG;  
 
  722    valid_flag += POS_UPDATE;
 
  725  if (EvalPriority(n0183_msg, active_priority_velocity,
 
  726                   priority_map_velocity)) {
 
  728      gSog = temp_data.gSog;
 
  729      valid_flag += SOG_UPDATE;
 
  730      gCog = temp_data.gCog;
 
  731      valid_flag += COG_UPDATE;
 
  732      m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
 
  736  if (!std::isnan(temp_data.gVar)) {
 
  737    if (EvalPriority(n0183_msg, active_priority_variation,
 
  738                     priority_map_variation)) {
 
  740        gVar = temp_data.gVar;
 
  741        valid_flag += VAR_UPDATE;
 
  742        m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
 
  752  if ((valid_flag & POS_VALID) == POS_VALID) {
 
  754      if (g_SatsInView < 4) {
 
  756        m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
 
  761  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  765bool CommBridge::HandleN0183_THS(
const N0183MsgPtr& n0183_msg) {
 
  766  string str = n0183_msg->payload;
 
  768  ClearNavData(temp_data);
 
  770  if (!m_decoder.DecodeTHS(str, temp_data)) 
return false;
 
  773  if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
 
  774    gHdt = temp_data.gHdt;
 
  775    valid_flag += HDT_UPDATE;
 
  776    m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  779  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  783bool CommBridge::HandleN0183_HDT(
const N0183MsgPtr& n0183_msg) {
 
  784  string str = n0183_msg->payload;
 
  786  ClearNavData(temp_data);
 
  788  if (!m_decoder.DecodeHDT(str, temp_data)) 
return false;
 
  791  if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
 
  792    gHdt = temp_data.gHdt;
 
  793    valid_flag += HDT_UPDATE;
 
  794    m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  797  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  801bool CommBridge::HandleN0183_HDG(
const N0183MsgPtr& n0183_msg) {
 
  802  string str = n0183_msg->payload;
 
  804  ClearNavData(temp_data);
 
  806  if (!m_decoder.DecodeHDG(str, temp_data)) 
return false;
 
  811  if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
 
  812    gHdm = temp_data.gHdm;
 
  813    m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  817  if (!std::isnan(temp_data.gVar)) {
 
  818    if (EvalPriority(n0183_msg, active_priority_variation,
 
  819                     priority_map_variation)) {
 
  820      gVar = temp_data.gVar;
 
  821      valid_flag += VAR_UPDATE;
 
  822      m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
 
  826  if (bHDM) MakeHDTFromHDM();
 
  828  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  832bool CommBridge::HandleN0183_HDM(
const N0183MsgPtr& n0183_msg) {
 
  833  string str = n0183_msg->payload;
 
  835  ClearNavData(temp_data);
 
  837  if (!m_decoder.DecodeHDM(str, temp_data)) 
return false;
 
  840  if (EvalPriority(n0183_msg, active_priority_heading, priority_map_heading)) {
 
  841    gHdm = temp_data.gHdm;
 
  843    valid_flag += HDT_UPDATE;
 
  844    m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
  847  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  851bool CommBridge::HandleN0183_VTG(
const N0183MsgPtr& n0183_msg) {
 
  852  string str = n0183_msg->payload;
 
  854  ClearNavData(temp_data);
 
  856  if (!m_decoder.DecodeVTG(str, temp_data)) 
return false;
 
  859  if (EvalPriority(n0183_msg, active_priority_velocity,
 
  860                   priority_map_velocity)) {
 
  861    gSog = temp_data.gSog;
 
  862    valid_flag += SOG_UPDATE;
 
  863    gCog = temp_data.gCog;
 
  864    valid_flag += COG_UPDATE;
 
  865    m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
 
  868  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  872bool CommBridge::HandleN0183_GSV(
const N0183MsgPtr& n0183_msg) {
 
  873  string str = n0183_msg->payload;
 
  875  ClearNavData(temp_data);
 
  877  if (!m_decoder.DecodeGSV(str, temp_data)) 
return false;
 
  880  if (EvalPriority(n0183_msg, active_priority_satellites,
 
  881                   priority_map_satellites)) {
 
  882    if (temp_data.n_satellites >= 0) {
 
  883      g_SatsInView = temp_data.n_satellites;
 
  886      m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
 
  890  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  894bool CommBridge::HandleN0183_GGA(
const N0183MsgPtr& n0183_msg) {
 
  895  string str = n0183_msg->payload;
 
  897  ClearNavData(temp_data);
 
  899  bool is_valid = 
true;
 
  900  if (!m_decoder.DecodeGGA(str, temp_data)) is_valid = 
false;
 
  902  if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) 
return false;
 
  905  if (EvalPriority(n0183_msg, active_priority_position,
 
  906                   priority_map_position)) {
 
  908      gLat = temp_data.gLat;
 
  909      gLon = temp_data.gLon;
 
  910      valid_flag += POS_VALID;
 
  911      m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
  912      m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG;  
 
  914    valid_flag += POS_UPDATE;
 
  917  if (EvalPriority(n0183_msg, active_priority_satellites,
 
  918                   priority_map_satellites)) {
 
  920      if (temp_data.n_satellites >= 0) {
 
  921        g_SatsInView = temp_data.n_satellites;
 
  924        m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
 
  929  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  933bool CommBridge::HandleN0183_GLL(
const N0183MsgPtr& n0183_msg) {
 
  934  string str = n0183_msg->payload;
 
  936  ClearNavData(temp_data);
 
  939  if (!m_decoder.DecodeGLL(str, temp_data)) bvalid = 
false;
 
  941  if (std::isnan(temp_data.gLat) || std::isnan(temp_data.gLon)) 
return false;
 
  944  if (EvalPriority(n0183_msg, active_priority_position,
 
  945                   priority_map_position)) {
 
  947      gLat = temp_data.gLat;
 
  948      gLon = temp_data.gLon;
 
  949      valid_flag += POS_VALID;
 
  950      m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
  951      m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG;  
 
  953    valid_flag += POS_UPDATE;
 
  956  SendBasicNavdata(valid_flag, m_log_callbacks);
 
  961  string str = n0183_msg->payload;
 
  964  wxString sentence(str.c_str());
 
  966  AisError ais_error = AIS_GENERIC_ERROR;
 
  967  ais_error = DecodeSingleVDO(sentence, &gpd);
 
  969  if (ais_error == AIS_NoError) {
 
  971    if (!std::isnan(gpd.
kLat) && !std::isnan(gpd.
kLon)) {
 
  972      if (EvalPriority(n0183_msg, active_priority_position,
 
  973                       priority_map_position)) {
 
  976        valid_flag += POS_UPDATE;
 
  977        valid_flag += POS_VALID;
 
  978        m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
  979        m_n_log_watchdog_period =
 
  980            N_ACTIVE_LOG_WATCHDOG;  
 
  984    if (!std::isnan(gpd.
kCog) && !std::isnan(gpd.
kSog)) {
 
  985      if (EvalPriority(n0183_msg, active_priority_velocity,
 
  986                       priority_map_velocity)) {
 
  988        valid_flag += SOG_UPDATE;
 
  990        valid_flag += COG_UPDATE;
 
  991        m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
 
  995    if (!std::isnan(gpd.
kHdt)) {
 
  996      if (EvalPriority(n0183_msg, active_priority_heading,
 
  997                       priority_map_heading)) {
 
  999        valid_flag += HDT_UPDATE;
 
 1000        m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
 1004    SendBasicNavdata(valid_flag, m_log_callbacks);
 
 
 1009bool CommBridge::HandleSignalK(
const SignalKMsgPtr& sK_msg) {
 
 1010  string str = sK_msg->raw_message;
 
 1013  if (sK_msg->context_self != sK_msg->context) 
return false;
 
 1015  g_ownshipMMSI_SK = sK_msg->context_self;
 
 1018  ClearNavData(temp_data);
 
 1020  if (!m_decoder.DecodeSignalK(str, temp_data)) 
return false;
 
 1024  if (!std::isnan(temp_data.gLat) && !std::isnan(temp_data.gLon)) {
 
 1025    if (EvalPriority(sK_msg, active_priority_position, priority_map_position)) {
 
 1026      gLat = temp_data.gLat;
 
 1027      gLon = temp_data.gLon;
 
 1028      valid_flag += POS_UPDATE;
 
 1029      valid_flag += POS_VALID;
 
 1030      m_watchdogs.position_watchdog = gps_watchdog_timeout_ticks;
 
 1031      m_n_log_watchdog_period = N_ACTIVE_LOG_WATCHDOG;  
 
 1035  if (!std::isnan(temp_data.gSog)) {
 
 1036    if (EvalPriority(sK_msg, active_priority_velocity, priority_map_velocity)) {
 
 1037      gSog = temp_data.gSog;
 
 1038      valid_flag += SOG_UPDATE;
 
 1039      if ((
gSog > 0.05) && !std::isnan(temp_data.gCog)) {
 
 1040        gCog = temp_data.gCog;
 
 1041        valid_flag += COG_UPDATE;
 
 1043      m_watchdogs.velocity_watchdog = gps_watchdog_timeout_ticks;
 
 1047  if (!std::isnan(temp_data.gHdt)) {
 
 1048    if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
 
 1049      gHdt = temp_data.gHdt;
 
 1050      valid_flag += HDT_UPDATE;
 
 1051      m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
 1055  if (!std::isnan(temp_data.gHdm)) {
 
 1056    if (EvalPriority(sK_msg, active_priority_heading, priority_map_heading)) {
 
 1057      gHdm = temp_data.gHdm;
 
 1059      valid_flag += HDT_UPDATE;
 
 1060      m_watchdogs.heading_watchdog = gps_watchdog_timeout_ticks;
 
 1064  if (!std::isnan(temp_data.gVar)) {
 
 1065    if (EvalPriority(sK_msg, active_priority_variation,
 
 1066                     priority_map_variation)) {
 
 1067      gVar = temp_data.gVar;
 
 1068      valid_flag += VAR_UPDATE;
 
 1069      m_watchdogs.variation_watchdog = gps_watchdog_timeout_ticks;
 
 1073  if (temp_data.n_satellites > 0) {
 
 1074    if (EvalPriority(sK_msg, active_priority_satellites,
 
 1075                     priority_map_satellites)) {
 
 1076      g_SatsInView = temp_data.n_satellites;
 
 1078      m_watchdogs.satellite_watchdog = sat_watchdog_timeout_ticks;
 
 1082  if (g_pMUX && g_pMUX->IsLogActive())
 
 1085  SendBasicNavdata(valid_flag, m_log_callbacks);
 
 1089void CommBridge::ClearPriorityMaps() {
 
 1090  priority_map_position.clear();
 
 1091  priority_map_velocity.clear();
 
 1092  priority_map_heading.clear();
 
 1093  priority_map_variation.clear();
 
 1094  priority_map_satellites.clear();
 
 1098  if (category == 
"position")
 
 1099    return active_priority_position;
 
 1100  else if (category == 
"velocity")
 
 1101    return active_priority_velocity;
 
 1102  else if (category == 
"heading")
 
 1103    return active_priority_heading;
 
 1104  else if (category == 
"variation")
 
 1105    return active_priority_variation;
 
 1106  else if (category == 
"satellites")
 
 1107    return active_priority_satellites;
 
 1109    return active_priority_void;
 
 1112void CommBridge::UpdateAndApplyMaps(
const std::vector<string>& new_maps) {
 
 1113  ApplyPriorityMaps(new_maps);
 
 1115  PresetPriorityContainers();
 
 1118bool CommBridge::LoadConfig() {
 
 1119  if (TheBaseConfig()) {
 
 1120    TheBaseConfig()->SetPath(
"/Settings/CommPriority");
 
 1122    std::vector<string> new_maps;
 
 1123    wxString pri_string;
 
 1125    TheBaseConfig()->Read(
"PriorityPosition", &pri_string);
 
 1126    string s_prio = string(pri_string.c_str());
 
 1127    new_maps.push_back(s_prio);
 
 1129    TheBaseConfig()->Read(
"PriorityVelocity", &pri_string);
 
 1130    s_prio = string(pri_string.c_str());
 
 1131    new_maps.push_back(s_prio);
 
 1133    TheBaseConfig()->Read(
"PriorityHeading", &pri_string);
 
 1134    s_prio = string(pri_string.c_str());
 
 1135    new_maps.push_back(s_prio);
 
 1137    TheBaseConfig()->Read(
"PriorityVariation", &pri_string);
 
 1138    s_prio = string(pri_string.c_str());
 
 1139    new_maps.push_back(s_prio);
 
 1141    TheBaseConfig()->Read(
"PrioritySatellites", &pri_string);
 
 1142    s_prio = string(pri_string.c_str());
 
 1143    new_maps.push_back(s_prio);
 
 1145    ApplyPriorityMaps(new_maps);
 
 1150bool CommBridge::SaveConfig()
 const {
 
 1151  if (TheBaseConfig()) {
 
 1152    TheBaseConfig()->SetPath(
"/Settings/CommPriority");
 
 1154    wxString pri_string =
 
 1155        wxString(GetPriorityMap(priority_map_position).c_str());
 
 1156    TheBaseConfig()->Write(
"PriorityPosition", pri_string);
 
 1158    pri_string = wxString(GetPriorityMap(priority_map_velocity).c_str());
 
 1159    TheBaseConfig()->Write(
"PriorityVelocity", pri_string);
 
 1161    pri_string = wxString(GetPriorityMap(priority_map_heading).c_str());
 
 1162    TheBaseConfig()->Write(
"PriorityHeading", pri_string);
 
 1164    pri_string = wxString(GetPriorityMap(priority_map_variation).c_str());
 
 1165    TheBaseConfig()->Write(
"PriorityVariation", pri_string);
 
 1167    pri_string = wxString(GetPriorityMap(priority_map_satellites).c_str());
 
 1168    TheBaseConfig()->Write(
"PrioritySatellites", pri_string);
 
 1173bool CommBridge::EvalPriority(
const NavMsgPtr& msg,
 
 1175                              PriorityMap& priority_map) {
 
 1176  string this_key = GetPriorityKey(msg);
 
 1177  if (debug_priority) printf(
"This Key: %s\n", this_key.c_str());
 
 1180  wxStringTokenizer tkz(this_key, 
";");
 
 1181  wxString wxs_this_source = tkz.GetNextToken();
 
 1182  string source = wxs_this_source.ToStdString();
 
 1183  wxString wxs_this_identifier = tkz.GetNextToken();
 
 1184  string this_identifier = wxs_this_identifier.ToStdString();
 
 1186  wxStringTokenizer tka(wxs_this_source, 
":");
 
 1188  std::stringstream ss;
 
 1189  ss << tka.GetNextToken();
 
 1191  ss >> source_address;
 
 1199  if (msg->bus == NavAddr::Bus::N0183) {
 
 1200    if (!strncmp(active_priority.prio_class.c_str(), 
"velocity", 8)) {
 
 1201      bool pos_ok = 
false;
 
 1202      if (!strcmp(active_priority_position.active_source.c_str(),
 
 1204        if (active_priority_position.recent_active_time != -1) {
 
 1208      if (!pos_ok) 
return false;
 
 1215  auto it = priority_map.find(this_key);
 
 1216  if (it == priority_map.end()) {
 
 1218    size_t n = priority_map.size();
 
 1219    priority_map[this_key] = 
static_cast<int>(n);
 
 1222  this_priority = priority_map[this_key];
 
 1224  if (debug_priority) {
 
 1225    for (
const auto& jt : priority_map) {
 
 1226      printf(
"               priority_map:  %s  %d\n", jt.first.c_str(),
 
 1233  if (this_priority > active_priority.active_priority) {
 
 1235      printf(
"      Drop low priority: %s %d %d \n", source.c_str(),
 
 1236             this_priority, active_priority.active_priority);
 
 1241  if (this_priority < active_priority.active_priority) {
 
 1242    active_priority.active_priority = this_priority;
 
 1243    active_priority.active_source = source;
 
 1244    active_priority.active_identifier = this_identifier;
 
 1245    active_priority.active_source_address = source_address;
 
 1246    wxDateTime now = wxDateTime::Now();
 
 1247    active_priority.recent_active_time = now.GetTicks();
 
 1250      printf(
"  Restoring high priority: %s %d\n", source.c_str(),
 
 1258  if (!active_priority.active_source.empty()) {
 
 1259    if (debug_priority) printf(
"source: %s\n", source.c_str());
 
 1261      printf(
"active_source: %s\n", active_priority.active_source.c_str());
 
 1263    if (source != active_priority.active_source) {
 
 1266      int lowest_priority = -10;  
 
 1267      for (
const auto& jt : priority_map) {
 
 1268        if (jt.second > lowest_priority) lowest_priority = jt.second;
 
 1271      priority_map[this_key] = lowest_priority + 1;
 
 1273        printf(
"          Lowering priority A: %s :%d\n", source.c_str(),
 
 1274               priority_map[this_key]);
 
 1282  if (msg->bus == NavAddr::Bus::N0183) {
 
 1283    auto msg_0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(msg);
 
 1285      if (!active_priority.active_identifier.empty()) {
 
 1287          printf(
"this_identifier: %s\n", this_identifier.c_str());
 
 1289          printf(
"active_priority.active_identifier: %s\n",
 
 1290                 active_priority.active_identifier.c_str());
 
 1292        if (this_identifier != active_priority.active_identifier) {
 
 1295          if (priority_map[this_key] == active_priority.active_priority) {
 
 1296            int lowest_priority = -10;  
 
 1297            for (
const auto& jt : priority_map) {
 
 1298              if (jt.second > lowest_priority) lowest_priority = jt.second;
 
 1301            priority_map[this_key] = lowest_priority + 1;
 
 1303              printf(
"          Lowering priority B: %s :%d\n", source.c_str(),
 
 1304                     priority_map[this_key]);
 
 1315  else if (msg->bus == NavAddr::Bus::N2000) {
 
 1316    auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
 
 1318      if (!active_priority.active_identifier.empty()) {
 
 1319        if (this_identifier != active_priority.active_identifier) {
 
 1322          if (priority_map[this_key] == active_priority.active_priority) {
 
 1323            int lowest_priority = -10;  
 
 1324            for (
const auto& jt : priority_map) {
 
 1325              if (jt.second > lowest_priority) lowest_priority = jt.second;
 
 1328            priority_map[this_key] = lowest_priority + 1;
 
 1330              printf(
"          Lowering priority: %s :%d\n", source.c_str(),
 
 1331                     priority_map[this_key]);
 
 1341  active_priority.active_source = source;
 
 1342  active_priority.active_identifier = this_identifier;
 
 1343  active_priority.active_source_address = source_address;
 
 1344  wxDateTime now = wxDateTime::Now();
 
 1345  active_priority.recent_active_time = now.GetTicks();
 
 1347    printf(
"  Accepting high priority: %s %d\n", source.c_str(), this_priority);
 
 1349  if (active_priority.prio_class == 
"position") {
 
 1350    if (this_priority != m_last_position_priority) {
 
 1351      m_last_position_priority = this_priority;
 
 1354      msg_.Printf(_(
"GNSS position fix priority shift:") + 
" %s\n %s \n -> %s",
 
 1355                  this_identifier.c_str(), m_last_position_source.c_str(),
 
 1357      auto& noteman = NotificationManager::GetInstance();
 
 1358      noteman.AddNotification(NotificationSeverity::kInformational,
 
 1359                              msg_.ToStdString());
 
 1361    m_last_position_source = source;
 
void Notify(const std::shared_ptr< const AppMsg > &msg)
Send message to everyone listening to given message type.
string key() const override
Return unique key used by observable to notify/listen.
string to_string() const override
Return printable string for logging etc without trailing nl.
bool HandleN0183_AIVDO(const N0183MsgPtr &n0183_msg)
Processes NMEA 0183 AIVDO sentences containing own vessel's AIS data.
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString &error_msg="") const
Logs an input message with context information.
Actual data sent between application and transport layer.
Representation of message status as determined by the multiplexer.
A regular Nmea0183 message.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
virtual bool IsVisible() const =0
Return true if log is visible i.e., if it's any point using Add().
virtual void Add(const Logline &l)=0
Add a formatted string to log output.
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
Custom event class for OpenCPN's notification system.
A parsed SignalK message over ipv4.
Decoded messages send/receive support.
CommBridge class and helpers.
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
bool g_bSatValid
Indicates valid GNSS reception status based on satellite visibility and successful parsing of NMEA018...
Variables maintained by comm stack, read-only access for others.
Global variables stored in configuration file.
Extern C linked utilities.
Hooks into gui available in model.
GUI constant definitions.
Multiplexer class and helpers.
User notifications manager.
double gHdm
Magnetic heading in degrees (0-359.99).
double gVar
Magnetic variation in degrees.
double gHdt
True heading in degrees (0-359.99).
double gLat
Vessel's current latitude in decimal degrees.
double gCog
Course over ground in degrees (0-359.99).
double gSog
Speed over ground in knots.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
A generic position and navigation data structure.
double kCog
Course over ground in degrees.
double kHdt
True heading in degrees.
double kLat
Latitude in decimal degrees.
double kSog
Speed over ground in knots.
double kLon
Longitude in decimal degrees.