2018-01-29 12:43:22 +00:00
# ifndef __OGN_H__
# define __OGN_H__
# include <stdio.h>
# include <string.h>
# include <stdint.h>
# ifndef __AVR__
# include <time.h>
# endif
# include <math.h>
# include "intmath.h"
# include "bitcount.h"
# include "nmea.h"
# include "mavlink.h"
# include "ldpc.h"
# include "format.h"
/*
class OGN_SlowPacket // "slow packet" for transmitting position encoded in packet transmission times
{ public :
union
{ uint32_t Word [ 12 ] ; // OGN packet as 32-bit words
uint8_t Byte [ 45 ] ; // OGN packet as 8-bit bytes
struct // OGN packet as HeaderWord+Position+FEC
{ uint32_t Header ; // ECRR PMTT AAAA AAAA AAAA AAAA AAAA AAAA
// E=Emergency, C=enCrypt/Custom, RR=Relay count, P=Parity, M=isMeteo/Telemetry, TT=address Type, AA..=Address:24-bit
// When enCrypt/Custom is set the data (position or whatever) can only be decoded by the owner
// This option is indented to pass any type of custom data not foreseen otheriwse
uint32_t Data [ 4 ] ; // 0: QQTT TTTT LLLL LLLL LLLL LLLL LLLL LLLL QQ=fix Quality:2, TTTTTT=time:6, LL..=Latitude:20
// 1: MBDD DDDD LLLL LLLL LLLL LLLL LLLL LLLL F=fixMode:1 B=isBaro:1, DDDDDD=DOP:6, LL..=Longitude:20
// 2: RRRR RRRR SSSS SSSS SSAA AAAA AAAA AAAA RR..=turn Rate:8, SS..=Speed:10, AA..=Alt:14
// 3: BBBB BBBB YYYY PCCC CCCC CCDD DDDD DDDD BB..=Baro altitude:8, YYYY=AcftType:4, P=Stealth:1, CC..=Climb:9, DD..=Heading:10
uint32_t FEC [ 7 ] ; // Gallager code: 194 check bits for 160 user bits
} ;
} ;
*/
/*
class OGN_HardwareByte
{ public :
union
{ uint8_t Byte ;
struct
{ uint8_t Baro : 2 ; // 0=none, 1=BMP180, 2=BMP280, 3=MS5611
uint8_t RF : 2 ; // 0=RFM69, 1=RFM95, 2=CC1101, ...
} ;
} ;
} ;
class OGN_FirmwareByte
{ public :
union
{ uint8_t Byte ;
struct
{ uint8_t Revision : 3 ;
uint8_t Version : 3 ;
} ;
} ;
} ;
*/
// the packet description here is how it look on the little-endian CPU before sending it to the RF chip
// nRF905, CC1101, SPIRIT1, RFM69 chips actually reverse the bit order within every byte
// thus on the air the bits appear MSbit first for every byte transmitted
class OGN_Packet // Packet structure for the OGN tracker
{ public :
static const int Words = 5 ;
static const int Bytes = 20 ;
union
{ uint32_t HeaderWord ; // ECRR PMTT AAAA AAAA AAAA AAAA AAAA AAAA
// E=Emergency, C=enCrypt/Custom, RR=Relay count, P=Parity, M=isMeteo/Other, TT=address Type, AA..=Address:24-bit
// When enCrypt/Custom is set the data (position or whatever) can only be decoded by the owner
// This option is indented to pass any type of custom data not foreseen otheriwse
struct
{ unsigned int Address : 24 ; // aircraft address
unsigned int AddrType : 2 ; // address type: 0 = random, 1 = ICAO, 2 = FLARM, 3 = OGN
unsigned int Other : 1 ; // 0 = position packet, 1 = other information like status
unsigned int Parity : 1 ; // parity takes into account bits 0..27 thus only the 28 lowest bits
unsigned int RelayCount : 2 ; // 0 = direct packet, 1 = relayed once, 2 = relayed twice, ...
unsigned int Encrypted : 1 ; // packet is encrypted
unsigned int Emergency : 1 ; // aircraft in emergency (not used for now)
} Header ;
} ;
union
{ uint32_t Data [ 4 ] ; // 0: QQTT TTTT LLLL LLLL LLLL LLLL LLLL LLLL QQ=fix Quality:2, TTTTTT=time:6, LL..=Latitude:20
// 1: MBDD DDDD LLLL LLLL LLLL LLLL LLLL LLLL F=fixMode:1 B=isBaro:1, DDDDDD=DOP:6, LL..=Longitude:20
// 2: RRRR RRRR SSSS SSSS SSAA AAAA AAAA AAAA RR..=turn Rate:8, SS..=Speed:10, AA..=Alt:14
// 3: BBBB BBBB YYYY PCCC CCCC CCDD DDDD DDDD BB..=Baro altitude:8, YYYY=AcftType:4, P=Stealth:1, CC..=Climb:9, DD..=Heading:10
// meteo/telemetry types: Meteo conditions, Thermal wind/climb, Device status, Precise time,
// meteo report: Humidity, Barometric pressure, Temperature, wind Speed/Direction
// 2: HHHH HHHH SSSS SSSS SSAA AAAA AAAA AAAA
// 3: TTTT TTTT YYYY BBBB BBBB BBDD DDDD DDDD YYYY = report tYpe (meteo, thermal, water level, other telemetry)
// Device status: Time, baro pressure+temperature, GPS altitude, supply voltage, TX power, RF noise, software version, software features, hardware features,
// 0: UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UU..=Unix time
// 1: SSSS SSSS SSSS SSSS TTTT TTTT HHHH HHHH SS..=slot time, TT..=temperature, HH..=humidity
// 2: BBBB BBBB BBBB BBBB BBAA AAAA AAAA AAAA Baro pressure[0.5Pa], GPS altitude
// 3: VVVV VVVV YYYY HHHH HHHH XXXX VVVV VVVV VV..=firmware version, YYYY = report type, TT..=Temperatature, XX..=TxPower, VV..=battery voltage
// Pilot status:
// 0: NNNN NNNN NNNN NNNN NNNN NNNN NNNN NNNN Name: 9 char x 7bit or 10 x 6bit or Huffman encoding ?
// 1: NNNN NNNN NNNN NNNN NNNN NNNN NNNN NNNN
struct
{ signed int Latitude : 24 ; // // QQTT TTTT LLLL LLLL LLLL LLLL LLLL LLLL QQ=fix Quality:2, TTTTTT=time:6, LL..=Latitude:24
unsigned int Time : 6 ; // [sec] // time, just second thus ambiguity every every minute
unsigned int FixQuality : 2 ; // // 0 = none, 1 = GPS, 2 = Differential GPS (can be WAAS)
signed int Longitude : 24 ; // // MBDD DDDD LLLL LLLL LLLL LLLL LLLL LLLL F=fixMode:1 B=isBaro:1, DDDDDD=DOP:6, LL..=Longitude:24
unsigned int DOP : 6 ; // // GPS Dilution of Precision
unsigned int BaroMSB : 1 ; // // negated bit #8 of the altitude difference between baro and GPS
unsigned int FixMode : 1 ; // // 0 = 2-D, 1 = 3-D
unsigned int Altitude : 14 ; // [m] VR // RRRR RRRR SSSS SSSS SSAA AAAA AAAA AAAA RR..=turn Rate:8, SS..=Speed:10, AA..=Alt:14
unsigned int Speed : 10 ; // [0.1m/s] VR
unsigned int TurnRate : 8 ; // [0.1deg/s] VR
unsigned int Heading : 10 ; // [360/1024deg] // BBBB BBBB YYYY PCCC CCCC CCDD DDDD DDDD BB..=Baro altitude:8, YYYY=AcftType:4, P=Stealth:1, CC..=Climb:9, DD..=Heading:10
unsigned int ClimbRate : 9 ; // [0.1m/s] VR // rate of climb/decent from GPS or from baro sensor
unsigned int Stealth : 1 ; // // not really used till now
unsigned int AcftType : 4 ; // [0..15] // type of aircraft: 1 = glider, 2 = towplane, 3 = helicopter, ...
unsigned int BaroAltDiff : 8 ; // [m] // lower 8 bits of the altitude difference between baro and GPS
} Position ;
struct
{ unsigned int Pulse : 8 ; // [bpm] // pilot: heart pulse rate
unsigned int Oxygen : 7 ; // [%] // pilot: oxygen level in the blood
unsigned int FEScurr : 5 ; // [A] // FES current
unsigned int RxRate : 4 ; // [/min] // log2 of received packet rate
unsigned int Time : 6 ; // [sec] // same as in the position packet
unsigned int FixQuality : 2 ;
unsigned int AudioNoise : 8 ; // [dB] //
unsigned int RadioNoise : 8 ; // [dBm] // noise seen by the RF chip
unsigned int Temperature : 9 ; // [0.1degC] VR // temperature by the baro or RF chip
unsigned int Humidity : 7 ; // [%] // humidity
unsigned int Altitude : 14 ; // [m] VR // same as in the position packet
unsigned int Pressure : 14 ; // [0.08hPa] // barometric pressure
unsigned int Satellites : 4 ; // [ ]
unsigned int Firmware : 8 ; // [ ] // firmware version
unsigned int Hardware : 8 ; // [ ] // hardware version
unsigned int TxPower : 4 ; // [dBm] // RF trancmitter power
2018-02-25 18:47:03 +00:00
unsigned int ReportType : 4 ; // [0] // 0 for the status report
2018-01-29 12:43:22 +00:00
unsigned int Voltage : 8 ; // [1/64V] VR // supply/battery voltage
} Status ;
2018-02-25 18:47:03 +00:00
struct
{ uint8_t Data [ 14 ] ; // [16x7bit]packed string of 16-char: 7bit/char
unsigned int DataChars : 4 ; // [int] number of characters in the packed string
unsigned int ReportType : 4 ; // [1] // 1 for the Info packets
uint8_t Check ; // CRC check
} Info ;
struct
{ signed int Latitude : 24 ; // // Latitude of the measurement
unsigned int Time : 6 ; // [sec] // time, just second thus ambiguity every every minute
unsigned int : 2 ; // // spare
signed int Longitude : 24 ; // // Longitude of the measurement
unsigned int : 6 ; // // spare
unsigned int BaroMSB : 1 ; // // negated bit #8 of the altitude difference between baro and GPS
unsigned int : 1 ; // // spare
unsigned int Altitude : 14 ; // [m] VR // Altitude of the measurement
unsigned int Speed : 10 ; // [0.1m/s] VR // Horizontal wind speed
unsigned int : 8 ; // // spare
unsigned int Heading : 10 ; // // Wind direction
unsigned int ClimbRate : 9 ; // [0.1m/s] VR // Vertical wind speed
unsigned int : 1 ; // // spare
unsigned int ReportType : 4 ; // // 2 for wind/thermal report
unsigned int BaroAltDiff : 8 ; // [m] // lower 8 bits of the altitude difference between baro and GPS
} Wind ;
2018-01-29 12:43:22 +00:00
} ;
uint8_t * Byte ( void ) const { return ( uint8_t * ) & HeaderWord ; } // packet as bytes
uint32_t * Word ( void ) const { return ( uint32_t * ) & HeaderWord ; } // packet as words
// void recvBytes(const uint8_t *SrcPacket) { memcpy(Byte(), SrcPacket, Bytes); } // load data bytes e.g. from a demodulator
# ifdef __AVR__
# endif
# ifndef __AVR__
void Dump ( void ) const
{ printf ( " %08lX: %08lX %08lX %08lX %08lX \n " ,
( long int ) HeaderWord , ( long int ) Data [ 0 ] , ( long int ) Data [ 1 ] ,
( long int ) Data [ 2 ] , ( long int ) Data [ 3 ] ) ; }
void DumpBytes ( void ) const
{ for ( uint8_t Idx = 0 ; Idx < Bytes ; Idx + + )
{ printf ( " %02X " , Byte ( ) [ Idx ] ) ; }
printf ( " \n " ) ; }
int WriteDeviceStatus ( char * Out )
{ return sprintf ( Out , " h%02X v%02X %dsat/%d %ldm %3.1fhPa %+4.1fdegC %d%% %4.2fV %d/%+4.1fdBm %d/min " ,
Status . Hardware , Status . Firmware , Status . Satellites , Status . FixQuality , ( long int ) DecodeAltitude ( ) , 0.08 * Status . Pressure , 0.1 * DecodeTemperature ( ) , Status . Humidity ,
( 1.0 / 64 ) * DecodeVoltage ( ) , Status . TxPower + 4 , - 0.5 * Status . RadioNoise , ( 1 < < Status . RxRate ) - 1 ) ;
}
void Print ( void ) const
{ if ( ! Header . Other ) { PrintPosition ( ) ; return ; }
if ( Status . ReportType = = 0 ) { PrintDeviceStatus ( ) ; return ; }
}
void PrintDeviceStatus ( void ) const
{ printf ( " %c:%06lX R%c%c %02ds: " ,
' 0 ' + Header . AddrType , ( long int ) Header . Address , ' 0 ' + Header . RelayCount , Header . Emergency ? ' E ' : ' ' , Status . Time ) ;
printf ( " h%02X v%02X %dsat/%d %ldm %3.1fhPa %+4.1fdegC %d%% %4.2fV Tx:%ddBm Rx:%+4.1fdBm %d/min " ,
Status . Hardware , Status . Firmware , Status . Satellites , Status . FixQuality , ( long int ) DecodeAltitude ( ) , 0.08 * Status . Pressure , 0.1 * DecodeTemperature ( ) , Status . Humidity ,
( 1.0 / 64 ) * DecodeVoltage ( ) , Status . TxPower + 4 , - 0.5 * Status . RadioNoise , ( 1 < < Status . RxRate ) - 1 ) ;
printf ( " \n " ) ;
}
void PrintPosition ( void ) const
{ printf ( " %c%X:%c:%06lX R%c%c " ,
Position . Stealth ? ' s ' : ' ' , ( int ) Position . AcftType , ' 0 ' + Header . AddrType , ( long int ) Header . Address , ' 0 ' + Header . RelayCount ,
Header . Emergency ? ' E ' : ' ' ) ;
printf ( " %d/%dD/%4.1f " , ( int ) Position . FixQuality , ( int ) Position . FixMode + 2 , 0.1 * ( 10 + DecodeDOP ( ) ) ) ;
if ( Position . Time < 60 ) printf ( " %02ds: " , ( int ) Position . Time ) ;
else printf ( " ---: " ) ;
printf ( " [%+10.6f, %+11.6f]deg %ldm " ,
0.0001 / 60 * DecodeLatitude ( ) , 0.0001 / 60 * DecodeLongitude ( ) , ( long int ) DecodeAltitude ( ) ) ;
if ( hasBaro ( ) )
{ printf ( " [%+dm] " , ( int ) getBaroAltDiff ( ) ) ; }
printf ( " %3.1fm/s %05.1fdeg %+4.1fm/s %+4.1fdeg/s " ,
0.1 * DecodeSpeed ( ) , 0.1 * DecodeHeading ( ) , 0.1 * DecodeClimbRate ( ) , 0.1 * DecodeTurnRate ( ) ) ;
printf ( " \n " ) ;
}
2018-02-18 14:41:50 +00:00
void Encode ( MAV_ADSB_VEHICLE * MAV )
2018-05-19 01:03:18 +00:00
{ MAV - > ICAO_address = Header . Address ;
MAV - > lat = ( ( int64_t ) 50 * DecodeLatitude ( ) + 1 ) / 3 ; // convert coordinates to [1e-7deg]
MAV - > lon = ( ( int64_t ) 50 * DecodeLongitude ( ) + 1 ) / 3 ;
MAV - > altitude = 1000 * DecodeAltitude ( ) ; // convert to [mm[
MAV - > heading = 10 * DecodeHeading ( ) ; // [cdeg/s]
MAV - > hor_velocity = 10 * DecodeSpeed ( ) ; // [cm/s]
MAV - > ver_velocity = 10 * DecodeClimbRate ( ) ; // [cm/s]
MAV - > flags = 0x1F ; // all valid except for Squawk, not simulated
MAV - > altitude_type = 1 ; // GPS altitude
static char Prefix [ 4 ] = { ' R ' , ' I ' , ' F ' , ' O ' } ; // prefix for Random, ICAO, Flarm and OGN address-types
MAV - > callsign [ 0 ] = Prefix [ Header . AddrType ] ; // create a call-sign from address-type and address
Format_Hex ( ( char * ) MAV - > callsign + 1 , ( uint8_t ) ( Header . Address > > 16 ) ) ; // highest byte
Format_Hex ( ( char * ) MAV - > callsign + 3 , ( uint16_t ) ( Header . Address & 0xFFFF ) ) ; // two lower bytes
MAV - > callsign [ 7 ] = 0 ; // end-of-string for call-sign
MAV - > squawk = 0 ; // what shall we put there for OGN ?
MAV - > tslc = 1 ; // 1sec for now but should be more precise
MAV - > emiter_type = 0 ; // could be more precise
}
2018-01-29 12:43:22 +00:00
int8_t ReadAPRS ( const char * Msg ) // read an APRS position message
{ Clear ( ) ;
const char * Data = strchr ( Msg , ' : ' ) ; if ( Data = = 0 ) return - 1 ; // where the time/position data starts
Data + + ;
const char * Dest = strchr ( Msg , ' > ' ) ; if ( Dest = = 0 ) return - 1 ; // where the destination call is
Dest + + ;
const char * Comma = strchr ( Dest , ' , ' ) ; // the first comma after the destination call
Position . AcftType = 0xF ;
uint8_t AddrType ;
uint32_t Address ;
if ( memcmp ( Msg , " RND " , 3 ) = = 0 ) AddrType = 0 ;
else if ( memcmp ( Msg , " ICA " , 3 ) = = 0 ) AddrType = 1 ;
else if ( memcmp ( Msg , " FLR " , 3 ) = = 0 ) AddrType = 2 ;
else if ( memcmp ( Msg , " OGN " , 3 ) = = 0 ) AddrType = 3 ;
else AddrType = 4 ;
if ( AddrType < 4 )
{ if ( Read_Hex ( Address , Msg + 3 ) = = 6 ) Header . Address = Address ;
Header . AddrType = AddrType ; }
if ( Comma )
{ if ( memcmp ( Comma + 1 , " RELAY* " , 6 ) = = 0 ) Header . RelayCount = 1 ;
else if ( Comma [ 10 ] = = ' * ' ) Header . RelayCount = 1 ;
}
if ( Data [ 0 ] ! = ' / ' ) return - 1 ;
int8_t Time ;
if ( Data [ 7 ] = = ' h ' ) // HHMMSS UTC time
{ Time = Read_Dec2 ( Data + 5 ) ; if ( Time < 0 ) return - 1 ; }
else if ( Data [ 7 ] = = ' z ' ) // DDHHMM UTC time
{ Time = 0 ; }
else return - 1 ;
Position . Time = Time ;
Data + = 8 ;
Position . FixMode = 1 ;
Position . FixQuality = 1 ;
EncodeDOP ( 0xFF ) ;
int8_t LatDeg = Read_Dec2 ( Data ) ; if ( LatDeg < 0 ) return - 1 ;
int8_t LatMin = Read_Dec2 ( Data + 2 ) ; if ( LatMin < 0 ) return - 1 ;
if ( Data [ 4 ] ! = ' . ' ) return - 1 ;
int8_t LatFrac = Read_Dec2 ( Data + 5 ) ; if ( LatFrac < 0 ) return - 1 ;
int32_t Latitude = ( int32_t ) LatDeg * 600000 + ( int32_t ) LatMin * 10000 + ( int32_t ) LatFrac * 100 ;
char LatSign = Data [ 7 ] ;
Data + = 8 + 1 ;
int16_t LonDeg = Read_Dec3 ( Data ) ; if ( LonDeg < 0 ) return - 1 ;
int8_t LonMin = Read_Dec2 ( Data + 3 ) ; if ( LonMin < 0 ) return - 1 ;
if ( Data [ 5 ] ! = ' . ' ) return - 1 ;
int8_t LonFrac = Read_Dec2 ( Data + 6 ) ; if ( LonFrac < 0 ) return - 1 ;
int32_t Longitude = ( int32_t ) LonDeg * 600000 + ( int32_t ) LonMin * 10000 + ( int32_t ) LonFrac * 100 ;
char LonSign = Data [ 8 ] ;
Data + = 9 + 1 ;
int16_t Speed = 0 ;
int16_t Heading = 0 ;
if ( Data [ 3 ] = = ' / ' )
{ Heading = Read_Dec3 ( Data ) ;
Speed = Read_Dec3 ( Data + 4 ) ;
Data + = 7 ; }
EncodeHeading ( Heading * 10 ) ;
EncodeSpeed ( ( ( int32_t ) Speed * 337146 + 0x8000 ) > > 16 ) ;
uint32_t Altitude = 0 ;
if ( ( Data [ 0 ] = = ' / ' ) & & ( Data [ 1 ] = = ' A ' ) & & ( Data [ 2 ] = = ' = ' ) & & ( Read_UnsDec ( Altitude , Data + 3 ) = = 6 ) )
{ Altitude = ( 3 * Altitude + 5 ) / 10 ;
Data + = 9 ; }
EncodeAltitude ( Altitude ) ;
for ( ; ; )
{ if ( Data [ 0 ] ! = ' ' ) break ;
Data + + ;
if ( ( Data [ 0 ] = = ' ! ' ) & & ( Data [ 1 ] = = ' W ' ) & & ( Data [ 4 ] = = ' ! ' ) )
{ Latitude + = ( Data [ 2 ] - ' 0 ' ) * 10 ;
Longitude + = ( Data [ 3 ] - ' 0 ' ) * 10 ;
Data + = 5 ; continue ; }
if ( ( Data [ 0 ] = = ' i ' ) & & ( Data [ 1 ] = = ' d ' ) )
{ uint32_t ID ; Read_Hex ( ID , Data + 2 ) ;
Header . Address = ID & 0x00FFFFFF ;
Header . AddrType = ( ID > > 24 ) & 0x03 ;
Position . AcftType = ( ID > > 26 ) & 0x0F ;
Position . Stealth = ID > > 31 ;
Data + = 10 ; continue ; }
if ( ( Data [ 0 ] = = ' F ' ) & & ( Data [ 1 ] = = ' L ' ) & & ( Data [ 5 ] = = ' . ' ) )
{ int16_t FLdec = Read_Dec3 ( Data + 2 ) ;
int16_t FLfrac = Read_Dec2 ( Data + 6 ) ;
if ( ( FLdec > = 0 ) & & ( FLfrac > = 0 ) )
{ uint32_t StdAlt = FLdec * 100 + FLfrac ;
StdAlt = ( StdAlt * 3 + 5 ) / 10 ;
EncodeStdAltitude ( StdAlt ) ; }
Data + = 8 ; continue ; }
if ( ( Data [ 0 ] = = ' + ' ) | | ( Data [ 0 ] = = ' - ' ) )
{ int32_t Value ; int8_t Len = Read_Float1 ( Value , Data ) ;
if ( Len > 0 )
{ Data + = Len ;
if ( memcmp ( Data , " fpm " , 3 ) = = 0 ) { EncodeClimbRate ( Value / 200 ) ; Data + = 3 ; continue ; }
if ( memcmp ( Data , " rot " , 3 ) = = 0 ) { EncodeTurnRate ( 3 * Value ) ; Data + = 3 ; continue ; }
}
}
if ( ( Data [ 0 ] = = ' g ' ) & & ( Data [ 1 ] = = ' p ' ) & & ( Data [ 2 ] = = ' s ' ) )
{ int16_t HorPrec = Read_Dec2 ( Data + 3 ) ;
if ( HorPrec < 0 ) HorPrec = Read_Dec1 ( Data [ 3 ] ) ;
if ( HorPrec > = 0 )
{ uint16_t DOP = HorPrec * 5 ; if ( DOP < 10 ) DOP = 10 ; else if ( DOP > 230 ) DOP = 230 ;
EncodeDOP ( DOP - 10 ) ; Data + = 5 ; }
}
while ( Data [ 0 ] > ' ' ) Data + + ;
}
if ( LatSign = = ' S ' ) Latitude = ( - Latitude ) ; else if ( LatSign ! = ' N ' ) return - 1 ;
EncodeLatitude ( Latitude ) ;
if ( LonSign = = ' W ' ) Longitude = ( - Longitude ) ; else if ( LonSign ! = ' E ' ) return - 1 ;
EncodeLongitude ( Longitude ) ;
return 0 ; }
# endif // __AVR__
// calculate distance vector [LatDist, LonDist] from a given reference [RefLat, Reflon]
int calcDistanceVector ( int32_t & LatDist , int32_t & LonDist , int32_t RefLat , int32_t RefLon , uint16_t LatCos = 3000 , int32_t MaxDist = 0x7FFF )
{ LatDist = ( ( DecodeLatitude ( ) - RefLat ) * 1517 + 0x1000 ) > > 13 ; // convert from 1/600000deg to meters (40000000m = 360deg) => x 5/27 = 1517/(1<<13)
if ( abs ( LatDist ) > MaxDist ) return - 1 ;
LonDist = ( ( DecodeLongitude ( ) - RefLon ) * 1517 + 0x1000 ) > > 13 ;
if ( abs ( LonDist ) > ( 4 * MaxDist ) ) return - 1 ;
LonDist = ( LonDist * LatCos + 0x800 ) > > 12 ;
if ( abs ( LonDist ) > MaxDist ) return - 1 ;
return 1 ; }
// sets position [Lat, Lon] according to given distance vector [LatDist, LonDist] from a reference point [RefLat, RefLon]
void setDistanceVector ( int32_t LatDist , int32_t LonDist , int32_t RefLat , int32_t RefLon , uint16_t LatCos = 3000 )
{ EncodeLatitude ( RefLat + ( LatDist * 27 ) / 5 ) ;
LonDist = ( LonDist < < 12 ) / LatCos ; // LonDist/=cosine(Latitude)
EncodeLongitude ( RefLon + ( LonDist * 27 ) / 5 ) ; }
// Centripetal acceleration
static int16_t calcCPaccel ( int16_t Speed , int16_t TurnRate ) { return ( ( int32_t ) TurnRate * Speed * 229 + 0x10000 ) > > 17 ; } // [0.1m/s^2]
int16_t calcCPaccel ( void ) { return calcCPaccel ( DecodeSpeed ( ) , DecodeTurnRate ( ) ) ; }
// Turn radius
static int16_t calcTurnRadius ( int16_t Speed , int16_t TurnRate , int16_t MaxRadius = 0x7FFF ) // [m]
{ if ( TurnRate = = 0 ) return 0 ;
int32_t Radius = 14675 * Speed ;
Radius / = TurnRate ; Radius = ( Radius + 128 ) > > 8 ;
if ( abs ( Radius ) > MaxRadius ) return 0 ;
return Radius ; }
int16_t calcTurnRadius ( int16_t MaxRadius = 0x7FFF ) { return calcTurnRadius ( DecodeSpeed ( ) , DecodeTurnRate ( ) , MaxRadius ) ; }
// uint8_t WritePFLAA(char *NMEA, uint8_t Status, GPS_Position &Position)
// { return WritePFLAA(NMEA, uint8_t Status, Position.Latitude, Position.Longitude, (Position.Altitude+5)/10, Position.LatitudeCosine); }
// produce PFLAA sentence (relative position) from a reference point [RefLat, RefLon]
uint8_t WritePFLAA ( char * NMEA , uint8_t Status , int32_t RefLat , int32_t RefLon , int32_t RefAlt , uint16_t LatCos )
{ int32_t LatDist = 0 , LonDist = 0 ;
if ( calcDistanceVector ( LatDist , LonDist , RefLat , RefLon , LatCos ) < 0 ) return 0 ; // return zero, when distance too large
int32_t AltDist = DecodeAltitude ( ) - RefAlt ;
return WritePFLAA ( NMEA , Status , LatDist , LonDist , AltDist , Status ) ; } // return number of formatted characters
uint8_t WritePFLAA ( char * NMEA , uint8_t Status , int32_t LatDist , int32_t LonDist , int32_t AltDist )
{ uint8_t Len = 0 ;
Len + = Format_String ( NMEA + Len , " $PFLAA, " ) ; // sentence name and alarm-level (but no alarms for trackers)
NMEA [ Len + + ] = ' 0 ' + Status ;
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , LatDist ) ;
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , LonDist ) ;
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , AltDist ) ; // [m] relative altitude
NMEA [ Len + + ] = ' , ' ;
NMEA [ Len + + ] = ' 0 ' + Header . AddrType ; // address-type (3=OGN)
NMEA [ Len + + ] = ' , ' ;
uint32_t Addr = Header . Address ; // [24-bit] address
Len + = Format_Hex ( NMEA + Len , ( uint8_t ) ( Addr > > 16 ) ) ; // XXXXXX 24-bit address: RND, ICAO, FLARM, OGN
Len + = Format_Hex ( NMEA + Len , ( uint16_t ) Addr ) ;
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , DecodeHeading ( ) , 4 , 1 ) ; // [deg] heading (by GPS)
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , DecodeTurnRate ( ) , 2 , 1 ) ; // [deg/sec] turn rate
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , DecodeSpeed ( ) , 2 , 1 ) ; // [approx. m/s] ground speed
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , DecodeClimbRate ( ) , 2 , 1 ) ; // [m/s] climb/sink rate
NMEA [ Len + + ] = ' , ' ;
NMEA [ Len + + ] = HexDigit ( Position . AcftType ) ; // [0..F] aircraft-type: 1=glider, 2=tow plane, etc.
Len + = NMEA_AppendCheckCRNL ( NMEA , Len ) ;
NMEA [ Len ] = 0 ;
return Len ; } // return number of formatted characters
uint8_t Print ( char * Out ) const
{ uint8_t Len = 0 ;
Out [ Len + + ] = HexDigit ( Position . AcftType ) ; Out [ Len + + ] = ' : ' ;
Out [ Len + + ] = ' 0 ' + Header . AddrType ; Out [ Len + + ] = ' : ' ;
uint32_t Addr = Header . Address ;
Len + = Format_Hex ( Out + Len , ( uint8_t ) ( Addr > > 16 ) ) ;
Len + = Format_Hex ( Out + Len , ( uint16_t ) Addr ) ;
Out [ Len + + ] = ' ' ;
// Len+=Format_SignDec(Out+Len, -(int16_t)RxRSSI/2); Out[Len++]='d'; Out[Len++]='B'; Out[Len++]='m';
// Out[Len++]=' ';
Len + = Format_UnsDec ( Out + Len , ( uint16_t ) Position . Time , 2 ) ;
Out [ Len + + ] = ' ' ;
Len + = PrintLatitude ( Out + Len , DecodeLatitude ( ) ) ;
Out [ Len + + ] = ' ' ;
Len + = PrintLongitude ( Out + Len , DecodeLongitude ( ) ) ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , ( uint32_t ) DecodeAltitude ( ) ) ; Out [ Len + + ] = ' m ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , DecodeSpeed ( ) , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_SignDec ( Out + Len , DecodeClimbRate ( ) , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' \n ' ; Out [ Len ] = 0 ;
return Len ; }
static uint8_t PrintLatitude ( char * Out , int32_t Lat )
{ uint8_t Len = 0 ;
char Sign = ' N ' ;
if ( Lat < 0 ) { Sign = ' S ' ; Lat = ( - Lat ) ; }
uint32_t Deg = Lat / 600000 ;
Lat - = 600000 * Deg ;
Len + = Format_UnsDec ( Out + Len , Deg , 2 , 0 ) ;
Len + = Format_UnsDec ( Out + Len , Lat , 6 , 4 ) ;
Out [ Len + + ] = Sign ;
return Len ; }
static uint8_t PrintLongitude ( char * Out , int32_t Lon )
{ uint8_t Len = 0 ;
char Sign = ' E ' ;
if ( Lon < 0 ) { Sign = ' W ' ; Lon = ( - Lon ) ; }
uint32_t Deg = Lon / 600000 ;
Lon - = 600000 * Deg ;
Len + = Format_UnsDec ( Out + Len , Deg , 3 , 0 ) ;
Len + = Format_UnsDec ( Out + Len , Lon , 6 , 4 ) ;
Out [ Len + + ] = Sign ;
return Len ; }
// OGN_Packet() { Clear(); }
void Clear ( void ) { HeaderWord = 0 ; Data [ 0 ] = 0 ; Data [ 1 ] = 0 ; Data [ 2 ] = 0 ; Data [ 3 ] = 0 ; }
uint32_t getAddressAndType ( void ) const { return HeaderWord & 0x03FFFFFF ; } // Address with address-type: 26-bit
void setAddressAndType ( uint32_t AddrAndType ) { HeaderWord = ( HeaderWord & 0xFC000000 ) | ( AddrAndType & 0x03FFFFFF ) ; }
bool goodAddrParity ( void ) const { return ( ( Count1s ( HeaderWord & 0x0FFFFFFF ) & 1 ) = = 0 ) ; } // Address parity should be EVEN
void calcAddrParity ( void ) { if ( ! goodAddrParity ( ) ) HeaderWord ^ = 0x08000000 ; } // if not correct parity, flip the parity bit
bool hasBaro ( void ) const { return Position . BaroMSB | | Position . BaroAltDiff ; }
void clrBaro ( void ) { Position . BaroMSB = 0 ; Position . BaroAltDiff = 0 ; }
int16_t getBaroAltDiff ( void ) const { int16_t AltDiff = Position . BaroAltDiff ; if ( Position . BaroMSB = = 0 ) AltDiff | = 0xFF00 ; return AltDiff ; }
2018-02-25 18:47:03 +00:00
void setBaroAltDiff ( int32_t AltDiff )
2018-01-29 12:43:22 +00:00
{ if ( AltDiff < ( - 255 ) ) AltDiff = ( - 255 ) ; else if ( AltDiff > 255 ) AltDiff = 255 ;
Position . BaroMSB = ( AltDiff & 0xFF00 ) = = 0 ; Position . BaroAltDiff = AltDiff & 0xFF ; }
void EncodeStdAltitude ( int32_t StdAlt ) { setBaroAltDiff ( ( StdAlt - DecodeAltitude ( ) ) ) ; }
int32_t DecodeStdAltitude ( void ) const { return ( DecodeAltitude ( ) + getBaroAltDiff ( ) ) ; }
static uint16_t EncodeUR2V8 ( uint16_t Value ) // Encode unsigned 12bit (0..3832) as 10bit
{ if ( Value < 0x100 ) { }
else if ( Value < 0x300 ) Value = 0x100 | ( ( Value - 0x100 ) > > 1 ) ;
else if ( Value < 0x700 ) Value = 0x200 | ( ( Value - 0x300 ) > > 2 ) ;
else if ( Value < 0xF00 ) Value = 0x300 | ( ( Value - 0x700 ) > > 3 ) ;
else Value = 0x3FF ;
return Value ; }
static uint16_t DecodeUR2V8 ( uint16_t Value ) // Decode 10bit 0..0x3FF
{ uint16_t Range = Value > > 8 ;
Value & = 0x0FF ;
if ( Range = = 0 ) return Value ; // 000..0FF
if ( Range = = 1 ) return 0x101 + ( Value < < 1 ) ; // 100..2FE
if ( Range = = 2 ) return 0x302 + ( Value < < 2 ) ; // 300..6FC
return 0x704 + ( Value < < 3 ) ; } // 700..EF8 // in 12bit (0..3832)
static uint8_t EncodeUR2V5 ( uint16_t Value ) // Encode unsigned 9bit (0..472) as 7bit
{ if ( Value < 0x020 ) { }
else if ( Value < 0x060 ) Value = 0x020 | ( ( Value - 0x020 ) > > 1 ) ;
else if ( Value < 0x0E0 ) Value = 0x040 | ( ( Value - 0x060 ) > > 2 ) ;
else if ( Value < 0x1E0 ) Value = 0x060 | ( ( Value - 0x0E0 ) > > 3 ) ;
else Value = 0x07F ;
return Value ; }
static uint16_t DecodeUR2V5 ( uint16_t Value ) // Decode 7bit as unsigned 9bit (0..472)
{ uint8_t Range = ( Value > > 5 ) & 0x03 ;
Value & = 0x1F ;
if ( Range = = 0 ) { } // 000..01F
else if ( Range = = 1 ) { Value = 0x021 + ( Value < < 1 ) ; } // 020..05E
else if ( Range = = 2 ) { Value = 0x062 + ( Value < < 2 ) ; } // 060..0DC
else { Value = 0x0E4 + ( Value < < 3 ) ; } // 0E0..1D8 => max. Value = 472
return Value ; }
static uint8_t EncodeSR2V5 ( int16_t Value ) // Encode signed 10bit (-472..+472) as 8bit
{ uint8_t Sign = 0 ; if ( Value < 0 ) { Value = ( - Value ) ; Sign = 0x80 ; }
Value = EncodeUR2V5 ( Value ) ;
return Value | Sign ; }
static int16_t DecodeSR2V5 ( int16_t Value ) // Decode
{ int16_t Sign = Value & 0x80 ;
Value = DecodeUR2V5 ( Value & 0x7F ) ;
return Sign ? - Value : Value ; }
static uint16_t EncodeUR2V6 ( uint16_t Value ) // Encode unsigned 10bit (0..952) as 8 bit
{ if ( Value < 0x040 ) { }
else if ( Value < 0x0C0 ) Value = 0x040 | ( ( Value - 0x040 ) > > 1 ) ;
else if ( Value < 0x1C0 ) Value = 0x080 | ( ( Value - 0x0C0 ) > > 2 ) ;
else if ( Value < 0x3C0 ) Value = 0x0C0 | ( ( Value - 0x1C0 ) > > 3 ) ;
else Value = 0x0FF ;
return Value ; }
static uint16_t DecodeUR2V6 ( uint16_t Value ) // Decode 8bit as unsigned 10bit (0..952)
{ uint16_t Range = ( Value > > 6 ) & 0x03 ;
Value & = 0x3F ;
if ( Range = = 0 ) { } // 000..03F
else if ( Range = = 1 ) { Value = 0x041 + ( Value < < 1 ) ; } // 040..0BE
else if ( Range = = 2 ) { Value = 0x0C2 + ( Value < < 2 ) ; } // 0C0..1BC
else { Value = 0x1C4 + ( Value < < 3 ) ; } // 1C0..3B8 => max. Value = 952
return Value ; }
static uint16_t EncodeSR2V6 ( int16_t Value ) // Encode signed 11bit (-952..+952) as 9bit
{ uint16_t Sign = 0 ; if ( Value < 0 ) { Value = ( - Value ) ; Sign = 0x100 ; }
Value = EncodeUR2V6 ( Value ) ;
return Value | Sign ; }
static int16_t DecodeSR2V6 ( int16_t Value ) // Decode 9bit as signed 11bit (-952..+952)
{ int16_t Sign = Value & 0x100 ;
Value = DecodeUR2V6 ( Value & 0x00FF ) ;
return Sign ? - Value : Value ; }
void EncodeLatitude ( int32_t Latitude ) // encode Latitude: units are 0.0001/60 degrees
{ Position . Latitude = Latitude > > 3 ; }
int32_t DecodeLatitude ( void ) const
{ int32_t Latitude = Position . Latitude ;
// if(Latitude&0x00800000) Latitude|=0xFF000000;
Latitude = ( Latitude < < 3 ) + 4 ; return Latitude ; }
void EncodeLongitude ( int32_t Longitude ) // encode Longitude: units are 0.0001/60 degrees
{ Position . Longitude = Longitude > > = 4 ; }
int32_t DecodeLongitude ( void ) const
{ int32_t Longitude = Position . Longitude ;
// if(Longitude&0x00800000) Longitude|=0xFF000000;
Longitude = ( Longitude < < 4 ) + 8 ; return Longitude ; }
static uint16_t EncodeUR2V12 ( uint16_t Value ) // encode unsigned 16-bit (0..61432) as 14-bit
{ if ( Value < 0x1000 ) { }
else if ( Value < 0x3000 ) Value = 0x1000 | ( ( Value - 0x1000 ) > > 1 ) ;
else if ( Value < 0x7000 ) Value = 0x2000 | ( ( Value - 0x3000 ) > > 2 ) ;
else if ( Value < 0xF000 ) Value = 0x3000 | ( ( Value - 0x7000 ) > > 3 ) ;
else Value = 0x3FFF ;
return Value ; }
static uint16_t DecodeUR2V12 ( uint16_t Value )
{ uint16_t Range = Value > > 12 ;
Value & = 0x0FFF ;
if ( Range = = 0 ) return Value ; // 0000..0FFF
if ( Range = = 1 ) return 0x1001 + ( Value < < 1 ) ; // 1000..2FFE
if ( Range = = 2 ) return 0x3002 + ( Value < < 2 ) ; // 3000..6FFC
return 0x7004 + ( Value < < 3 ) ; } // 7000..EFF8 => max: 61432
void EncodeAltitude ( int32_t Altitude ) // encode altitude in meters
{ if ( Altitude < 0 ) Altitude = 0 ;
Position . Altitude = EncodeUR2V12 ( ( uint16_t ) Altitude ) ; }
int32_t DecodeAltitude ( void ) const // return Altitude in meters
{ return DecodeUR2V12 ( Position . Altitude ) ; }
void EncodeDOP ( uint8_t DOP )
2018-03-09 13:11:21 +00:00
{ // if(DOP<0) DOP=0;
if ( DOP < 0x10 ) { }
2018-01-29 12:43:22 +00:00
else if ( DOP < 0x30 ) DOP = 0x10 | ( ( DOP - 0x10 ) > > 1 ) ;
else if ( DOP < 0x70 ) DOP = 0x20 | ( ( DOP - 0x30 ) > > 2 ) ;
else if ( DOP < 0xF0 ) DOP = 0x30 | ( ( DOP - 0x70 ) > > 3 ) ;
else DOP = 0x3F ;
Position . DOP = DOP ; }
uint8_t DecodeDOP ( void ) const
{ uint8_t DOP = Position . DOP ;
uint8_t Range = DOP > > 4 ;
DOP & = 0x0F ;
if ( Range = = 0 ) return DOP ; // 00..0F
if ( Range = = 1 ) return 0x11 + ( DOP < < 1 ) ; // 10..2E
if ( Range = = 2 ) return 0x32 + ( DOP < < 2 ) ; // 30..6C
return 0x74 + ( DOP < < 3 ) ; } // 70..E8 => max. DOP = 232*0.1=23.2
void EncodeSpeed ( int16_t Speed ) // speed in 0.2 knots (or 0.1m/s)
{ if ( Speed < 0 ) Speed = 0 ;
else Speed = EncodeUR2V8 ( Speed ) ;
Position . Speed = Speed ; }
int16_t DecodeSpeed ( void ) const // return speed in 0.2 knots or 0.1m/s units
{ return DecodeUR2V8 ( Position . Speed ) ; } // => max. speed: 3832*0.2 = 766 knots
int16_t DecodeHeading ( void ) const // return Heading in 0.1 degree units 0..359.9 deg
{ int32_t Heading = Position . Heading ;
return ( Heading * 3600 + 512 ) > > 10 ; }
void EncodeHeading ( int16_t Heading )
{ Position . Heading = ( ( ( int32_t ) Heading < < 10 ) + 180 ) / 3600 ; }
void setHeadingAngle ( uint16_t HeadingAngle )
{ Position . Heading = ( ( ( HeadingAngle + 32 ) > > 6 ) ) ; }
uint16_t getHeadingAngle ( void ) const
{ return ( uint16_t ) Position . Heading < < 6 ; }
void EncodeTurnRate ( int16_t Turn ) // [0.1 deg/sec]
{ Position . TurnRate = EncodeSR2V5 ( Turn ) ; }
int16_t DecodeTurnRate ( void ) const
{ return DecodeSR2V5 ( Position . TurnRate ) ; }
void EncodeClimbRate ( int16_t Climb )
{ Position . ClimbRate = EncodeSR2V6 ( Climb ) ; }
int16_t DecodeClimbRate ( void ) const
{ return DecodeSR2V6 ( Position . ClimbRate ) ; }
// --------------------------------------------------------------------------------------------------------------
// Status fields
void EncodeTemperature ( int16_t Temp ) { Status . Temperature = EncodeSR2V5 ( Temp - 200 ) ; } // [0.1degC]
int16_t DecodeTemperature ( void ) const { return 200 + DecodeSR2V5 ( Status . Temperature ) ; }
void EncodeVoltage ( uint16_t Voltage ) { Status . Voltage = EncodeUR2V6 ( Voltage ) ; } // [1/64V]
uint16_t DecodeVoltage ( void ) const { return DecodeUR2V6 ( Status . Voltage ) ; }
// --------------------------------------------------------------------------------------------------------------
2018-02-25 18:47:03 +00:00
// Info fields: pack and unpack 7-bit char into the Info packets
void setInfoChar ( uint8_t Char , uint8_t Idx ) // put 7-bit Char onto give position
{ if ( Idx > = 16 ) return ;
Char & = 0x7F ;
uint8_t BitIdx = Idx * 7 ; // [bits] bit index to the target field
Idx = BitIdx > > 3 ; // [bytes] index of the first byte to change
uint8_t Ofs = BitIdx & 0x07 ;
if ( Ofs = = 0 ) { Info . Data [ Idx ] = ( Info . Data [ Idx ] & 0x80 ) | Char ; return ; }
if ( Ofs = = 1 ) { Info . Data [ Idx ] = ( Info . Data [ Idx ] & 0x01 ) | ( Char < < 1 ) ; return ; }
uint8_t Len1 = 8 - Ofs ;
uint8_t Len2 = Ofs - 1 ;
uint8_t Msk1 = 0xFF ; Msk1 < < = Ofs ;
uint8_t Msk2 = 0x01 ; Msk2 = ( Msk2 < < Len2 ) - 1 ;
Info . Data [ Idx ] = ( Info . Data [ Idx ] & ( ~ Msk1 ) ) | ( Char < < Ofs ) ;
Info . Data [ Idx + 1 ] = ( Info . Data [ Idx + 1 ] & ( ~ Msk2 ) ) | ( Char > > Len1 ) ; }
uint8_t getInfoChar ( uint8_t Idx ) // get 7-bit Char from given position
{ if ( Idx > = 16 ) return 0 ;
uint8_t BitIdx = Idx * 7 ; // [bits] bit index to the target field
Idx = BitIdx > > 3 ; // [bytes] index of the first byte to change
uint8_t Ofs = BitIdx & 0x07 ;
if ( Ofs = = 0 ) return Info . Data [ Idx ] & 0x7F ;
if ( Ofs = = 1 ) return Info . Data [ Idx ] > > 1 ;
uint8_t Len = 8 - Ofs ;
return ( Info . Data [ Idx ] > > Ofs ) | ( ( Info . Data [ Idx + 1 ] < < Len ) & 0x7F ) ; }
// --------------------------------------------------------------------------------------------------------------
2018-01-29 12:43:22 +00:00
// void Whiten (void) { TEA_Encrypt(Position, OGN_WhitenKey, 4); TEA_Encrypt(Position+2, OGN_WhitenKey, 4); } // whiten the position
// void Dewhiten(void) { TEA_Decrypt(Position, OGN_WhitenKey, 4); TEA_Decrypt(Position+2, OGN_WhitenKey, 4); } // de-whiten the position
void Whiten ( void ) { TEA_Encrypt_Key0 ( Data , 8 ) ; TEA_Encrypt_Key0 ( Data + 2 , 8 ) ; } // whiten the position
void Dewhiten ( void ) { TEA_Decrypt_Key0 ( Data , 8 ) ; TEA_Decrypt_Key0 ( Data + 2 , 8 ) ; } // de-whiten the position
static void TEA_Encrypt ( uint32_t * Data , const uint32_t * Key , int Loops = 4 )
{ uint32_t v0 = Data [ 0 ] , v1 = Data [ 1 ] ; // set up
const uint32_t delta = 0x9e3779b9 ; uint32_t sum = 0 ; // a key schedule constant
uint32_t k0 = Key [ 0 ] , k1 = Key [ 1 ] , k2 = Key [ 2 ] , k3 = Key [ 3 ] ; // cache key
for ( int i = 0 ; i < Loops ; i + + ) // basic cycle start
{ sum + = delta ;
v0 + = ( ( v1 < < 4 ) + k0 ) ^ ( v1 + sum ) ^ ( ( v1 > > 5 ) + k1 ) ;
v1 + = ( ( v0 < < 4 ) + k2 ) ^ ( v0 + sum ) ^ ( ( v0 > > 5 ) + k3 ) ; } // end cycle
Data [ 0 ] = v0 ; Data [ 1 ] = v1 ;
}
static void TEA_Decrypt ( uint32_t * Data , const uint32_t * Key , int Loops = 4 )
{ uint32_t v0 = Data [ 0 ] , v1 = Data [ 1 ] ; // set up
const uint32_t delta = 0x9e3779b9 ; uint32_t sum = delta * Loops ; // a key schedule constant
uint32_t k0 = Key [ 0 ] , k1 = Key [ 1 ] , k2 = Key [ 2 ] , k3 = Key [ 3 ] ; // cache key
for ( int i = 0 ; i < Loops ; i + + ) // basic cycle start */
{ v1 - = ( ( v0 < < 4 ) + k2 ) ^ ( v0 + sum ) ^ ( ( v0 > > 5 ) + k3 ) ;
v0 - = ( ( v1 < < 4 ) + k0 ) ^ ( v1 + sum ) ^ ( ( v1 > > 5 ) + k1 ) ;
sum - = delta ; } // end cycle
Data [ 0 ] = v0 ; Data [ 1 ] = v1 ;
}
static void TEA_Encrypt_Key0 ( uint32_t * Data , int Loops = 4 )
{ uint32_t v0 = Data [ 0 ] , v1 = Data [ 1 ] ; // set up
const uint32_t delta = 0x9e3779b9 ; uint32_t sum = 0 ; // a key schedule constant
for ( int i = 0 ; i < Loops ; i + + ) // basic cycle start
{ sum + = delta ;
v0 + = ( v1 < < 4 ) ^ ( v1 + sum ) ^ ( v1 > > 5 ) ;
v1 + = ( v0 < < 4 ) ^ ( v0 + sum ) ^ ( v0 > > 5 ) ; } // end cycle
Data [ 0 ] = v0 ; Data [ 1 ] = v1 ;
}
static void TEA_Decrypt_Key0 ( uint32_t * Data , int Loops = 4 )
{ uint32_t v0 = Data [ 0 ] , v1 = Data [ 1 ] ; // set up
const uint32_t delta = 0x9e3779b9 ; uint32_t sum = delta * Loops ; // a key schedule constant
for ( int i = 0 ; i < Loops ; i + + ) // basic cycle start */
{ v1 - = ( v0 < < 4 ) ^ ( v0 + sum ) ^ ( v0 > > 5 ) ;
v0 - = ( v1 < < 4 ) ^ ( v1 + sum ) ^ ( v1 > > 5 ) ;
sum - = delta ; } // end cycle
Data [ 0 ] = v0 ; Data [ 1 ] = v1 ;
}
static uint8_t Gray ( uint8_t Binary ) { return Binary ^ ( Binary > > 1 ) ; }
static uint8_t Binary ( uint8_t Gray )
{ Gray = Gray ^ ( Gray > > 4 ) ;
Gray = Gray ^ ( Gray > > 2 ) ;
Gray = Gray ^ ( Gray > > 1 ) ;
return Gray ; }
uint8_t getTxSlot ( uint8_t Idx ) const // Idx=0..15
{ const uint32_t * DataPtr = Data ;
uint32_t Mask = 1 ; Mask < < = Idx ;
uint8_t Slot = 0 ;
for ( uint8_t Bit = 0 ; Bit < 6 ; Bit + + )
{ Slot > > = 1 ;
if ( DataPtr [ Bit ] & Mask ) Slot | = 0x20 ;
Mask < < = 1 ; Slot > > = 1 ; }
return Gray ( Slot ) ; }
} ;
// ---------------------------------------------------------------------------------------------------------------------
class OGN_TxPacket // OGN packet with FEC code, like for transmission
{ public :
static const int Words = 7 ;
static const int Bytes = 26 ;
OGN_Packet Packet ; // OGN packet
uint32_t FEC [ 2 ] ; // Gallager code: 48 check bits for 160 user bits
public :
uint8_t Print ( char * Out )
{ uint8_t Len = 0 ;
Out [ Len + + ] = HexDigit ( Packet . Position . AcftType ) ; Out [ Len + + ] = ' : ' ;
Out [ Len + + ] = ' 0 ' + Packet . Header . AddrType ; Out [ Len + + ] = ' : ' ;
uint32_t Addr = Packet . Header . Address ;
Len + = Format_Hex ( Out + Len , ( uint8_t ) ( Addr > > 16 ) ) ;
Len + = Format_Hex ( Out + Len , ( uint16_t ) Addr ) ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , ( uint16_t ) Packet . Position . Time , 2 ) ;
Out [ Len + + ] = ' ' ;
Len + = Packet . PrintLatitude ( Out + Len , Packet . DecodeLatitude ( ) ) ;
Out [ Len + + ] = ' ' ;
Len + = Packet . PrintLongitude ( Out + Len , Packet . DecodeLongitude ( ) ) ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , ( uint32_t ) Packet . DecodeAltitude ( ) ) ; Out [ Len + + ] = ' m ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , Packet . DecodeSpeed ( ) , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_SignDec ( Out + Len , Packet . DecodeClimbRate ( ) , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' \n ' ; Out [ Len ] = 0 ;
return Len ; }
void Dump ( void ) const
{ printf ( " %08lX: %08lX %08lX %08lX %08lX [%08lX %04lX] (%d) \n " ,
( long int ) Packet . HeaderWord , ( long int ) Packet . Data [ 0 ] , ( long int ) Packet . Data [ 1 ] ,
( long int ) Packet . Data [ 2 ] , ( long int ) Packet . Data [ 3 ] , ( long int ) FEC [ 0 ] ,
( long int ) FEC [ 1 ] , ( int ) checkFEC ( ) ) ; }
void DumpBytes ( void ) const
{ for ( uint8_t Idx = 0 ; Idx < Bytes ; Idx + + )
{ printf ( " %02X " , Packet . Byte ( ) [ Idx ] ) ; }
printf ( " \n " ) ; }
// void calcFEC(void) { LDPC_Encode(&Packet.HeaderWord, FEC); } // calculate the 48-bit parity check
// void calcFEC(const uint32_t ParityGen[48][5]) { LDPC_Encode(&PacketHeaderWord, FEC, ParityGen); }
void calcFEC ( void ) { LDPC_Encode ( Packet . Word ( ) ) ; } // calculate the 48-bit parity check
uint8_t checkFEC ( void ) const { return LDPC_Check ( Packet . Word ( ) ) ; } // returns number of parity checks that fail (0 => no errors, all fine)
uint8_t * Byte ( void ) const { return ( uint8_t * ) & Packet . HeaderWord ; } // packet as bytes
uint32_t * Word ( void ) const { return ( uint32_t * ) & Packet . HeaderWord ; } // packet as words
void recvBytes ( const uint8_t * SrcPacket ) { memcpy ( Byte ( ) , SrcPacket , Bytes ) ; } // load data bytes e.g. from a demodulator
/*
uint8_t calcErrorPattern ( uint8_t * ErrPatt , const uint8_t * OtherPacket ) const
{ uint8_t ByteIdx = 0 ; const uint32_t * WordPtr = Packet . Word ( ) ;
for ( uint8_t WordIdx = 0 ; WordIdx < Words ; WordIdx + + )
{ uint32_t Word = WordPtr [ WordIdx ] ;
for ( int Idx = 0 ; Idx < 4 ; Idx + + )
{ if ( ByteIdx > = Bytes ) break ;
ErrPatt [ ByteIdx ] = Packet [ ByteIdx ] ^ Word ; ByteIdx + + ;
Word > > = 8 ; }
}
return Bytes ; }
*/
} ;
// ---------------------------------------------------------------------------------------------------------------------
class OGN_RxPacket // OGN packet with FEC code and some reception info
{ public :
static const int Words = 7 ;
static const int Bytes = 26 ;
OGN_Packet Packet ;
uint32_t FEC [ 2 ] ; // Gallager code: 48 check bits for 160 user bits
union
{ uint8_t State ; //
struct
{ bool : 1 ; //
bool Ready : 1 ; // is ready for transmission
bool Sent : 1 ; // has already been transmitted out
bool Corr : 1 ; // correctly received or corrected by FEC
uint8_t RxErr : 4 ; // number of bit errors corrected upon reception
} ;
} ;
uint8_t RxChan ; // RF channel where the packet was received
uint8_t RxRSSI ; // [-0.5dBm]
uint8_t Rank ; // rank: low altitude and weak signal => high rank
public :
OGN_RxPacket ( ) { Clear ( ) ; }
void Clear ( void ) { Packet . Clear ( ) ; State = 0 ; Rank = 0 ; }
uint8_t * Byte ( void ) const { return ( uint8_t * ) & Packet . HeaderWord ; } // packet as bytes
uint32_t * Word ( void ) const { return ( uint32_t * ) & Packet . HeaderWord ; } // packet as words
void recvBytes ( const uint8_t * SrcPacket ) { memcpy ( Byte ( ) , SrcPacket , Bytes ) ; } // load data bytes e.g. from a demodulator
uint8_t calcErrorPattern ( uint8_t * ErrPatt , const uint8_t * OtherPacket ) const
{ uint8_t ByteIdx = 0 ; const uint32_t * WordPtr = Packet . Word ( ) ;
for ( uint8_t WordIdx = 0 ; WordIdx < Words ; WordIdx + + )
{ uint32_t Word = WordPtr [ WordIdx ] ;
for ( int Idx = 0 ; Idx < 4 ; Idx + + )
{ if ( ByteIdx > = Bytes ) break ;
ErrPatt [ ByteIdx ] = OtherPacket [ ByteIdx ] ^ Word ; ByteIdx + + ;
Word > > = 8 ; }
}
return Bytes ; }
// void calcFEC(void) { LDPC_Encode(&Packet.HeaderWord, FEC); } // calculate the 48-bit parity check
// void calcFEC(const uint32_t ParityGen[48][5]) { LDPC_Encode(&PacketHeaderWord, FEC, ParityGen); }
void calcFEC ( void ) { LDPC_Encode ( Packet . Word ( ) ) ; } // calculate the 48-bit parity check
uint8_t checkFEC ( void ) const { return LDPC_Check ( Packet . Word ( ) ) ; } // returns number of parity checks that fail (0 => no errors, all fine)
int BitErr ( OGN_RxPacket & RefPacket ) const // return number of different data bits between this Packet and RefPacket
{ return Count1s ( Packet . HeaderWord ^ RefPacket . Packet . HeaderWord )
+ Count1s ( Packet . Data [ 0 ] ^ RefPacket . Packet . Data [ 0 ] )
+ Count1s ( Packet . Data [ 1 ] ^ RefPacket . Packet . Data [ 1 ] )
+ Count1s ( Packet . Data [ 2 ] ^ RefPacket . Packet . Data [ 2 ] )
+ Count1s ( Packet . Data [ 3 ] ^ RefPacket . Packet . Data [ 3 ] )
+ Count1s ( FEC [ 0 ] ^ RefPacket . FEC [ 0 ] )
+ Count1s ( ( FEC [ 1 ] ^ RefPacket . FEC [ 1 ] ) & 0xFFFF ) ; }
void calcRelayRank ( int32_t RxAltitude ) // [0.1m] altitude of reception
{ if ( Packet . Header . Emergency ) { Rank = 0xFF ; return ; } // emergency packets always highest rank
Rank = 0 ;
if ( Packet . Header . Other ) return ; // only relay position packets
if ( Packet . Position . Time > = 60 ) return ; // don't relay packets with unknown time - but maybe we should ?
if ( Packet . Header . RelayCount > 0 ) return ; // no rank for relayed packets (only single relay)
if ( RxRSSI > 128 ) // [-0.5dB] weaker signal => higher rank
Rank + = ( RxRSSI - 128 ) > > 2 ; // 1point/2dB less signal
RxAltitude - = 10 * Packet . DecodeAltitude ( ) ; // [0.1m] lower altitude => higher rank
if ( RxAltitude > 0 )
Rank + = RxAltitude > > 9 ; // 2points/100m of altitude below
int16_t ClimbRate = Packet . DecodeClimbRate ( ) ; // [0.1m/s] higher sink rate => higher rank
if ( ClimbRate < 0 )
Rank + = ( - ClimbRate ) > > 3 ; // 1point/0.8m/s of sink
}
uint8_t ReadPOGNT ( const char * NMEA )
{ uint8_t Len = 0 ;
if ( memcmp ( NMEA , " $POGNT, " , 7 ) ! = 0 ) return - 1 ;
Len + = 7 ;
if ( NMEA [ Len + 2 ] ! = ' , ' ) return - 1 ;
int8_t Time = Read_Dec2 ( NMEA + Len ) ;
if ( ( Time < 0 ) | | ( Time > = 60 ) ) return - 1 ;
Packet . Position . Time = Time ;
Len + = 3 ;
if ( NMEA [ Len + 1 ] ! = ' , ' ) return - 1 ;
int8_t AcftType = Read_Hex1 ( NMEA [ Len ] ) ;
if ( AcftType < 0 ) return - 1 ;
Packet . Position . AcftType = AcftType ;
Len + = 2 ;
if ( NMEA [ Len + 1 ] ! = ' , ' ) return - 1 ;
int8_t AddrType = Read_Hex1 ( NMEA [ Len ] ) ;
if ( ( AddrType < 0 ) | | ( AddrType > = 4 ) ) return - 1 ;
Packet . Header . AddrType = AddrType ;
Len + = 2 ;
uint32_t Addr ;
int8_t Ret = Read_Hex ( Addr , NMEA + Len ) ; if ( Ret < = 0 ) return - 1 ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Packet . Header . Address = Addr ;
Len + = Ret + 1 ;
if ( NMEA [ Len + 1 ] ! = ' , ' ) return - 1 ;
int8_t Relay = Read_Hex1 ( NMEA [ Len ] ) ;
if ( ( Relay < 0 ) | | ( Relay > = 4 ) ) return - 1 ;
Packet . Header . RelayCount = Relay ;
Len + = 2 ;
if ( NMEA [ Len + 2 ] ! = ' , ' ) return - 1 ;
int8_t FixQuality = Read_Hex1 ( NMEA [ Len ] ) ;
int8_t FixMode = Read_Hex1 ( NMEA [ Len + 1 ] ) ;
if ( ( FixQuality < 0 ) | | ( FixQuality > = 4 ) ) return - 1 ;
if ( ( FixMode < 0 ) | | ( FixMode > = 2 ) ) return - 1 ;
Packet . Position . FixQuality = FixQuality ;
Packet . Position . FixMode = FixMode ;
Len + = 3 ;
int32_t DOP = 0 ;
Ret = Read_Float1 ( DOP , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
if ( DOP < 10 ) DOP = 10 ;
Packet . EncodeDOP ( DOP - 10 ) ;
Len + = Ret + 1 ;
if ( NMEA [ Len + 10 ] ! = ' , ' ) return - 1 ;
int8_t Deg = Read_Dec2 ( NMEA + Len ) ; if ( Deg < 0 ) return - 1 ;
int8_t Min = Read_Dec2 ( NMEA + Len + 2 ) ; if ( Min < 0 ) return - 1 ;
if ( NMEA [ Len + 4 ] ! = ' . ' ) return - 1 ;
int16_t Frac = Read_Dec4 ( NMEA + Len + 5 ) ; if ( Frac < 0 ) return - 1 ;
char Sign = NMEA [ Len + 9 ] ;
int32_t Lat = Deg * 600000 + Min * 10000 + Frac ;
if ( Sign = = ' N ' ) { } else if ( Sign = = ' S ' ) { Lat = ( - Lat ) ; } else return - 1 ;
Packet . EncodeLatitude ( Lat ) ;
Len + = 11 ;
if ( NMEA [ Len + 11 ] ! = ' , ' ) return - 1 ;
Deg = Read_Dec3 ( NMEA + Len ) ; if ( Deg < 0 ) return - 1 ;
Min = Read_Dec2 ( NMEA + Len + 3 ) ; if ( Min < 0 ) return - 1 ;
if ( NMEA [ Len + 5 ] ! = ' . ' ) return - 1 ;
Frac = Read_Dec4 ( NMEA + Len + 6 ) ; if ( Frac < 0 ) return - 1 ;
Sign = NMEA [ Len + 10 ] ;
int32_t Lon = Deg * 600000 + Min * 10000 + Frac ;
if ( Sign = = ' E ' ) { } else if ( Sign = = ' W ' ) { Lon = ( - Lon ) ; } else return - 1 ;
Packet . EncodeLongitude ( Lon ) ;
Len + = 12 ;
int32_t Alt = 0 ;
Ret = Read_SignDec ( Alt , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
Packet . EncodeAltitude ( Alt ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t AltDiff = 0 ;
Ret = Read_SignDec ( AltDiff , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
// printf("Ret=%d, AltDiff=%d -> %s\n", Ret, AltDiff, NMEA+Len);
if ( Ret = = 0 ) Packet . clrBaro ( ) ;
else Packet . setBaroAltDiff ( AltDiff ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t Climb = 0 ;
Ret = Read_Float1 ( Climb , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
// printf("Ret=%d, Climb=%d -> %s\n", Ret, Climb, NMEA+Len);
Packet . EncodeClimbRate ( Climb ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t Speed = 0 ;
Ret = Read_Float1 ( Speed , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
Packet . EncodeSpeed ( Speed ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t Heading = 0 ;
Ret = Read_Float1 ( Heading , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
Packet . EncodeHeading ( Heading ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t TurnRate = 0 ;
Ret = Read_Float1 ( TurnRate , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
Packet . EncodeTurnRate ( TurnRate ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t RSSI = 0 ;
Ret = Read_SignDec ( RSSI , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
RxRSSI = ( - 2 * RSSI ) ;
if ( NMEA [ Len + Ret ] ! = ' , ' ) return - 1 ;
Len + = Ret + 1 ;
int32_t Err = 0 ;
Ret = Read_SignDec ( Err , NMEA + Len ) ; if ( Ret < 0 ) return - 1 ;
RxErr = Err ;
if ( NMEA [ Len + Ret ] ! = ' * ' ) return - 1 ;
Len + = Ret + 1 ;
return Len ; }
uint8_t WritePOGNT ( char * NMEA )
{ uint8_t Len = 0 ;
Len + = Format_String ( NMEA + Len , " $POGNT, " ) ; // sentence name
if ( Packet . Position . Time < 60 )
Len + = Format_UnsDec ( NMEA + Len , ( uint16_t ) Packet . Position . Time , 2 ) ; // [sec] time
NMEA [ Len + + ] = ' , ' ;
NMEA [ Len + + ] = HexDigit ( Packet . Position . AcftType ) ; // [0..F] aircraft-type: 1=glider, 2=tow plane, etc.
NMEA [ Len + + ] = ' , ' ;
NMEA [ Len + + ] = ' 0 ' + Packet . Header . AddrType ; // [0..3] address-type: 1=ICAO, 2=FLARM, 3=OGN
NMEA [ Len + + ] = ' , ' ;
uint32_t Addr = Packet . Header . Address ; // [24-bit] address
Len + = Format_Hex ( NMEA + Len , ( uint8_t ) ( Addr > > 16 ) ) ;
Len + = Format_Hex ( NMEA + Len , ( uint16_t ) Addr ) ;
NMEA [ Len + + ] = ' , ' ;
NMEA [ Len + + ] = ' 0 ' + Packet . Header . RelayCount ; // [0..3] counts retransmissions
NMEA [ Len + + ] = ' , ' ;
NMEA [ Len + + ] = ' 0 ' + Packet . Position . FixQuality ; // [] fix quality
NMEA [ Len + + ] = ' 0 ' + Packet . Position . FixMode ; // [] fix mode
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , ( uint16_t ) ( Packet . DecodeDOP ( ) + 10 ) , 2 , 1 ) ; // [] Dilution of Precision
NMEA [ Len + + ] = ' , ' ;
Len + = Packet . PrintLatitude ( NMEA + Len , Packet . DecodeLatitude ( ) ) ; // [] Latitude
NMEA [ Len + + ] = ' , ' ;
Len + = Packet . PrintLongitude ( NMEA + Len , Packet . DecodeLongitude ( ) ) ; // [] Longitude
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , ( uint32_t ) Packet . DecodeAltitude ( ) ) ; // [m] Altitude (by GPS)
NMEA [ Len + + ] = ' , ' ;
if ( Packet . hasBaro ( ) )
Len + = Format_SignDec ( NMEA + Len , ( int32_t ) Packet . getBaroAltDiff ( ) ) ; // [m] Standard Pressure Altitude (by Baro)
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , Packet . DecodeClimbRate ( ) , 2 , 1 ) ; // [m/s] climb/sink rate (by GPS or pressure sensor)
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , Packet . DecodeSpeed ( ) , 2 , 1 ) ; // [m/s] ground speed (by GPS)
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , Packet . DecodeHeading ( ) , 4 , 1 ) ; // [deg] heading (by GPS)
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , Packet . DecodeTurnRate ( ) , 2 , 1 ) ; // [deg/s] turning rate (by GPS)
NMEA [ Len + + ] = ' , ' ;
Len + = Format_SignDec ( NMEA + Len , - ( int16_t ) RxRSSI / 2 ) ; // [dBm] received signal level
NMEA [ Len + + ] = ' , ' ;
Len + = Format_UnsDec ( NMEA + Len , ( uint16_t ) RxErr ) ; // [bits] corrected transmisison errors
Len + = NMEA_AppendCheckCRNL ( NMEA , Len ) ;
NMEA [ Len ] = 0 ;
return Len ; }
void Print ( void ) const
{ printf ( " [%02d/%+6.1fdBm/%2d] " , RxChan , - 0.5 * RxRSSI , RxErr ) ;
Packet . Print ( ) ; }
uint8_t Print ( char * Out ) const
{ uint8_t Len = 0 ;
Out [ Len + + ] = HexDigit ( Packet . Position . AcftType ) ; Out [ Len + + ] = ' : ' ;
Out [ Len + + ] = ' 0 ' + Packet . Header . AddrType ; Out [ Len + + ] = ' : ' ;
uint32_t Addr = Packet . Header . Address ;
Len + = Format_Hex ( Out + Len , ( uint8_t ) ( Addr > > 16 ) ) ;
Len + = Format_Hex ( Out + Len , ( uint16_t ) Addr ) ;
Out [ Len + + ] = ' ' ;
Len + = Format_SignDec ( Out + Len , - ( int16_t ) RxRSSI / 2 ) ; Out [ Len + + ] = ' d ' ; Out [ Len + + ] = ' B ' ; Out [ Len + + ] = ' m ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , ( uint16_t ) Packet . Position . Time , 2 ) ;
Out [ Len + + ] = ' ' ;
Len + = Packet . PrintLatitude ( Out + Len , Packet . DecodeLatitude ( ) ) ;
Out [ Len + + ] = ' ' ;
Len + = Packet . PrintLongitude ( Out + Len , Packet . DecodeLongitude ( ) ) ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , ( uint32_t ) Packet . DecodeAltitude ( ) ) ; Out [ Len + + ] = ' m ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_UnsDec ( Out + Len , Packet . DecodeSpeed ( ) , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' ' ;
Len + = Format_SignDec ( Out + Len , Packet . DecodeClimbRate ( ) , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' \n ' ; Out [ Len ] = 0 ;
return Len ; }
void Dump ( void ) const
{ printf ( " %08lX: %08lX %08lX %08lX %08lX [%08lX %04lX] (%d) \n " ,
( long int ) Packet . HeaderWord , ( long int ) Packet . Data [ 0 ] , ( long int ) Packet . Data [ 1 ] ,
( long int ) Packet . Data [ 2 ] , ( long int ) Packet . Data [ 3 ] ,
( long int ) FEC [ 0 ] , ( long int ) FEC [ 1 ] , ( int ) checkFEC ( ) ) ; }
void DumpBytes ( void ) const
{ for ( uint8_t Idx = 0 ; Idx < 26 ; Idx + + )
{ printf ( " %02X " , Packet . Byte ( ) [ Idx ] ) ; }
printf ( " (%d) \n " , LDPC_Check ( Packet . Byte ( ) ) ) ; }
} ;
# ifdef WITH_PPM
class OGN_PPM_Packet // OGN packet with FEC code and some reception info
{ public :
static const int Words = 12 ;
OGN_Packet Packet ;
uint32_t FEC [ 7 ] ; // Gallager code: 194 check bits for 160 user bits
public :
void calcFEC ( void ) { LDPC_Encode_n354k160 ( Packet . Word ( ) ) ; } // calculate the 48-bit parity check
uint8_t checkFEC ( void ) const { return LDPC_Check_n354k160 ( Packet . Word ( ) ) ; } // returns number of parity checks that fail (0 => no errors, all fine)
uint32_t * Word ( void ) const { return Packet . Word ( ) ; }
void Dump ( void ) const
{ printf ( " %08lX: %08lX %08lX %08lX %08lX [%08lX %08lX %08lX %08lX %08lX %08lX %01lX] (%d) \n " ,
( long int ) Packet . HeaderWord , ( long int ) Packet . Data [ 0 ] , ( long int ) Packet . Data [ 1 ] ,
( long int ) Packet . Data [ 2 ] , ( long int ) Packet . Data [ 3 ] ,
( long int ) FEC [ 0 ] , ( long int ) FEC [ 1 ] , ( long int ) FEC [ 2 ] , ( long int ) FEC [ 2 ] ,
( long int ) FEC [ 4 ] , ( long int ) FEC [ 5 ] , ( long int ) FEC [ 6 ] , ( int ) checkFEC ( ) ) ; }
static uint8_t Gray ( uint8_t Binary ) { return Binary ^ ( Binary > > 1 ) ; }
static uint8_t Binary ( uint8_t Gray )
{ Gray = Gray ^ ( Gray > > 4 ) ;
Gray = Gray ^ ( Gray > > 2 ) ;
Gray = Gray ^ ( Gray > > 1 ) ;
return Gray ; }
uint8_t getSymbol ( uint16_t Idx )
{ if ( Idx > = 59 ) return 0xFF ;
uint32_t * Word = Packet . Word ( ) ;
uint8_t Symbol = 0 ; uint8_t SymbMask = 1 ;
for ( uint8_t Bit = 0 ; Bit < 6 ; Bit + + , Idx + = 59 )
{ uint8_t WordIdx = Idx > > 5 ; uint8_t BitIdx = Idx & 31 ;
uint32_t Mask = 1 ; Mask < < = BitIdx ;
if ( Word [ WordIdx ] & Mask ) Symbol | = SymbMask ;
SymbMask < < = 1 ; }
return Gray ( Symbol ) ; }
void clear ( void )
{ memset ( Packet . Word ( ) , 0 , Words * 4 ) ; }
void setSymbol ( uint16_t Idx , uint8_t Symbol )
{ if ( Idx > = 59 ) return ;
Symbol = Binary ( Symbol ) ;
uint32_t * Word = Packet . Word ( ) ;
for ( uint8_t Bit = 0 ; Bit < 6 ; Bit + + , Idx + = 59 )
{ if ( Symbol & 1 )
{ uint8_t WordIdx = Idx > > 5 ; uint8_t BitIdx = Idx & 31 ;
uint32_t Mask = 1 ; Mask < < = BitIdx ;
Word [ WordIdx ] | = Mask ; }
Symbol > > = 1 ; }
}
} ;
# endif // WITH_PPM
// ---------------------------------------------------------------------------------------------------------------------
template < uint8_t Size = 8 >
class OGN_PrioQueue
{ public :
// static const uint8_t Size = 8; // number of packets kept
OGN_RxPacket Packet [ Size ] ; // OGN packets
uint16_t Sum ; // sum of all ranks
uint8_t Low , LowIdx ; // the lowest rank and the index of it
public :
void Clear ( void ) // clear (reset) the queue
{ for ( uint8_t Idx = 0 ; Idx < Size ; Idx + + ) // clear every packet
{ Packet [ Idx ] . Clear ( ) ; }
Sum = 0 ; Low = 0 ; LowIdx = 0 ; } // clear the rank sum, lowest rank
OGN_RxPacket * operator [ ] ( uint8_t Idx ) { return Packet + Idx ; }
uint8_t getNew ( void ) // get (index of) a free or lowest rank packet
{ Sum - = Packet [ LowIdx ] . Rank ; Packet [ LowIdx ] . Rank = 0 ; Low = 0 ; return LowIdx ; } // remove old packet from the rank sum
void addNew ( uint8_t NewIdx ) // add the new packet to the queue
{ uint32_t AddressAndType = Packet [ NewIdx ] . Packet . getAddressAndType ( ) ; // get ID of this packet: ID is address-type and address (2+24 = 26 bits)
for ( uint8_t Idx = 0 ; Idx < Size ; Idx + + ) // look for other packets with same ID
{ if ( Idx = = NewIdx ) continue ; // avoid the new packet
if ( Packet [ Idx ] . Packet . getAddressAndType ( ) = = AddressAndType ) // if another packet with same ID:
{ clean ( Idx ) ; } // then remove it: set rank to zero
}
uint8_t Rank = Packet [ NewIdx ] . Rank ; Sum + = Rank ; // add the new packet to the rank sum
if ( NewIdx = = LowIdx ) reCalc ( ) ;
else { if ( Rank < Low ) { Low = Rank ; LowIdx = NewIdx ; } }
// if(NewIdx!=LowIdx) //
// { if(Rank<=Low) { Low=Rank; LowIdx=NewIdx; } }
// else reCalc();
}
uint8_t getRand ( uint32_t Rand ) const // get a position by random selection but probabilities prop. to ranks
{ if ( Sum = = 0 ) return Rand % Size ; //
uint16_t RankIdx = Rand % Sum ;
uint8_t Idx ; uint16_t RankSum = 0 ;
for ( Idx = 0 ; Idx < Size ; Idx + + )
{ uint8_t Rank = Packet [ Idx ] . Rank ; if ( Rank = = 0 ) continue ;
RankSum + = Rank ; if ( RankSum > RankIdx ) return Idx ; }
return Rand % Size ; }
void reCalc ( void ) // find the lowest rank and calc. the sum of all ranks
{ Sum = Low = Packet [ 0 ] . Rank ; LowIdx = 0 ; // take minimum at the first slot
for ( uint8_t Idx = 1 ; Idx < Size ; Idx + + ) // loop over all other slots
{ uint8_t Rank = Packet [ Idx ] . Rank ;
Sum + = Rank ; // sum up the ranks
if ( Rank < Low ) { Low = Rank ; LowIdx = Idx ; } // update the minimum
}
}
void cleanTime ( uint8_t Time ) // clean up slots of given Time
{ for ( int Idx = 0 ; Idx < Size ; Idx + + )
{ if ( ( Packet [ Idx ] . Rank ) & & ( Packet [ Idx ] . Packet . Position . Time = = Time ) )
{ clean ( Idx ) ; }
}
}
void clean ( uint8_t Idx ) // clean given slot
{ Sum - = Packet [ Idx ] . Rank ; Packet [ Idx ] . Rank = 0 ; Low = 0 ; LowIdx = Idx ; }
void decrRank ( uint8_t Idx , uint8_t Decr = 1 ) // decrement rank of given slot
{ uint8_t Rank = Packet [ Idx ] . Rank ; if ( Rank = = 0 ) return ; // if zero already: do nothing
if ( Decr > Rank ) Decr = Rank ; // if to decrement by more than the rank already: reduce the decrement
Rank - = Decr ; Sum - = Decr ; // decrement the rank and the sum of ranks
if ( Rank < Low ) { Low = Rank ; LowIdx = Idx ; } // if new minimum: update the minimum.
Packet [ Idx ] . Rank = Rank ; } // update the rank of this slot
uint8_t Print ( char * Out )
{ uint8_t Len = 0 ;
for ( uint8_t Idx = 0 ; Idx < Size ; Idx + + )
{ uint8_t Rank = Packet [ Idx ] . Rank ;
Out [ Len + + ] = ' ' ; Len + = Format_Hex ( Out + Len , Rank ) ;
if ( Rank )
{ Out [ Len + + ] = ' / ' ; Len + = Format_Hex ( Out + Len , Packet [ Idx ] . Packet . getAddressAndType ( ) ) ;
Out [ Len + + ] = ' : ' ; Len + = Format_UnsDec ( Out + Len , Packet [ Idx ] . Packet . Position . Time , 2 ) ; }
}
Out [ Len + + ] = ' ' ; Len + = Format_Hex ( Out + Len , Sum ) ;
Out [ Len + + ] = ' / ' ; Len + = Format_Hex ( Out + Len , LowIdx ) ;
Out [ Len + + ] = ' \n ' ; Out [ Len ] = 0 ; return Len ; }
} ;
class GPS_Position
{ public :
union
{ uint8_t Flags ; // bit #0 = GGA and RMC had same Time
struct
2018-02-18 14:41:50 +00:00
{ bool hasGPS : 1 ; // all required GPS information has been supplied (but this is not the GPS lock status)
bool hasBaro : 1 ; // barometric information has beed supplied
bool isReady : 1 ; // is ready for the following treaement
2018-01-29 12:43:22 +00:00
bool Sent : 1 ; // has been transmitted
2018-05-19 01:03:18 +00:00
bool hasTime : 1 ; // full Unix Date/Time has been supplied
2018-02-18 14:41:50 +00:00
bool hasRMC : 1 ; // GxRMC has been supplied
bool hasGGA : 1 ; // GxGGA has been supplied
bool hasGSA : 1 ; // GxGSA has been supplied
2018-01-29 12:43:22 +00:00
} ;
} ;
int8_t FixQuality ; // 0 = none, 1 = GPS, 2 = Differential GPS (can be WAAS)
int8_t FixMode ; // 0 = not set (from GSA) 1 = none, 2 = 2-D, 3 = 3-D
int8_t Satellites ; // number of active satellites
int8_t Year , Month , Day ; // Date (UTC) from GPS
int8_t Hour , Min , Sec ; // Time-of-day (UTC) from GPS
int8_t FracSec ; // [1/100 sec] some GPS-es give second fraction with the time-of-day
uint8_t PDOP ; // [0.1] dilution of precision
uint8_t HDOP ; // [0.1] horizontal dilution of precision
uint8_t VDOP ; // [0.1] vertical dilution of precision
int16_t Speed ; // [0.1 m/s] speed-over-ground
int16_t Heading ; // [0.1 deg] heading-over-ground
int16_t ClimbRate ; // [0.1 meter/sec)
int16_t TurnRate ; // [0.1 deg/sec]
int16_t GeoidSeparation ; // [0.1 meter] difference between Geoid and Ellipsoid
int32_t Altitude ; // [0.1 meter] height above Geoid (sea level)
int32_t Latitude ; // [0.0001/60 deg] about 0.018m accuracy (to convert to u-Blox GPS 1e-7deg units mult by 50/3)
int32_t Longitude ; // [0.0001/60 deg]
uint16_t LatitudeCosine ; // [2^-12] Latitude cosine for distance calculation
int16_t Temperature ; // [0.1 degC]
uint32_t Pressure ; // [0.25 Pa] from pressure sensor
int32_t StdAltitude ; // [0.1 meter] standard pressure altitude (from the pressure sensor and atmosphere calculator)
public :
GPS_Position ( ) { Clear ( ) ; }
void Clear ( void )
{ Flags = 0 ; FixQuality = 0 ; FixMode = 0 ;
PDOP = 0 ; HDOP = 0 ; VDOP = 0 ;
setDefaultDate ( ) ; setDefaultTime ( ) ;
Latitude = 0 ; Longitude = 0 ; LatitudeCosine = 3000 ;
Altitude = 0 ; GeoidSeparation = 0 ;
Speed = 0 ; Heading = 0 ; ClimbRate = 0 ; TurnRate = 0 ;
2018-02-18 14:41:50 +00:00
Temperature = 0 ; Pressure = 0 ; StdAltitude = 0 ; }
2018-01-29 12:43:22 +00:00
2018-02-18 14:41:50 +00:00
void setDefaultDate ( ) { Year = 00 ; Month = 1 ; Day = 1 ; } // default Date is 01-JAN-2000
void setDefaultTime ( ) { Hour = 0 ; Min = 0 ; Sec = 0 ; FracSec = 0 ; } // default Time is 00:00:00.00
2018-01-29 12:43:22 +00:00
2018-02-18 14:41:50 +00:00
bool isTimeValid ( void ) const // is the GPS time-of-day valid
2018-01-29 12:43:22 +00:00
{ return ( Hour > = 0 ) & & ( Min > = 0 ) & & ( Sec > = 0 ) ; } // all data must have been correctly read: negative means not correctly read)
bool isDateValid ( void ) const // is the GPS date valid ?
{ return ( Year > = 0 ) & & ( Month > = 0 ) & & ( Day > = 0 ) ; }
2018-02-18 14:41:50 +00:00
bool isValid ( void ) const // is GPS data is valid = GPS lock
2018-01-29 12:43:22 +00:00
{ if ( ! isTimeValid ( ) ) return 0 ; // is GPS time valid/present ?
if ( ! isDateValid ( ) ) return 0 ; // is GPS date valid/present ?
if ( FixQuality = = 0 ) return 0 ; // Fix quality must be 1=GPS or 2=DGPS
if ( FixMode = = 1 ) return 0 ; // if GSA says "no lock" (when GSA is not there, FixMode=0)
if ( Satellites < = 0 ) return 0 ; // if number of satellites none or invalid
return 1 ; }
void copyTime ( GPS_Position & RefPosition ) // copy HH:MM:SS.SSS from another record
{ FracSec = RefPosition . FracSec ;
Sec = RefPosition . Sec ;
Min = RefPosition . Min ;
Hour = RefPosition . Hour ; }
void copyDate ( GPS_Position & RefPosition ) // copy YY:MM:DD from another record
{ Day = RefPosition . Day ;
Month = RefPosition . Month ;
Year = RefPosition . Year ; }
void copyTimeDate ( GPS_Position & RefPosition ) { copyTime ( RefPosition ) ; copyDate ( RefPosition ) ; }
uint8_t incrTime ( void ) // increment HH:MM:SS by one second
{ Sec + + ; if ( Sec < 60 ) return 0 ;
Sec = 0 ;
Min + + ; if ( Min < 60 ) return 0 ;
Min = 0 ;
Hour + + ; if ( Hour < 24 ) return 0 ;
Hour = 0 ;
return 1 ; } // return 1 if date needs to be incremented
uint8_t MonthDays ( void ) // number of days per month
{ const uint16_t Table = 0x0AD5 ; // 1010 1101 0101 0=30days, 1=31days
// const uint8_t Table[12] = { 31,28,31,30, 31,30,31,31, 30,31,30,31 };
if ( ( Month < 1 ) | | ( Month > 12 ) ) return 0 ;
if ( Month = = 2 ) return 28 + isLeapYear ( ) ;
return 30 + ( ( Table > > ( Month - 1 ) ) & 1 ) ; }
void incrDate ( int8_t Days = 1 ) // increment YY:MM:DD
{ uint8_t DaysPerMonth = MonthDays ( ) ;
Day + = Days ; if ( Day < = DaysPerMonth ) return ;
Day - = DaysPerMonth ; Month + + ; if ( Month < = 12 ) return ;
Month = 1 ; Year + + ; }
void incrTimeDate ( void ) { if ( incrTime ( ) ) incrDate ( ) ; }
# ifndef __AVR__ // there is not printf() with AVR
void PrintDateTime ( void ) const { printf ( " %02d.%02d.%04d %02d:%02d:%05.2f " , Day , Month , 2000 + Year , Hour , Min , Sec + 0.01 * FracSec ) ; }
void PrintTime ( void ) const { printf ( " %02d:%02d:%05.2f " , Hour , Min , Sec + 0.01 * FracSec ) ; }
int PrintDateTime ( char * Out ) const { return sprintf ( Out , " %02d.%02d.%04d %02d:%02d:%02d.%02d " , Day , Month , Year , Hour , Min , Sec , FracSec ) ; }
int PrintTime ( char * Out ) const { return sprintf ( Out , " %02d:%02d:%02d.%02d " , Hour , Min , Sec , FracSec ) ; }
void Print ( void ) const
{ printf ( " Time/Date = " ) ; PrintDateTime ( ) ; printf ( " " ) ; // printf(" = %10ld.%03dsec\n", (long int)UnixTime, mSec);
printf ( " FixQuality/Mode=%d/%d: %d satellites DOP/H/V=%3.1f/%3.1f/%3.1f " , FixQuality , FixMode , Satellites , 0.1 * PDOP , 0.1 * HDOP , 0.1 * VDOP ) ;
printf ( " FixQuality=%d: %d satellites HDOP=%3.1f " , FixQuality , Satellites , 0.1 * HDOP ) ;
printf ( " Lat/Lon/Alt = [%+10.6f,%+10.6f]deg %+3.1f(%+3.1f)m LatCosine=%+6.3f " , 0.0001 / 60 * Latitude , 0.0001 / 60 * Longitude , 0.1 * Altitude , 0.1 * GeoidSeparation , 1.0 / ( 1 < < 12 ) * LatitudeCosine ) ;
printf ( " Speed/Heading = %3.1fm/s %05.1fdeg \n " , 0.1 * Speed , 0.1 * Heading ) ;
}
int Print ( char * Out ) const
{ int Len = 0 ;
Len + = sprintf ( Out + Len , " Time/Date = " ) ; Len + = PrintDateTime ( Out + Len ) ; printf ( " " ) ; // Len+=sprintf(Out+Len, " = %10ld.%02dsec\n", (long int)UnixTime, FracSec);
Len + = sprintf ( Out + Len , " FixQuality/Mode=%d/%d: %d satellites DOP/H/V=%3.1f/%3.1f/%3.1f " , FixQuality , FixMode , Satellites , 0.1 * PDOP , 0.1 * HDOP , 0.1 * VDOP ) ;
Len + = sprintf ( Out + Len , " Lat/Lon/Alt = [%+10.6f,%+10.6f]deg %+3.1f(%+3.1f)m " , 0.0001 / 60 * Latitude , 0.0001 / 60 * Longitude , 0.1 * Altitude , 0.1 * GeoidSeparation ) ;
Len + = sprintf ( Out + Len , " Speed/Heading = %3.1fm/s %05.1fdeg \n " , 0.1 * Speed , 0.1 * Heading ) ;
return Len ; }
void PrintLine ( void ) const
{ PrintTime ( ) ;
printf ( " %d/%d/%02d/%4.1f/%4.1f/%4.1f " , FixQuality , FixMode , Satellites , 0.1 * PDOP , 0.1 * HDOP , 0.1 * VDOP ) ;
printf ( " [%+10.6f,%+10.6f]deg %+3.1f(%+3.1f)m " , 0.0001 / 60 * Latitude , 0.0001 / 60 * Longitude , 0.1 * Altitude , 0.1 * GeoidSeparation ) ;
printf ( " %4.1fm/s %05.1fdeg " , 0.1 * Speed , 0.1 * Heading ) ;
printf ( " \n " ) ; }
int PrintLine ( char * Out ) const
2018-02-18 14:41:50 +00:00
{ int Len = 0 ; // PrintDateTime(Out);
2018-05-19 01:03:18 +00:00
Out [ Len + + ] = hasTime ? ' U ' : ' _ ' ;
2018-02-18 14:41:50 +00:00
Out [ Len + + ] = hasGPS ? ' G ' : ' _ ' ;
Out [ Len + + ] = hasBaro ? ' B ' : ' _ ' ;
Out [ Len + + ] = hasRMC ? ' R ' : ' _ ' ;
Out [ Len + + ] = hasGGA ? ' G ' : ' _ ' ;
Out [ Len + + ] = hasGSA ? ' G ' : ' _ ' ;
Out [ Len + + ] = isValid ( ) ? ' V ' : ' _ ' ;
Out [ Len + + ] = isTimeValid ( ) ? ' T ' : ' _ ' ;
Out [ Len + + ] = isDateValid ( ) ? ' D ' : ' _ ' ;
Out [ Len + + ] = ' ' ; Len + = Format_UnsDec ( Out + Len , ( uint16_t ) Hour , 2 ) ;
Out [ Len + + ] = ' : ' ; Len + = Format_UnsDec ( Out + Len , ( uint16_t ) Min , 2 ) ;
Out [ Len + + ] = ' : ' ; Len + = Format_UnsDec ( Out + Len , ( uint16_t ) Sec , 2 ) ;
Out [ Len + + ] = ' . ' ; Len + = Format_UnsDec ( Out + Len , ( uint16_t ) FracSec , 2 ) ;
Len + = sprintf ( Out + Len , " %d/%d/%02d " , FixQuality , FixMode , Satellites ) ;
2018-01-29 12:43:22 +00:00
Out [ Len + + ] = ' / ' ; Len + = Format_UnsDec ( Out + Len , PDOP , 2 , 1 ) ;
Out [ Len + + ] = ' / ' ; Len + = Format_UnsDec ( Out + Len , HDOP , 2 , 1 ) ;
Out [ Len + + ] = ' / ' ; Len + = Format_UnsDec ( Out + Len , VDOP , 2 , 1 ) ;
Out [ Len + + ] = ' ' ;
Out [ Len + + ] = ' [ ' ; Len + = Format_SignDec ( Out + Len , Latitude / 60 , 6 , 4 ) ;
Out [ Len + + ] = ' , ' ; Len + = Format_SignDec ( Out + Len , Longitude / 60 , 7 , 4 ) ;
Out [ Len + + ] = ' ] ' ; Out [ Len + + ] = ' d ' ; Out [ Len + + ] = ' e ' ; Out [ Len + + ] = ' g ' ;
Out [ Len + + ] = ' ' ; Len + = Format_SignDec ( Out + Len , Altitude , 4 , 1 ) ; Out [ Len + + ] = ' m ' ;
Out [ Len + + ] = ' / ' ; Len + = Format_SignDec ( Out + Len , GeoidSeparation , 4 , 1 ) ; Out [ Len + + ] = ' m ' ;
Out [ Len + + ] = ' ' ; Len + = Format_UnsDec ( Out + Len , Speed , 2 , 1 ) ; Out [ Len + + ] = ' m ' ; Out [ Len + + ] = ' / ' ; Out [ Len + + ] = ' s ' ;
Out [ Len + + ] = ' ' ; Len + = Format_UnsDec ( Out + Len , Heading , 4 , 1 ) ; Out [ Len + + ] = ' d ' ; Out [ Len + + ] = ' e ' ; Out [ Len + + ] = ' g ' ;
2018-02-18 14:41:50 +00:00
Out [ Len + + ] = ' ' ; Len + = Format_SignDec ( Out + Len , Temperature , 2 , 1 ) ; Out [ Len + + ] = ' C ' ;
Out [ Len + + ] = ' ' ; Len + = Format_UnsDec ( Out + Len , Pressure / 4 ) ; Out [ Len + + ] = ' P ' ; Out [ Len + + ] = ' a ' ;
Out [ Len + + ] = ' ' ; Len + = Format_SignDec ( Out + Len , StdAltitude , 2 , 1 ) ; Out [ Len + + ] = ' m ' ;
2018-01-29 12:43:22 +00:00
Out [ Len + + ] = ' \n ' ; Out [ Len + + ] = 0 ; return Len ; }
# endif // __AVR__
int8_t ReadNMEA ( NMEA_RxMsg & RxMsg )
{ if ( RxMsg . isGPGGA ( ) ) return ReadGGA ( RxMsg ) ;
else if ( RxMsg . isGNGGA ( ) ) return ReadGGA ( RxMsg ) ;
else if ( RxMsg . isGPRMC ( ) ) return ReadRMC ( RxMsg ) ;
else if ( RxMsg . isGNRMC ( ) ) return ReadRMC ( RxMsg ) ;
else if ( RxMsg . isGPGSA ( ) ) return ReadGSA ( RxMsg ) ;
else if ( RxMsg . isGNGSA ( ) ) return ReadGSA ( RxMsg ) ;
else return 0 ; }
int8_t ReadNMEA ( const char * NMEA )
{ int Err = 0 ;
Err = ReadGGA ( NMEA ) ; if ( Err ! = ( - 1 ) ) return Err ;
Err = ReadGSA ( NMEA ) ; if ( Err ! = ( - 1 ) ) return Err ;
Err = ReadRMC ( NMEA ) ; if ( Err ! = ( - 1 ) ) return Err ;
return 0 ; }
int8_t ReadGGA ( NMEA_RxMsg & RxMsg )
{ if ( RxMsg . Parms < 14 ) return - 1 ; // no less than 14 paramaters
2018-02-18 14:41:50 +00:00
hasGPS = ReadTime ( ( const char * ) RxMsg . ParmPtr ( 0 ) ) > 0 ; // read time and check if same as the RMC says
2018-01-29 12:43:22 +00:00
FixQuality = Read_Dec1 ( * RxMsg . ParmPtr ( 5 ) ) ; if ( FixQuality < 0 ) FixQuality = 0 ; // fix quality: 0=invalid, 1=GPS, 2=DGPS
Satellites = Read_Dec2 ( ( const char * ) RxMsg . ParmPtr ( 6 ) ) ; // number of satellites
if ( Satellites < 0 ) Satellites = Read_Dec1 ( RxMsg . ParmPtr ( 6 ) [ 0 ] ) ;
if ( Satellites < 0 ) Satellites = 0 ;
ReadHDOP ( ( const char * ) RxMsg . ParmPtr ( 7 ) ) ; // horizontal dilution of precision
ReadLatitude ( * RxMsg . ParmPtr ( 2 ) , ( const char * ) RxMsg . ParmPtr ( 1 ) ) ; // Latitude
ReadLongitude ( * RxMsg . ParmPtr ( 4 ) , ( const char * ) RxMsg . ParmPtr ( 3 ) ) ; // Longitude
ReadAltitude ( * RxMsg . ParmPtr ( 9 ) , ( const char * ) RxMsg . ParmPtr ( 8 ) ) ; // Altitude
ReadGeoidSepar ( * RxMsg . ParmPtr ( 11 ) , ( const char * ) RxMsg . ParmPtr ( 10 ) ) ; // Geoid separation
// calcLatitudeCosine();
return 1 ; }
int8_t ReadGGA ( const char * GGA )
{ if ( ( memcmp ( GGA , " $GPGGA " , 6 ) ! = 0 ) & & ( memcmp ( GGA , " $GNGGA " , 6 ) ! = 0 ) ) return - 1 ; // check if the right sequence
uint8_t Index [ 20 ] ; if ( IndexNMEA ( Index , GGA ) < 14 ) return - 2 ; // index parameters and check the sum
2018-02-18 14:41:50 +00:00
hasGPS = ReadTime ( GGA + Index [ 0 ] ) > 0 ;
2018-01-29 12:43:22 +00:00
FixQuality = Read_Dec1 ( GGA [ Index [ 5 ] ] ) ; if ( FixQuality < 0 ) FixQuality = 0 ; // fix quality
Satellites = Read_Dec2 ( GGA + Index [ 6 ] ) ; // number of satellites
if ( Satellites < 0 ) Satellites = Read_Dec1 ( GGA [ Index [ 6 ] ] ) ;
if ( Satellites < 0 ) Satellites = 0 ;
ReadHDOP ( GGA + Index [ 7 ] ) ; // horizontal dilution of precision
ReadLatitude ( GGA [ Index [ 2 ] ] , GGA + Index [ 1 ] ) ; // Latitude
ReadLongitude ( GGA [ Index [ 4 ] ] , GGA + Index [ 3 ] ) ; // Longitude
ReadAltitude ( GGA [ Index [ 9 ] ] , GGA + Index [ 8 ] ) ; // Altitude
ReadGeoidSepar ( GGA [ Index [ 11 ] ] , GGA + Index [ 10 ] ) ; // Geoid separation
// calcLatitudeCosine();
return 1 ; }
int8_t ReadGSA ( NMEA_RxMsg & RxMsg )
{ if ( RxMsg . Parms < 17 ) return - 1 ;
FixMode = Read_Dec1 ( * RxMsg . ParmPtr ( 1 ) ) ; if ( FixMode < 0 ) FixMode = 0 ; // fix mode
ReadPDOP ( ( const char * ) RxMsg . ParmPtr ( 14 ) ) ; // total dilution of precision
ReadHDOP ( ( const char * ) RxMsg . ParmPtr ( 15 ) ) ; // horizontal dilution of precision
ReadVDOP ( ( const char * ) RxMsg . ParmPtr ( 16 ) ) ; // vertical dilution of precision
return 1 ; }
int8_t ReadGSA ( const char * GSA )
{ if ( ( memcmp ( GSA , " $GPGSA " , 6 ) ! = 0 ) & & ( memcmp ( GSA , " $GNGSA " , 6 ) ! = 0 ) ) return - 1 ; // check if the right sequence
uint8_t Index [ 20 ] ; if ( IndexNMEA ( Index , GSA ) < 17 ) return - 2 ; // index parameters and check the sum
FixMode = Read_Dec1 ( GSA [ Index [ 1 ] ] ) ; if ( FixMode < 0 ) FixMode = 0 ;
ReadPDOP ( GSA + Index [ 14 ] ) ;
ReadHDOP ( GSA + Index [ 15 ] ) ;
ReadVDOP ( GSA + Index [ 16 ] ) ;
return 1 ; }
int ReadRMC ( NMEA_RxMsg & RxMsg )
{ if ( RxMsg . Parms < 12 ) return - 1 ; // no less than 12 parameters
2018-02-18 14:41:50 +00:00
hasGPS = ReadTime ( ( const char * ) RxMsg . ParmPtr ( 0 ) ) > 0 ; // read time and check if same as the GGA says
2018-01-29 12:43:22 +00:00
if ( ReadDate ( ( const char * ) RxMsg . ParmPtr ( 8 ) ) < 0 ) setDefaultDate ( ) ; // date
ReadLatitude ( * RxMsg . ParmPtr ( 3 ) , ( const char * ) RxMsg . ParmPtr ( 2 ) ) ; // Latitude
ReadLongitude ( * RxMsg . ParmPtr ( 5 ) , ( const char * ) RxMsg . ParmPtr ( 4 ) ) ; // Longitude
ReadSpeed ( ( const char * ) RxMsg . ParmPtr ( 6 ) ) ; // Speed
ReadHeading ( ( const char * ) RxMsg . ParmPtr ( 7 ) ) ; // Heading
calcLatitudeCosine ( ) ;
return 1 ; }
int8_t ReadRMC ( const char * RMC )
{ if ( ( memcmp ( RMC , " $GPRMC " , 6 ) ! = 0 ) & & ( memcmp ( RMC , " $GNRMC " , 6 ) ! = 0 ) ) return - 1 ; // check if the right sequence
uint8_t Index [ 20 ] ; if ( IndexNMEA ( Index , RMC ) < 12 ) return - 2 ; // index parameters and check the sum
2018-02-18 14:41:50 +00:00
hasGPS = ReadTime ( RMC + Index [ 0 ] ) > 0 ;
2018-01-29 12:43:22 +00:00
if ( ReadDate ( RMC + Index [ 8 ] ) < 0 ) setDefaultDate ( ) ;
ReadLatitude ( RMC [ Index [ 3 ] ] , RMC + Index [ 2 ] ) ;
ReadLongitude ( RMC [ Index [ 5 ] ] , RMC + Index [ 4 ] ) ;
ReadSpeed ( RMC + Index [ 6 ] ) ;
ReadHeading ( RMC + Index [ 7 ] ) ;
calcLatitudeCosine ( ) ;
return 1 ; }
2018-02-18 14:41:50 +00:00
int16_t calcTimeDiff ( GPS_Position & RefPos ) const
{ int16_t TimeDiff = ( FracSec + ( int16_t ) Sec * 100 ) - ( RefPos . FracSec + ( int16_t ) RefPos . Sec * 100 ) ;
if ( TimeDiff < ( - 3000 ) ) TimeDiff + = 6000 ;
else if ( TimeDiff > = 3000 ) TimeDiff - = 6000 ;
return TimeDiff ; } // [0.01s]
int16_t calcDifferences ( GPS_Position & RefPos ) // calculate climb rate and turn rate with an earlier reference position
2018-01-29 12:43:22 +00:00
{ ClimbRate = 0 ; TurnRate = 0 ;
if ( RefPos . FixQuality = = 0 ) return 0 ;
2018-02-18 14:41:50 +00:00
int16_t TimeDiff = calcTimeDiff ( RefPos ) ;
if ( TimeDiff < 5 ) return 0 ;
2018-01-29 12:43:22 +00:00
TurnRate = Heading - RefPos . Heading ;
if ( TurnRate > 1800 ) TurnRate - = 3600 ; else if ( TurnRate < ( - 1800 ) ) TurnRate + = 3600 ;
2018-02-18 14:41:50 +00:00
ClimbRate = Altitude - RefPos . Altitude ;
if ( hasBaro & & RefPos . hasBaro & & ( abs ( Altitude - StdAltitude ) < 2500 ) )
2018-01-29 12:43:22 +00:00
{ ClimbRate = StdAltitude - RefPos . StdAltitude ; }
2018-02-18 14:41:50 +00:00
if ( TimeDiff = = 100 )
2018-01-29 12:43:22 +00:00
{ }
2018-02-18 14:41:50 +00:00
else if ( TimeDiff = = 200 )
2018-01-29 12:43:22 +00:00
{ ClimbRate = ( ClimbRate + 1 ) > > 1 ;
TurnRate = ( TurnRate + 1 ) > > 1 ; }
else
2018-02-18 14:41:50 +00:00
{ ClimbRate = ( ( int32_t ) ClimbRate * 100 ) / TimeDiff ;
TurnRate = ( ( int32_t ) TurnRate * 100 ) / TimeDiff ; }
return TimeDiff ; } // [0.01s]
void Write ( MAV_GPS_RAW_INT * MAV ) const
{ MAV - > time_usec = ( int64_t ) 1000000 * getUnixTime ( ) + 10000 * FracSec ;
MAV - > lat = ( ( int64_t ) 50 * Latitude + 1 ) / 3 ;
MAV - > lon = ( ( int64_t ) 50 * Longitude + 1 ) / 3 ;
MAV - > alt = 100 * Altitude ;
MAV - > vel = 10 * Speed ;
MAV - > cog = 10 * Heading ; ;
MAV - > fix_type = 1 + FixQuality ;
MAV - > eph = 10 * HDOP ;
MAV - > epv = 10 * VDOP ;
MAV - > satellites_visible = Satellites ; }
void Read ( const MAV_GPS_RAW_INT * MAV , uint64_t UnixTime_ms = 0 )
{ if ( UnixTime_ms ) setUnixTime_ms ( UnixTime_ms ) ;
Latitude = ( ( int64_t ) MAV - > lat * 3 + 25 ) / 50 ;
Longitude = ( ( int64_t ) MAV - > lon * 3 + 25 ) / 50 ;
Altitude = ( MAV - > alt + 50 ) / 100 ; // [0.1m]
Heading = ( MAV - > cog + 5 ) / 10 ; // [0.1deg]
Speed = ( MAV - > vel + 5 ) / 10 ; // [0.1m/s]
HDOP = ( MAV - > eph + 5 ) / 10 ;
VDOP = ( MAV - > epv + 5 ) / 10 ;
// GeoidSeparation = Parameters.GeoidSepar;
Satellites = MAV - > satellites_visible ;
FixMode = MAV - > fix_type - 1 ;
FixQuality = 1 ;
hasGPS = 1 ; }
void Read ( const MAV_GLOBAL_POSITION_INT * MAV , uint64_t UnixTime_ms = 0 )
{ if ( UnixTime_ms ) setUnixTime_ms ( UnixTime_ms ) ;
Latitude = ( ( int64_t ) MAV - > lat * 3 + 25 ) / 50 ;
Longitude = ( ( int64_t ) MAV - > lon * 3 + 25 ) / 50 ;
Altitude = ( MAV - > alt + 50 ) / 100 ; // [0.1m]
ClimbRate = - MAV - > vz / 10 ; // [0.1m/s]
Heading = ( uint32_t ) ( ( uint16_t ) IntAtan2 ( MAV - > vy , MAV - > vx ) * ( uint32_t ) 450 + 0x1000 ) > > 13 ; // [0.1degC]
Speed = IntSqrt ( ( int32_t ) MAV - > vx * MAV - > vx + ( int32_t ) MAV - > vy * MAV - > vy ) / 10 ; // [0.1m/s]
// GeoidSeparation = Parameters.GeoidSepar;
FixMode = 3 ;
FixQuality = 1 ;
hasGPS = 1 ; }
void Read ( const MAV_SCALED_PRESSURE * MAV , uint64_t UnixTime_ms = 0 )
{ if ( UnixTime_ms ) setUnixTime_ms ( UnixTime_ms ) ;
Pressure = 100 * 4 * MAV - > press_abs ;
Temperature = MAV - > temperature / 10 ;
hasBaro = 1 ; }
2018-01-29 12:43:22 +00:00
void Encode ( OGN_Packet & Packet ) const
2018-02-18 14:41:50 +00:00
{ Packet . Position . FixQuality = FixQuality < 3 ? FixQuality : 3 ; //
if ( ( FixQuality > 0 ) & & ( FixMode > = 2 ) ) Packet . Position . FixMode = FixMode - 2 ; //
2018-01-29 12:43:22 +00:00
else Packet . Position . FixMode = 0 ;
2018-02-18 14:41:50 +00:00
if ( PDOP > 0 ) Packet . EncodeDOP ( PDOP - 10 ) ; // encode PDOP from GSA
else Packet . EncodeDOP ( HDOP - 10 ) ; // or if no GSA: use HDOP
int8_t ShortTime = Sec ; // the 6-bit time field in the OGN packet
if ( FracSec > = 50 ) { ShortTime + = 1 ; if ( ShortTime > = 60 ) ShortTime - = 60 ; } // round to the closest full second
Packet . Position . Time = ShortTime ; // Time
Packet . EncodeLatitude ( Latitude ) ; // Latitude
Packet . EncodeLongitude ( Longitude ) ; // Longitude
Packet . EncodeSpeed ( Speed ) ; // Speed
Packet . EncodeHeading ( Heading ) ; // Heading = track-over-ground
Packet . EncodeClimbRate ( ClimbRate ) ; // Climb rate
Packet . EncodeTurnRate ( TurnRate ) ; // Turn rate
Packet . EncodeAltitude ( ( Altitude + 5 ) / 10 ) ; // Altitude
if ( hasBaro ) Packet . EncodeStdAltitude ( ( StdAltitude + 5 ) / 10 ) ; // Pressure altitude
else Packet . clrBaro ( ) ; //or no-baro if pressure sensor data not there
2018-01-29 12:43:22 +00:00
}
void EncodeStatus ( OGN_Packet & Packet ) const
{ Packet . Status . ReportType = 0 ;
int ShortTime = Sec ;
if ( FracSec > = 50 ) { ShortTime + = 1 ; if ( ShortTime > = 60 ) ShortTime - = 60 ; }
Packet . Status . Time = ShortTime ;
Packet . Status . FixQuality = FixQuality < 3 ? FixQuality : 3 ;
Packet . Status . Satellites = Satellites < 15 ? Satellites : 15 ;
Packet . EncodeAltitude ( ( Altitude + 5 ) / 10 ) ;
2018-02-18 14:41:50 +00:00
if ( hasBaro )
2018-01-29 12:43:22 +00:00
{ Packet . EncodeTemperature ( Temperature ) ;
Packet . Status . Pressure = ( Pressure + 16 ) > > 5 ; }
else
{ Packet . Status . Pressure = 0 ; }
Packet . Status . Humidity = 0 ;
}
// uint8_t getFreqPlan(void) const // get the frequency plan from Lat/Lon: 1 = Europe + Africa, 2 = USA/CAnada, 3 = Australia + South America, 4 = New Zeeland
// { if( (Longitude>=(-20*600000)) && (Longitude<=(60*600000)) ) return 1; // between -20 and 60 deg Lat => Europe + Africa: 868MHz band
// if( Latitude<(20*600000) ) // below 20deg latitude
// { if( ( Longitude>(164*600000)) && (Latitude<(-30*600000)) && (Latitude>(-48*600000)) ) return 4; // => New Zeeland
// return 3; } // => Australia + South America: upper half of 915MHz band
// return 2; } // => USA/Canada: full 915MHz band
2018-02-18 14:41:50 +00:00
void Encode ( OGN_Packet & Packet , int16_t dTime ) const // Encode position which is extrapolated by the given fraction of a second
{ Packet . Position . FixQuality = FixQuality < 3 ? FixQuality : 3 ; //
if ( ( FixQuality > 0 ) & & ( FixMode > = 2 ) ) Packet . Position . FixMode = FixMode - 2 ; //
else Packet . Position . FixMode = 0 ;
if ( PDOP > 0 ) Packet . EncodeDOP ( PDOP - 10 ) ; // encode PDOP from GSA
else Packet . EncodeDOP ( HDOP - 10 ) ; // or if no GSA: use HDOP
int32_t Lat , Lon , Alt ; int16_t Head ;
calcExtrapolation ( Lat , Lon , Alt , Head , dTime ) ;
int16_t ShortTime = Sec ; // the 6-bit time field in the OGN packet
dTime + = FracSec ;
while ( dTime > = 50 ) { dTime - = 100 ; ShortTime + + ; if ( ShortTime > = 60 ) ShortTime - = 60 ; }
while ( dTime < ( - 50 ) ) { dTime + = 100 ; ShortTime - - ; if ( ShortTime < 0 ) ShortTime + = 60 ; }
Packet . Position . Time = ShortTime ; // Time
Packet . EncodeLatitude ( Lat ) ; // Latitude
Packet . EncodeLongitude ( Lon ) ; // Longitude
Packet . EncodeSpeed ( Speed ) ; // Speed
Packet . EncodeHeading ( Head ) ; // Heading = track-over-ground
Packet . EncodeClimbRate ( ClimbRate ) ; // Climb rate
Packet . EncodeTurnRate ( TurnRate ) ; // Turn rate
Packet . EncodeAltitude ( ( Alt + 5 ) / 10 ) ; // Altitude
if ( hasBaro ) Packet . EncodeStdAltitude ( ( StdAltitude + ( Alt - Altitude ) + 5 ) / 10 ) ; // Pressure altitude
else Packet . clrBaro ( ) ; //or no-baro if pressure sensor data not there
}
void calcExtrapolation ( int32_t & Lat , int32_t & Lon , int32_t & Alt , int16_t & Head , int32_t dTime ) const // extrapolate GPS position by a fraction of a second
{ int16_t HeadAngle = ( ( int32_t ) Heading < < 12 ) / 225 ; // []
int16_t TurnAngle = ( ( ( dTime * TurnRate ) / 25 ) < < 9 ) / 225 ; // []
HeadAngle + = TurnAngle ;
int32_t LatSpeed = ( ( int32_t ) Speed * Icos ( HeadAngle ) ) > > 12 ; // [0.1m/s]
int32_t LonSpeed = ( ( int32_t ) Speed * Isin ( HeadAngle ) ) > > 12 ; // [0.1m/s]
Lat = Latitude + calcLatitudeExtrapolation ( dTime , LatSpeed ) ;
Lon = Longitude + calcLongitudeExtrapolation ( dTime , LonSpeed ) ;
Alt = Altitude + calcAltitudeExtrapolation ( dTime ) ;
Head = Heading + ( dTime * TurnRate ) / 100 ;
if ( Head < 0 ) Head + = 3600 ; else if ( Head > = 3600 ) Head - = 3600 ; }
int32_t calcAltitudeExtrapolation ( int32_t Time ) const // [0.01s]
{ return Time * ClimbRate / 100 ; } // [0.1m]
int32_t calcLatitudeExtrapolation ( int32_t Time , int32_t LatSpeed ) const // [0.01s]
{ return ( Time * LatSpeed * 177 ) > > 15 ; } // [0.1m]
int32_t calcLongitudeExtrapolation ( int32_t Time , int32_t LonSpeed ) const // [0.01s]
{ int16_t LatCosine = calcLatCosine ( calcLatAngle16 ( Latitude ) ) ;
return calcLongitudeExtrapolation ( Time , LonSpeed , LatCosine ) ; }
int32_t calcLongitudeExtrapolation ( int32_t Time , int32_t LonSpeed , int16_t LatCosine ) const // [0.01s]
{ return ( ( ( int32_t ) Time * LonSpeed * 177 ) > > 3 ) / LatCosine ; }
2018-01-29 12:43:22 +00:00
// static int32_t calcLatDistance(int32_t Lat1, int32_t Lat2) // [m] distance along latitude
// { return ((int64_t)(Lat2-Lat1)*0x2f684bda+0x80000000)>>32; }
// static int32_t calcLatAngle32(int32_t Lat) // convert latitude to 32-bit integer angle
// { return ((int64_t)Lat*2668799779u+0x4000000)>>27; }
2018-02-18 14:41:50 +00:00
static int16_t calcLatAngle16 ( int32_t Lat ) // convert latitude to 16-bit integer angle
2018-01-29 12:43:22 +00:00
{ return ( ( int64_t ) Lat * 1303125 + 0x80000000 ) > > 32 ; }
// static int32_t calcLatCosine(int32_t LatAngle) // calculate the cosine of the latitude 32-bit integer angle
// { return IntSine((uint32_t)(LatAngle+0x40000000)); }
// static int32_t calcLatCosine(int16_t LatAngle) // calculate the cosine of the latitude 16-bit integer angle
// { return IntSine((uint16_t)(LatAngle+0x4000)); }
static int16_t calcLatCosine ( int16_t LatAngle )
{ return Icos ( LatAngle ) ; }
// int32_t getLatDistance(int32_t RefLatitude) const // [m] distance along latitude
// { return calcLatDistance(RefLatitude, Latitude); }
// int32_t getLonDistance(int32_t RefLongitude) const // [m] distance along longitude
// { int32_t Dist = calcLatDistance(RefLongitude, Longitude); //
// int16_t LatAngle = calcLatAngle16(Latitude);
// int32_t LatCos = calcLatCosine(LatAngle);
// // printf("Latitude=%+d, LatAngle=%04X LatCos=%08X\n", Latitude, (uint16_t)LatAngle, LatCos);
// return ((int64_t)Dist*LatCos+0x40000000)>>31; } // distance corrected by the latitude cosine
void calcLatitudeCosine ( void )
{ int16_t LatAngle = calcLatAngle16 ( Latitude ) ;
LatitudeCosine = calcLatCosine ( LatAngle ) ; }
private :
int8_t ReadLatitude ( char Sign , const char * Value )
{ int8_t Deg = Read_Dec2 ( Value ) ; if ( Deg < 0 ) return - 1 ;
int8_t Min = Read_Dec2 ( Value + 2 ) ; if ( Min < 0 ) return - 1 ;
if ( Value [ 4 ] ! = ' . ' ) return - 1 ;
int16_t FracMin = Read_Dec4 ( Value + 5 ) ; if ( FracMin < 0 ) return - 1 ;
// printf("Latitude: %c %02d %02d %04d\n", Sign, Deg, Min, FracMin);
Latitude = ( int16_t ) Deg * 60 + Min ;
Latitude = Latitude * ( int32_t ) 10000 + FracMin ;
// printf("Latitude: %d\n", Latitude);
if ( Sign = = ' S ' ) Latitude = ( - Latitude ) ;
else if ( Sign ! = ' N ' ) return - 1 ;
// printf("Latitude: %d\n", Latitude);
return 0 ; } // Latitude units: 0.0001/60 deg
int8_t ReadLongitude ( char Sign , const char * Value )
{ int16_t Deg = Read_Dec3 ( Value ) ; if ( Deg < 0 ) return - 1 ;
int8_t Min = Read_Dec2 ( Value + 3 ) ; if ( Min < 0 ) return - 1 ;
if ( Value [ 5 ] ! = ' . ' ) return - 1 ;
int16_t FracMin = Read_Dec4 ( Value + 6 ) ; if ( FracMin < 0 ) return - 1 ;
Longitude = ( int16_t ) Deg * 60 + Min ;
Longitude = Longitude * ( int32_t ) 10000 + FracMin ;
if ( Sign = = ' W ' ) Longitude = ( - Longitude ) ;
else if ( Sign ! = ' E ' ) return - 1 ;
return 0 ; } // Longitude units: 0.0001/60 deg
int8_t ReadAltitude ( char Unit , const char * Value )
{ if ( Unit ! = ' M ' ) return - 1 ;
return Read_Float1 ( Altitude , Value ) ; } // Altitude units: 0.1 meter
int8_t ReadGeoidSepar ( char Unit , const char * Value )
{ if ( Unit ! = ' M ' ) return - 1 ;
return Read_Float1 ( GeoidSeparation , Value ) ; } // GeoidSepar units: 0.1 meter
int8_t ReadSpeed ( const char * Value )
{ int32_t Knots ;
if ( Read_Float1 ( Knots , Value ) < 1 ) return - 1 ; // Speed: 0.1 knots
Speed = ( 527 * Knots + 512 ) > > 10 ; return 0 ; } // convert speed to 0.1 meter/sec
int8_t ReadHeading ( const char * Value )
{ return Read_Float1 ( Heading , Value ) ; } // Heading units: 0.1 degree
int8_t ReadPDOP ( const char * Value )
{ int16_t DOP ;
if ( Read_Float1 ( DOP , Value ) < 1 ) return - 1 ;
if ( DOP < 10 ) DOP = 10 ;
else if ( DOP > 255 ) DOP = 255 ;
PDOP = DOP ; return 0 ; }
int ReadHDOP ( const char * Value )
{ int16_t DOP ;
if ( Read_Float1 ( DOP , Value ) < 1 ) return - 1 ;
if ( DOP < 10 ) DOP = 10 ;
else if ( DOP > 255 ) DOP = 255 ;
HDOP = DOP ; return 0 ; }
int ReadVDOP ( const char * Value )
{ int16_t DOP ;
if ( Read_Float1 ( DOP , Value ) < 1 ) return - 1 ;
if ( DOP < 10 ) DOP = 10 ;
else if ( DOP > 255 ) DOP = 255 ;
VDOP = DOP ; return 0 ; }
2018-02-18 14:41:50 +00:00
int8_t ReadTime ( const char * Value ) // read the Time field: HHMMSS.ss and check if it is a new one or the same one
2018-01-29 12:43:22 +00:00
{ int8_t Prev ; int8_t Same = 1 ;
Prev = Hour ;
2018-02-18 14:41:50 +00:00
Hour = Read_Dec2 ( Value ) ; if ( Hour < 0 ) return - 1 ; // read hour (two digits), return when invalid
2018-01-29 12:43:22 +00:00
if ( Prev ! = Hour ) Same = 0 ;
Prev = Min ;
2018-02-18 14:41:50 +00:00
Min = Read_Dec2 ( Value + 2 ) ; if ( Min < 0 ) return - 1 ; // read minute (two digits), return when invalid
2018-01-29 12:43:22 +00:00
if ( Prev ! = Min ) Same = 0 ;
Prev = Sec ;
2018-02-18 14:41:50 +00:00
Sec = Read_Dec2 ( Value + 4 ) ; if ( Sec < 0 ) return - 1 ; // read second (two digits), return when invalid
2018-01-29 12:43:22 +00:00
if ( Prev ! = Sec ) Same = 0 ;
Prev = FracSec ;
2018-02-18 14:41:50 +00:00
if ( Value [ 6 ] = = ' . ' ) // is there a fraction
{ FracSec = Read_Dec2 ( Value + 7 ) ; if ( FracSec < 0 ) return - 1 ; } // read the fraction, return when invalid
if ( Prev ! = FracSec ) Same = 0 ; // return 0 when time is valid but did not change
return Same ; } // return 1 when time did not change (both RMC and GGA were for same time)
int8_t ReadDate ( const char * Param ) // read the field DDMMYY
{ Day = Read_Dec2 ( Param ) ; if ( Day < 0 ) return - 1 ; // read calendar year (two digits - thus need to be extended to four)
Month = Read_Dec2 ( Param + 2 ) ; if ( Month < 0 ) return - 1 ; // read calendar month
Year = Read_Dec2 ( Param + 4 ) ; if ( Year < 0 ) return - 1 ; // read calendar day
return 0 ; } // return 0 when field valid and was read correctly
2018-01-29 12:43:22 +00:00
int8_t static IndexNMEA ( uint8_t Index [ 20 ] , const char * Seq ) // index parameters and verify the NMEA checksum
{ if ( Seq [ 0 ] ! = ' $ ' ) return - 1 ;
if ( Seq [ 6 ] ! = ' , ' ) return - 1 ;
uint8_t Check = Seq [ 1 ] ^ Seq [ 2 ] ^ Seq [ 3 ] ^ Seq [ 4 ] ^ Seq [ 5 ] ^ Seq [ 6 ] ;
Index [ 0 ] = 7 ; int8_t Params = 1 ; int8_t Ptr ;
for ( Ptr = 7 ; ; )
{ char ch = Seq [ Ptr + + ] ; if ( ch < ' ' ) return - 1 ;
if ( ch = = ' * ' ) break ;
Check ^ = ch ;
if ( ch = = ' , ' ) { Index [ Params + + ] = Ptr ; }
}
if ( Seq [ Ptr + + ] ! = HexDigit ( Check > > 4 ) ) { /* printf("H:%c:%c <=> %02X\n", Seq[Ptr-1],Seq[Ptr ], Check); */ return - 2 ; }
if ( Seq [ Ptr + + ] ! = HexDigit ( Check & 0x0F ) ) { /* printf("L:%c:%c <=> %02X\n", Seq[Ptr-2],Seq[Ptr-1], Check); */ return - 2 ; }
// printf("%s => [%d]\n", Seq, Params);
return Params ; }
public :
uint32_t getUnixTime ( void ) const // return the Unix timestamp (tested 2000-2037)
{ uint16_t Days = DaysSinceYear2000 ( ) + DaysSimce1jan ( ) ;
return Times60 ( Times60 ( Times24 ( ( uint32_t ) ( Days + 10957 ) ) ) ) + Times60 ( ( uint32_t ) ( Times60 ( ( uint16_t ) Hour ) + Min ) ) + Sec ; } // this appears to save about 100 bytes of code
// return (uint32_t)(Days+10957)*SecsPerDay + (uint32_t)Hour*SecsPerHour + (uint16_t)Min*SecsPerMin + Sec; } // compared to this line
uint32_t getFatTime ( void ) const // return timestamp in FAT format
{ uint16_t Date = ( ( uint16_t ) ( Year + 20 ) < < 9 ) | ( ( uint16_t ) Month < < 5 ) | Day ;
uint16_t Time = ( ( uint16_t ) Hour < < 11 ) | ( ( uint16_t ) Min < < 5 ) | ( Sec > > 1 ) ;
return ( ( uint32_t ) Date < < 16 ) | Time ; }
void setUnixTime ( uint32_t Time ) // works except for the 1.1.2000
{ uint32_t Days = Time / SecsPerDay ; // [day] since 1970
uint32_t DayTime = Time - Days * SecsPerDay ; // [sec] time-of-day
Hour = DayTime / SecsPerHour ; DayTime - = ( uint32_t ) Hour * SecsPerHour ; //
Min = DayTime / SecsPerMin ; DayTime - = ( uint16_t ) Min * SecsPerMin ;
Sec = DayTime ;
2018-02-18 14:41:50 +00:00
FracSec = 0 ;
2018-01-29 12:43:22 +00:00
Days - = 10957 + 1 ; // [day] since 2000 minus 1 day
Year = ( Days * 4 ) / ( ( 365 * 4 ) + 1 ) ; // [year] since 1970
Days - = 365 * Year + ( Year / 4 ) ;
Month = Days / 31 ;
Day = Days - ( uint16_t ) Month * 31 + 1 ; Month + + ;
uint32_t CheckTime = getUnixTime ( ) ;
if ( CheckTime < Time ) incrDate ( ( Time - CheckTime ) / SecsPerDay ) ;
2018-02-18 14:41:50 +00:00
hasTime = 1 ; }
void setUnixTime_ms ( uint64_t Time_ms )
{ uint32_t Time = Time_ms / 1000 ;
setUnixTime ( Time ) ;
FracSec = ( Time_ms - ( uint64_t ) Time * 1000 ) / 10 ; }
2018-01-29 12:43:22 +00:00
private :
static const uint32_t SecsPerMin = 60 ;
static const uint32_t SecsPerHour = 60 * 60 ;
static const uint32_t SecsPerDay = 24 * 60 * 60 ;
uint8_t isLeapYear ( void ) const { return ( Year & 3 ) = = 0 ; }
# ifdef __AVR__
int16_t DaysSimce1jan ( void ) const
{ static const uint8_t DaysDiff [ 12 ] PROGMEM = { 0 , 3 , 3 , 6 , 8 , 11 , 13 , 16 , 19 , 21 , 24 , 26 } ;
uint16_t Days = ( uint16_t ) ( Month - 1 ) * 28 + pgm_read_byte ( DaysDiff + ( Month - 1 ) ) + Day - 1 ;
if ( isLeapYear ( ) & & ( Month > 2 ) ) Days + + ;
return Days ; }
# else
int16_t DaysSimce1jan ( void ) const // 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
{ static const uint8_t DaysDiff [ 12 ] = { 0 , 3 , 3 , 6 , 8 , 11 , 13 , 16 , 19 , 21 , 24 , 26 } ;
uint16_t Days = ( uint16_t ) ( Month - 1 ) * 28 + DaysDiff [ Month - 1 ] + Day - 1 ;
if ( isLeapYear ( ) & & ( Month > 2 ) ) Days + + ;
return Days ; }
# endif
uint16_t DaysSinceYear2000 ( void ) const
{ uint16_t Days = 365 * Year ;
if ( Year > 0 ) Days + = ( ( Year - 1 ) > > 2 ) + 1 ;
return Days ; }
template < class Type >
static Type Times60 ( Type X ) { return ( ( X < < 4 ) - X ) < < 2 ; }
template < class Type >
static Type Times28 ( Type X ) { X + = ( X < < 1 ) + ( X < < 2 ) ; return X < < 2 ; }
template < class Type >
static Type Times24 ( Type X ) { X + = ( X < < 1 ) ; return X < < 3 ; }
} ;
# endif // of __OGN_H__