esp32-ogn-tracker/main/nmea.h

272 wiersze
10 KiB
C++

#ifndef __NMEA_H__
#define __NMEA_H__
#include <stdint.h>
uint8_t NMEA_Check(uint8_t *NMEA, uint8_t Len);
uint8_t NMEA_AppendCheck(uint8_t *NMEA, uint8_t Len);
inline uint8_t NMEA_AppendCheck(char *NMEA, uint8_t Len) { return NMEA_AppendCheck((uint8_t *)NMEA, Len); }
uint8_t NMEA_AppendCheckCRNL(uint8_t *NMEA, uint8_t Len);
inline uint8_t NMEA_AppendCheckCRNL(char *NMEA, uint8_t Len) { return NMEA_AppendCheckCRNL((uint8_t *)NMEA, Len); }
class NMEA_RxMsg // receiver for the NMEA sentences
{ public:
static const uint8_t MaxLen=120; // maximum length
static const uint8_t MaxParms=24; // maximum number of parameters (commas)
uint8_t Data[MaxLen]; // the message itself
uint8_t Len; // number of bytes
uint8_t Parms; // number of commas
uint8_t Parm[MaxParms]; // offset to each comma
uint8_t State; // bits: 0:loading, 1:complete, 2:locked,
uint8_t Check; // check sum: should be a XOR of all bytes between '$' and '*'
public:
void Clear(void) // Clear the frame: discard all data, ready for next message
{ State=0; Len=0; Parms=0; }
void Send(void (*SendByte)(char) ) const
{ for(uint8_t Idx=0; Idx<Len; Idx++)
{ (*SendByte)(Data[Idx]); }
(*SendByte)('\r'); (*SendByte)('\n'); }
void ProcessByte(uint8_t Byte) // pass all bytes through this call and it will build the frame
{
if(isComplete()) return; // if already a complete frame, ignore extra bytes
if(Len==0) // if data is empty
{ if(Byte!='$') return; // then ignore all bytes but '$'
Data[Len++]=Byte; // start storing the frame
setLoading(); Check=0x00; Parms=0; } // set state to "isLoading", clear checksum
/*
if(Byte=='$') // should '$' sign restart an ongoing NMEA sentence ?
{ Len=0; Data[Len++]=Byte;
setLoading(); Check=0x00; Parms=0; }
*/
else
{ if((Byte=='\r')||(Byte=='\n')) // if CR (or NL ?) then frame is complete
{ setComplete(); if(Len<MaxLen) Data[Len]=0;
return; }
else if(Byte<=' ') // other control bytes treat as errors
{ Clear(); return; } // and drop the frame
else if(Byte==',') // save comma positions to later get back to the fields
{ if(Parms<MaxParms) Parm[Parms++]=Len+1; } // save the position just after the comma
if(Len<MaxLen) { Data[Len++]=Byte; Check^=Byte; } // store data but if too much then treat as an error
else Clear(); // if too long, then drop the frame completely
}
return; }
uint8_t isLoading(void) const { return State &0x01; }
void setLoading(void) { State|=0x01; }
uint8_t isComplete(void) const { return State &0x02; }
void setComplete(void) { State|=0x02; }
uint8_t isLocked(void) const { return State&0x04; }
uint8_t isEmpty(void) const { return Len==0; }
uint8_t hasCheck(void) const
{ if(Len<4) return 0;
return Data[Len-3]=='*'; }
uint8_t isChecked(void) const // is the NMEA checksum OK ?
{ if(!hasCheck()) return 0;
uint8_t DataCheck = Check^Data[Len-3]^Data[Len-2]^Data[Len-1];
int8_t HighDigit=HexValue(Data[Len-2]); if(HighDigit<0) return 0;
int8_t LowDigit=HexValue(Data[Len-1]); if(LowDigit<0) return 0;
uint8_t FrameCheck = (HighDigit<<4) | LowDigit;
return DataCheck==FrameCheck; }
/*
static int8_t DecValue(uint8_t Char)
{ if(Char<'0') return -1;
if(Char<='9') return Char-'0';
return -1; }
*/
static int8_t HexValue(uint8_t Char)
{ if(Char<'0') return -1;
if(Char<='9') return Char-'0';
if(Char<'A') return -1;
if(Char<='F') return Char-('A'-10);
// if(Char<'a') return -1; // check-sum uses uppercase latters
// if(Char<='f') return Char-('a'-10);
return -1; }
const uint8_t *ParmPtr(uint8_t Field) const // get a pointer to given (comma separated) field
{ if(Field>=Parms) return 0;
return Data+Parm[Field]; }
uint8_t ParmLen(uint8_t Field) const
{ if(Field>=Parms) return 0;
if(Field==(Parms-1)) return Len-hasCheck()*3-Parm[Field];
return Parm[Field+1]-Parm[Field]-1; }
uint8_t isGP(void) const // GPS sentence ?
{ if(Data[1]!='G') return 0;
return Data[2]=='P'; }
uint8_t isGL(void) const // GLONASS sentence ?
{ if(Data[1]!='G') return 0;
return Data[2]=='L'; }
uint8_t isGA(void) const // GALILEO sentence ?
{ if(Data[1]!='G') return 0;
return Data[2]=='A'; }
uint8_t isGN(void) const
{ if(Data[1]!='G') return 0;
return Data[2]=='N'; }
uint8_t isBD(void) const // for Beidou GSA and GSV
{ if(Data[1]!='B') return 0;
return Data[2]=='D'; }
uint8_t isGx(void) const // GPS or GLONASS sentence ?
{ return Data[1]=='G'; }
uint8_t isGPRMC(void) const // GPS recomended minimum data
{ if(!isGP()) return 0;
if(Data[3]!='R') return 0;
if(Data[4]!='M') return 0;
return Data[5]=='C'; }
uint8_t isGNRMC(void) const // GPS recomended minimum data
{ if(!isGN()) return 0;
if(Data[3]!='R') return 0;
if(Data[4]!='M') return 0;
return Data[5]=='C'; }
uint8_t isGxRMC(void) const // GPS recomended minimum data
{ if(!isGx()) return 0;
if(Data[3]!='R') return 0;
if(Data[4]!='M') return 0;
return Data[5]=='C'; }
uint8_t isGPGGA(void) const // GPS 3-D fix data
{ if(!isGP()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='G') return 0;
return Data[5]=='A'; }
uint8_t isGNGGA(void) const // GPS 3-D fix data
{ if(!isGN()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='G') return 0;
return Data[5]=='A'; }
uint8_t isGxGGA(void) const //
{ if(!isGx()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='G') return 0;
return Data[5]=='A'; }
uint8_t isGxVTG(void) const // velocity relative to the ground
{ if(!isGx()) return 0;
if(Data[3]!='V') return 0;
if(Data[4]!='T') return 0;
return Data[5]=='G'; }
uint8_t isGxZDA(void) const // UTC time+date, time zone
{ if(!isGx()) return 0;
if(Data[3]!='Z') return 0;
if(Data[4]!='D') return 0;
return Data[5]=='A'; }
uint8_t isGPGSA(void) const // GPS satellite data
{ if(!isGP()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='A'; }
uint8_t isGNGSA(void) const //
{ if(!isGN()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='A'; }
uint8_t isGxGSA(void) const // GP=GPS, GA=Galileo, GL=Glonass, GB=BaiDou, GN=any of the systems combined
{ if(!isGx()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='A'; }
uint8_t isGxGSV(void) const // Satellite data
{ if(!isGx() && !isBD()) return 0; // we include as well $BDGSV
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGPGSV(void) const // GPS satellite data
{ if(!isGP()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGLGSV(void) const // GLONASS satellite data
{ if(!isGL()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGAGSV(void) const // GALILEO satellite data
{ if(!isGA()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isBDGSV(void) const // BEIDOU satellite data
{ if(!isBD()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGPTXT(void) const // GPS test message
{ if(!isGP()) return 0;
if(Data[3]!='T') return 0;
if(Data[4]!='X') return 0;
return Data[5]=='T'; }
uint8_t isP(void) const // Private thus specific to the euqipment
{ return Data[1]=='P'; }
uint8_t isPOGN(void) const // OGN dedicated NMEA sentence
{ if(Data[1]!='P') return 0;
if(Data[2]!='O') return 0;
if(Data[3]!='G') return 0;
return Data[4]=='N'; }
uint8_t isPOGNB(void) // barometric report from the OGN tracker
{ if(!isPOGN()) return 0;
return Data[5]=='B'; }
uint8_t isPOGNT(void) // other aircraft position (tracking) report from OGN trackers
{ if(!isPOGN()) return 0;
return Data[5]=='T'; }
uint8_t isPOGNS(void) // tracker parameters setup
{ if(!isPOGN()) return 0;
return Data[5]=='S'; }
uint8_t isPGRMZ(void) // barometric pressure report
{ if(!isP()) return 0;
if(Data[2]!='G') return 0;
if(Data[3]!='R') return 0;
if(Data[4]!='M') return 0;
return Data[5]=='Z'; }
uint8_t isPOGNL(void) // log file list request
{ if(!isPOGN()) return 0;
return Data[5]=='L'; }
uint8_t isPFLA(void) const // FLARM dedicated NMEA sentence
{ if(Data[1]!='P') return 0;
if(Data[2]!='F') return 0;
if(Data[3]!='L') return 0;
return Data[4]=='A'; }
uint8_t isPFLAC(void) // FLARM configuration NMEA
{ if(!isPFLA()) return 0;
return Data[5]=='C'; }
} ;
#endif // of __NMEA_H__