diff --git a/doc/Conversions.odt b/doc/Conversions.odt index 86e03ac..fc9afe7 100644 Binary files a/doc/Conversions.odt and b/doc/Conversions.odt differ diff --git a/lib/boatData/GwBoatData.cpp b/lib/boatData/GwBoatData.cpp index f3874a3..e05e0a4 100644 --- a/lib/boatData/GwBoatData.cpp +++ b/lib/boatData/GwBoatData.cpp @@ -58,7 +58,7 @@ uint32_t mtr2nm(uint32_t m) } double mtr2nm(double m) { - return m / 1852; + return m / 1852.0; } bool convertToJson(const GwSatInfoList &si,JsonVariant &variant){ diff --git a/lib/boatData/GwBoatData.h b/lib/boatData/GwBoatData.h index e76bfbe..f400241 100644 --- a/lib/boatData/GwBoatData.h +++ b/lib/boatData/GwBoatData.h @@ -16,6 +16,7 @@ class GwBoatItemBase{ GWSC(formatWind); GWSC(formatLatitude); GWSC(formatLongitude); + GWSC(formatXte); GWSC(formatFixed0); GWSC(formatDepth); GWSC(kelvinToC); @@ -239,7 +240,7 @@ class GwBoatData{ GWBOATDATA(double,DepthTransducer,4000,formatDepth) GWBOATDATA(double,SecondsSinceMidnight,4000,formatFixed0) GWBOATDATA(double,WaterTemperature,4000,kelvinToC) - GWBOATDATA(double,XTE,4000,formatFixed0) + GWBOATDATA(double,XTE,4000,formatXte) GWBOATDATA(double,DTW,4000,mtr2nm) GWBOATDATA(double,BTW,4000,formatCourse) GWBOATDATA(double,WPLatitude,4000,formatLatitude) diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index c5e2e58..145ecb3 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -128,6 +128,21 @@ private: } #define UD(item) updateDouble(boatData->item, item, msg.sourceId) #define UI(item) updateUint32(boatData->item, item, msg.sourceId) + tN2kXTEMode xteMode(const char modeChar){ + switch(modeChar){ + case 'D': + return N2kxtem_Differential; + case 'E': + return N2kxtem_Estimated; + case 'M': + return N2kxtem_Manual; + case 'S': + return N2kxtem_Simulator; + default: + break; + } + return N2kxtem_Autonomous; + } void convertRMB(const SNMEA0183Msg &msg) { LOG_DEBUG(GwLog::DEBUG + 1, "convert RMB"); @@ -141,23 +156,7 @@ private: tN2kXTEMode mode=N2kxtem_Autonomous; if (msg.FieldCount() > 13){ const char *modeChar=msg.Field(13); - switch(*modeChar){ - case 'D': - mode=N2kxtem_Differential; - break; - case 'E': - mode=N2kxtem_Estimated; - break; - case 'M': - mode=N2kxtem_Manual; - break; - case 'S': - mode=N2kxtem_Simulator; - break; - default: - break; - - } + mode=xteMode(*modeChar); } SetN2kXTE(n2kMsg,1,mode,false,rmb.xte); send(n2kMsg); @@ -725,6 +724,25 @@ private: SetN2kRateOfTurn(n2kMsg,1,ROT); send(n2kMsg); } + void convertXTE(const SNMEA0183Msg &msg){ + if (msg.FieldCount() < 6){ + LOG_DEBUG(GwLog::DEBUG,"unable to parse XTE %s",msg.line); + return; + } + if (msg.Field(0)[0] != 'A') return; + if (msg.Field(1)[0] != 'A') return; + if (msg.Field(4)[0] != 'N') return; //nm only + if (msg.FieldLen(2) < 1) return; + const char dir=msg.Field(3)[0]; + if (dir != 'L' && dir != 'R') return; + double xte=atof(msg.Field(2)) * nmTom; + if (dir == 'R') xte=-xte; + if (! updateDouble(boatData->XTE,xte,msg.sourceId)) return; + tN2kMsg n2kMsg; + tN2kXTEMode mode=xteMode(msg.Field(5)[0]); + SetN2kXTE(n2kMsg,1,mode,false,xte); + send(n2kMsg); + } //shortcut for lambda converters #define CVL [](const SNMEA0183Msg &msg, NMEA0183DataToN2KFunctions *p) -> void @@ -791,7 +809,10 @@ private: String(F("GLL")), &NMEA0183DataToN2KFunctions::convertGLL); converters.registerConverter( 127251UL, - String(F("ROT")), &NMEA0183DataToN2KFunctions::convertROT); + String(F("ROT")), &NMEA0183DataToN2KFunctions::convertROT); + converters.registerConverter( + 129283UL, + String(F("XTE")), &NMEA0183DataToN2KFunctions::convertXTE); unsigned long *aispgns=new unsigned long[7]{129810UL,129809UL,129040UL,129039UL,129802UL,129794UL,129038UL}; converters.registerConverter(7,&aispgns[0], String(F("AIVDM")),&NMEA0183DataToN2KFunctions::convertAIVDX); diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index 94403c7..31acd92 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -1030,6 +1030,50 @@ private: SendMessage(nmeamsg); } } + void HandleXTE(const tN2kMsg &msg){ + unsigned char SID=0; + tN2kXTEMode XTEMode; + bool NavigationTerminated=false; + double XTE=N2kDoubleNA; + if (! ParseN2kXTE(msg,SID,XTEMode,NavigationTerminated,XTE)){ + LOG_DEBUG(GwLog::DEBUG,"unable to parse PGN %d",msg.PGN); + return; + } + if (NavigationTerminated) return; + if (! updateDouble(boatData->XTE,XTE)) return; + tNMEA0183Msg nmeamsg; + if (!nmeamsg.Init("XTE",talkerId)) return; + const char *mode="A"; + switch(XTEMode){ + case N2kxtem_Differential: + mode="D"; + break; + case N2kxtem_Estimated: + mode="E"; + break; + case N2kxtem_Simulator: + mode="S"; + break; + case N2kxtem_Manual: + mode="M"; + break; + default: + break; + } + if (!nmeamsg.AddStrField("A")) return; + if (!nmeamsg.AddStrField("A")) return; + double val=mtr2nm(XTE); + const char *dir="L"; + if (val < 0) { + dir="R"; + val=-val; + } + if (! nmeamsg.AddDoubleField(val,1.0,"%.4f")) return; + if (! nmeamsg.AddStrField(dir)) return; + if (! nmeamsg.AddStrField("N")) return; + if (! nmeamsg.AddStrField(mode)) return; + SendMessage(nmeamsg); + } void registerConverters() @@ -1055,6 +1099,7 @@ private: converters.registerConverter(129539UL, &N2kToNMEA0183Functions::HandleDop); converters.registerConverter(129540UL, &N2kToNMEA0183Functions::HandleSats); converters.registerConverter(127251UL, &N2kToNMEA0183Functions::HandleROT); + converters.registerConverter(129283UL, &N2kToNMEA0183Functions::HandleXTE); #define HANDLE_AIS #ifdef HANDLE_AIS converters.registerConverter(129038UL, &N2kToNMEA0183Functions::HandleAISClassAPosReport); // AIS Class A Position Report, Message Type 1 diff --git a/web/index.js b/web/index.js index 9816c76..d83230c 100644 --- a/web/index.js +++ b/web/index.js @@ -548,6 +548,14 @@ let valueFormatters = { return x.toFixed(2); }, u:'°/s' + }, + formatXte:{ + f: function(v){ + let x=parseFloat(v); + if (isNaN(x)) return '---'; + return x.toFixed(0); + }, + u:'m' } } function createDashboardItem(name, def, parent) {