Merge pull request #76 from quantenschaum/75-true-wind

tried to fix wind conversion, disabled wind calculation
This commit is contained in:
Andreas Vogel 2024-09-29 16:46:19 +02:00 committed by GitHub
commit 358710ef03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 91 additions and 83 deletions

View File

@ -175,41 +175,42 @@ class GwBoatData{
GwBoatItemBase::GwBoatItemMap values; GwBoatItemBase::GwBoatItemMap values;
public: public:
GWBOATDATA(double,COG,4000,formatCourse) GWBOATDATA(double,COG,4000,formatCourse) // course over ground
GWBOATDATA(double,TWD,4000,formatCourse) GWBOATDATA(double,SOG,4000,formatKnots) // speed over ground
GWBOATDATA(double,SOG,4000,formatKnots) GWBOATDATA(double,HDT,4000,formatCourse) // true heading
GWBOATDATA(double,STW,4000,formatKnots) GWBOATDATA(double,HDM,4000,formatCourse) // magnetic heading
GWBOATDATA(double,TWS,4000,formatKnots) GWBOATDATA(double,STW,4000,formatKnots) // water speed
GWBOATDATA(double,AWS,4000,formatKnots) GWBOATDATA(double,VAR,4000,formatWind) // variation
GWBOATDATA(double,MaxTws,0,formatKnots) GWBOATDATA(double,DEV,4000,formatWind) // deviation
GWBOATDATA(double,AWA,4000,formatWind) // apparent wind ANGLE
GWBOATDATA(double,AWS,4000,formatKnots) // apparent wind speed
GWBOATDATA(double,MaxAws,0,formatKnots) GWBOATDATA(double,MaxAws,0,formatKnots)
GWBOATDATA(double,AWA,4000,formatWind) GWBOATDATA(double,TWD,4000,formatCourse) // true wind DIRECTION
GWBOATDATA(double,HDG,4000,formatCourse) //true heading GWBOATDATA(double,TWA,4000,formatWind) // true wind ANGLE
GWBOATDATA(double,MHDG,4000,formatCourse) //magnetic heading GWBOATDATA(double,TWS,4000,formatKnots) // true wind speed
GWBOATDATA(double,ROT,4000,formatRot) GWBOATDATA(double,MaxTws,0,formatKnots)
GWBOATDATA(double,VAR,4000,formatCourse) //Variation GWBOATDATA(double,ROT,4000,formatRot) // rate of turn
GWBOATDATA(double,DEV,4000,formatCourse) //Deviation GWBOATDATA(double,RPOS,4000,formatWind) // rudder position
GWBOATDATA(double,PRPOS,4000,formatWind) // secondary rudder position
GWBOATDATA(double,LAT,4000,formatLatitude)
GWBOATDATA(double,LON,4000,formatLongitude)
GWBOATDATA(double,ALT,4000,formatFixed0) //altitude
GWBOATDATA(double,HDOP,4000,formatDop) GWBOATDATA(double,HDOP,4000,formatDop)
GWBOATDATA(double,PDOP,4000,formatDop) GWBOATDATA(double,PDOP,4000,formatDop)
GWBOATDATA(double,VDOP,4000,formatDop) GWBOATDATA(double,VDOP,4000,formatDop)
GWBOATDATA(double,RPOS,4000,formatWind) //RudderPosition
GWBOATDATA(double,PRPOS,4000,formatWind) //second rudder pos
GWBOATDATA(double,LAT,4000,formatLatitude)
GWBOATDATA(double,LON,4000,formatLongitude)
GWBOATDATA(double,ALT,4000,formatFixed0) //altitude
GWBOATDATA(double,DBS,4000,formatDepth) //waterDepth (below surface) GWBOATDATA(double,DBS,4000,formatDepth) //waterDepth (below surface)
GWBOATDATA(double,DBT,4000,formatDepth) //DepthTransducer GWBOATDATA(double,DBT,4000,formatDepth) //DepthTransducer
GWBOATDATA(double,GPST,4000,formatTime) //GpsTime GWBOATDATA(double,GPST,4000,formatTime) // GPS time (seconds of day)
GWBOATDATA(uint32_t,GPSD,4000,formatDate) // GPS date (days since 1979-01-01)
GWBOATDATA(int16_t,TZ,8000,formatFixed0)
GWBOATDATA(double,WTemp,4000,kelvinToC) GWBOATDATA(double,WTemp,4000,kelvinToC)
GWBOATDATA(double,XTE,4000,formatXte)
GWBOATDATA(double,DTW,4000,mtr2nm) //distance wp
GWBOATDATA(double,BTW,4000,formatCourse) //bearing wp
GWBOATDATA(double,WPLat,4000,formatLatitude)
GWBOATDATA(double,WPLon,4000,formatLongitude)
GWBOATDATA(uint32_t,Log,16000,mtr2nm) GWBOATDATA(uint32_t,Log,16000,mtr2nm)
GWBOATDATA(uint32_t,TripLog,16000,mtr2nm) GWBOATDATA(uint32_t,TripLog,16000,mtr2nm)
GWBOATDATA(uint32_t,GPSD,4000,formatDate) //Date GWBOATDATA(double,DTW,4000,mtr2nm) // distance to waypoint
GWBOATDATA(int16_t,TZ,8000,formatFixed0) GWBOATDATA(double,BTW,4000,formatCourse) // bearing to waypoint
GWBOATDATA(double,XTE,4000,formatXte) // cross track error
GWBOATDATA(double,WPLat,4000,formatLatitude) // waypoint latitude
GWBOATDATA(double,WPLon,4000,formatLongitude) // waypoint longitude
GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::lifeTime,formatFixed0); GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::lifeTime,formatFixed0);
public: public:
GwBoatData(GwLog *logger); GwBoatData(GwLog *logger);

View File

@ -399,20 +399,20 @@ private:
return; return;
} }
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
tN2kWindReference n2kRef; tN2kWindReference n2kRef;
bool shouldSend=false; bool shouldSend=false;
WindAngle=formatDegToRad(WindAngle); WindAngle=formatDegToRad(WindAngle);
switch(Reference){ switch(Reference){
case NMEA0183Wind_Apparent: case NMEA0183Wind_Apparent:
n2kRef=N2kWind_Apparent; n2kRef=N2kWind_Apparent;
shouldSend=updateDouble(boatData->AWA,WindAngle,msg.sourceId) && shouldSend=updateDouble(boatData->AWA,WindAngle,msg.sourceId) &&
updateDouble(boatData->AWS,WindSpeed,msg.sourceId); updateDouble(boatData->AWS,WindSpeed,msg.sourceId);
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxAws->updateMax(WindSpeed); if (WindSpeed != NMEA0183DoubleNA) boatData->MaxAws->updateMax(WindSpeed);
break; break;
case NMEA0183Wind_True: case NMEA0183Wind_True:
n2kRef=N2kWind_True_North; n2kRef=N2kWind_True_water;
shouldSend=updateDouble(boatData->TWD,WindAngle,msg.sourceId) && shouldSend=updateDouble(boatData->TWA,WindAngle,msg.sourceId) &&
updateDouble(boatData->TWS,WindSpeed,msg.sourceId); updateDouble(boatData->TWS,WindSpeed,msg.sourceId);
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed); if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed);
break; break;
default: default:
@ -467,8 +467,7 @@ private:
void convertMWD(const SNMEA0183Msg &msg) void convertMWD(const SNMEA0183Msg &msg)
{ {
double WindAngle = NMEA0183DoubleNA, WindAngleMagnetic=NMEA0183DoubleNA, double WindDirection = NMEA0183DoubleNA, WindDirectionMagnetic=NMEA0183DoubleNA, WindSpeed = NMEA0183DoubleNA;
WindSpeed = NMEA0183DoubleNA;
if (msg.FieldCount() < 8 ) if (msg.FieldCount() < 8 )
{ {
LOG_DEBUG(GwLog::DEBUG, "failed to parse MWD %s", msg.line); LOG_DEBUG(GwLog::DEBUG, "failed to parse MWD %s", msg.line);
@ -476,11 +475,11 @@ private:
} }
if (msg.FieldLen(0) > 0 && msg.Field(1)[0] == 'T') if (msg.FieldLen(0) > 0 && msg.Field(1)[0] == 'T')
{ {
WindAngle = formatDegToRad(atof(msg.Field(0))); WindDirection = formatDegToRad(atof(msg.Field(0)));
} }
if (msg.FieldLen(2) > 0 && msg.Field(3)[0] == 'M') if (msg.FieldLen(2) > 0 && msg.Field(3)[0] == 'M')
{ {
WindAngleMagnetic = formatDegToRad(atof(msg.Field(2))); WindDirectionMagnetic = formatDegToRad(atof(msg.Field(2)));
} }
if (msg.FieldLen(4) > 0 && msg.Field(5)[0] == 'N') if (msg.FieldLen(4) > 0 && msg.Field(5)[0] == 'N')
{ {
@ -497,32 +496,30 @@ private:
} }
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
bool shouldSend = false; bool shouldSend = false;
if (WindAngle != NMEA0183DoubleNA){ if (WindDirection != NMEA0183DoubleNA){
shouldSend = updateDouble(boatData->TWD, WindAngle, msg.sourceId) && shouldSend = updateDouble(boatData->TWD, WindDirection, msg.sourceId) &&
updateDouble(boatData->TWS, WindSpeed, msg.sourceId); updateDouble(boatData->TWS, WindSpeed, msg.sourceId);
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed); if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed);
} if(shouldSend && boatData->HDT->isValid()) {
if (shouldSend) double twa = WindDirection-boatData->HDT->getData();
{ if(twa<0) { twa+=2*M_PI; }
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, WindAngle, N2kWind_True_North); updateDouble(boatData->TWA, twa, msg.sourceId);
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_True_North)); SetN2kWindSpeed(n2kMsg, 1, WindSpeed, twa, N2kWind_True_water);
} send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_True_water));
if (WindAngleMagnetic != NMEA0183DoubleNA && shouldSend){ }
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, WindAngleMagnetic, N2kWind_Magnetic);
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_Magnetic));
} }
} }
void convertHDM(const SNMEA0183Msg &msg){ void convertHDM(const SNMEA0183Msg &msg){
double MHDG=NMEA0183DoubleNA; double HDM=NMEA0183DoubleNA;
if (!NMEA0183ParseHDM_nc(msg, MHDG)) if (!NMEA0183ParseHDM_nc(msg, HDM))
{ {
LOG_DEBUG(GwLog::DEBUG, "failed to parse HDM %s", msg.line); LOG_DEBUG(GwLog::DEBUG, "failed to parse HDM %s", msg.line);
return; return;
} }
if (! UD(MHDG)) return; if (! UD(HDM)) return;
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
SetN2kMagneticHeading(n2kMsg,1,MHDG, SetN2kMagneticHeading(n2kMsg,1,HDM,
boatData->VAR->getDataWithDefault(N2kDoubleNA), boatData->VAR->getDataWithDefault(N2kDoubleNA),
boatData->DEV->getDataWithDefault(N2kDoubleNA) boatData->DEV->getDataWithDefault(N2kDoubleNA)
); );
@ -530,28 +527,29 @@ private:
} }
void convertHDT(const SNMEA0183Msg &msg){ void convertHDT(const SNMEA0183Msg &msg){
double HDG=NMEA0183DoubleNA; double HDT=NMEA0183DoubleNA;
if (!NMEA0183ParseHDT_nc(msg, HDG)) if (!NMEA0183ParseHDT_nc(msg, HDT))
{ {
LOG_DEBUG(GwLog::DEBUG, "failed to parse HDT %s", msg.line); LOG_DEBUG(GwLog::DEBUG, "failed to parse HDT %s", msg.line);
return; return;
} }
if (! UD(HDG)) return; if (! UD(HDT)) return;
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
SetN2kTrueHeading(n2kMsg,1,HDG); SetN2kTrueHeading(n2kMsg,1,HDT);
send(n2kMsg,msg.sourceId); send(n2kMsg,msg.sourceId);
} }
void convertHDG(const SNMEA0183Msg &msg){ void convertHDG(const SNMEA0183Msg &msg){
double MHDG=NMEA0183DoubleNA; double HDM=NMEA0183DoubleNA;
double VAR=NMEA0183DoubleNA;
double DEV=NMEA0183DoubleNA; double DEV=NMEA0183DoubleNA;
double VAR=NMEA0183DoubleNA;
if (msg.FieldCount() < 5) if (msg.FieldCount() < 5)
{ {
LOG_DEBUG(GwLog::DEBUG, "failed to parse HDG %s", msg.line); LOG_DEBUG(GwLog::DEBUG, "failed to parse HDG %s", msg.line);
return; return;
} }
if (msg.FieldLen(0)>0){ if (msg.FieldLen(0)>0){
MHDG=formatDegToRad(atof(msg.Field(0))); HDM=formatDegToRad(atof(msg.Field(0)));
} }
else{ else{
return; return;
@ -565,11 +563,11 @@ private:
if (msg.Field(4)[0] == 'W') VAR=-VAR; if (msg.Field(4)[0] == 'W') VAR=-VAR;
} }
if (! UD(MHDG)) return; if (! UD(HDM)) return;
UD(VAR); UD(VAR);
UD(DEV); UD(DEV);
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
SetN2kMagneticHeading(n2kMsg,1,MHDG,DEV,VAR); SetN2kMagneticHeading(n2kMsg,1,HDM,DEV,VAR);
send(n2kMsg,msg.sourceId,"127250M"); send(n2kMsg,msg.sourceId,"127250M");
} }
@ -705,11 +703,11 @@ private:
return; return;
} }
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
if (updateDouble(boatData->HDG,TrueHeading,msg.sourceId)){ if (updateDouble(boatData->HDT,TrueHeading,msg.sourceId)){
SetN2kTrueHeading(n2kMsg,1,TrueHeading); SetN2kTrueHeading(n2kMsg,1,TrueHeading);
send(n2kMsg,msg.sourceId); send(n2kMsg,msg.sourceId);
} }
if(updateDouble(boatData->MHDG,MagneticHeading,msg.sourceId)){ if(updateDouble(boatData->HDM,MagneticHeading,msg.sourceId)){
SetN2kMagneticHeading(n2kMsg,1,MagneticHeading, SetN2kMagneticHeading(n2kMsg,1,MagneticHeading,
boatData->DEV->getDataWithDefault(N2kDoubleNA), boatData->DEV->getDataWithDefault(N2kDoubleNA),
boatData->VAR->getDataWithDefault(N2kDoubleNA) boatData->VAR->getDataWithDefault(N2kDoubleNA)

View File

@ -185,13 +185,13 @@ private:
if (N2kIsNA(Variation)){ if (N2kIsNA(Variation)){
//no variation //no variation
if (ref == N2khr_magnetic){ if (ref == N2khr_magnetic){
updateDouble(boatData->MHDG,Heading); updateDouble(boatData->HDM,Heading);
if (NMEA0183SetHDM(NMEA0183Msg,Heading,talkerId)){ if (NMEA0183SetHDM(NMEA0183Msg,Heading,talkerId)){
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} }
} }
if (ref == N2khr_true){ if (ref == N2khr_true){
updateDouble(boatData->HDG,Heading); updateDouble(boatData->HDT,Heading);
if (NMEA0183SetHDT(NMEA0183Msg,Heading,talkerId)){ if (NMEA0183SetHDT(NMEA0183Msg,Heading,talkerId)){
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} }
@ -206,8 +206,8 @@ private:
if (ref == N2khr_true){ if (ref == N2khr_true){
MagneticHeading=Heading-Variation; MagneticHeading=Heading-Variation;
} }
updateDouble(boatData->MHDG,MagneticHeading); updateDouble(boatData->HDM,MagneticHeading);
updateDouble(boatData->HDG,Heading); updateDouble(boatData->HDT,Heading);
if (!N2kIsNA(MagneticHeading)){ if (!N2kIsNA(MagneticHeading)){
if (NMEA0183SetHDG(NMEA0183Msg, MagneticHeading,_Deviation, if (NMEA0183SetHDG(NMEA0183Msg, MagneticHeading,_Deviation,
Variation,talkerId)) Variation,talkerId))
@ -252,8 +252,8 @@ private:
tNMEA0183Msg NMEA0183Msg; tNMEA0183Msg NMEA0183Msg;
updateDouble(boatData->STW, WaterReferenced); updateDouble(boatData->STW, WaterReferenced);
unsigned long now = millis(); unsigned long now = millis();
double MagneticHeading = (boatData->HDG->isValid(now) && boatData->VAR->isValid(now)) ? boatData->HDG->getData() + boatData->VAR->getData() : NMEA0183DoubleNA; double MagneticHeading = (boatData->HDT->isValid(now) && boatData->VAR->isValid(now)) ? boatData->HDT->getData() + boatData->VAR->getData() : NMEA0183DoubleNA;
if (NMEA0183SetVHW(NMEA0183Msg, boatData->HDG->getDataWithDefault(NMEA0183DoubleNA), MagneticHeading, WaterReferenced,talkerId)) if (NMEA0183SetVHW(NMEA0183Msg, boatData->HDT->getDataWithDefault(NMEA0183DoubleNA), MagneticHeading, WaterReferenced,talkerId))
{ {
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} }
@ -468,37 +468,46 @@ private:
{ {
unsigned char SID; unsigned char SID;
tN2kWindReference WindReference; tN2kWindReference WindReference;
tNMEA0183WindReference NMEA0183Reference = NMEA0183Wind_True;
double x, y;
double WindAngle=N2kDoubleNA, WindSpeed=N2kDoubleNA; double WindAngle=N2kDoubleNA, WindSpeed=N2kDoubleNA;
if (ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference)) if (ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference)) {
{
tNMEA0183Msg NMEA0183Msg; tNMEA0183Msg NMEA0183Msg;
tNMEA0183WindReference NMEA0183Reference;
bool shouldSend = false;
if (WindReference == N2kWind_Apparent) // MWV sentence contains apparent/true ANGLE and SPEED
{ // https://gpsd.gitlab.io/gpsd/NMEA.html#_mwv_wind_speed_and_angle
// https://docs.vaisala.com/r/M211109EN-L/en-US/GUID-7402DEF8-5E82-446F-B63E-998F49F3D743/GUID-C77934C7-2A72-466E-BC52-CE6B8CC7ACB6
if (WindReference == N2kWind_Apparent) {
NMEA0183Reference = NMEA0183Wind_Apparent; NMEA0183Reference = NMEA0183Wind_Apparent;
updateDouble(boatData->AWA, WindAngle); updateDouble(boatData->AWA, WindAngle);
updateDouble(boatData->AWS, WindSpeed); updateDouble(boatData->AWS, WindSpeed);
setMax(boatData->MaxAws, boatData->AWS); setMax(boatData->MaxAws, boatData->AWS);
} shouldSend = true;
if (WindReference == N2kWind_True_North) }
{ if (WindReference == N2kWind_True_water) {
NMEA0183Reference = NMEA0183Wind_True; NMEA0183Reference = NMEA0183Wind_True;
updateDouble(boatData->TWD, WindAngle); updateDouble(boatData->TWA, WindAngle);
updateDouble(boatData->TWS, WindSpeed); updateDouble(boatData->TWS, WindSpeed);
setMax(boatData->MaxTws, boatData->TWS);
shouldSend = true;
if (boatData->HDT->isValid()) {
double twd = WindAngle+boatData->HDT->getData();
if (twd>2*M_PI) { twd-=2*M_PI; }
updateDouble(boatData->TWD, twd);
}
} }
if (NMEA0183SetMWV(NMEA0183Msg, formatCourse(WindAngle), NMEA0183Reference, WindSpeed,talkerId)) if (shouldSend && NMEA0183SetMWV(NMEA0183Msg, formatCourse(WindAngle), NMEA0183Reference, WindSpeed, talkerId)) {
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
}
if (WindReference == N2kWind_Apparent && boatData->SOG->isValid()) /* if (WindReference == N2kWind_Apparent && boatData->SOG->isValid())
{ // Lets calculate and send TWS/TWA if SOG is available { // Lets calculate and send TWS/TWA if SOG is available
x = WindSpeed * cos(WindAngle); double x = WindSpeed * cos(WindAngle);
y = WindSpeed * sin(WindAngle); double y = WindSpeed * sin(WindAngle);
updateDouble(boatData->TWD, atan2(y, -boatData->SOG->getData() + x)); updateDouble(boatData->TWD, atan2(y, -boatData->SOG->getData() + x));
updateDouble(boatData->TWS, sqrt((y * y) + ((-boatData->SOG->getData() + x) * (-boatData->SOG->getData() + x)))); updateDouble(boatData->TWS, sqrt((y * y) + ((-boatData->SOG->getData() + x) * (-boatData->SOG->getData() + x))));
@ -534,7 +543,7 @@ private:
return; return;
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} } */
} }
} }
//***************************************************************************** //*****************************************************************************