#ifndef __FANET_H__ #define __FANET_H__ #include #include #include #include #include "format.h" #include "intmath.h" // =============================================================================================== class FANET_Packet { public: union { uint8_t Flags; struct { uint8_t CR:3; // Coding rate used (RX) or to be used (TX) bool hasCRC:1; // CRC was there (RX) bool badCRC:1; // CRC was bad (RX) bool Done:1; } ; } ; uint8_t Len; // [bytes] packet length static const int MaxBytes = 40; uint8_t Byte[MaxBytes+2]; public: FANET_Packet() { Len=0; } uint8_t Dump(char *Out) { uint8_t Len=0; for(int Idx=0; IdxLen; Idx++) { Len+=Format_Hex(Out+Len, Byte[Idx]); } return Len; } bool ExtHeader(void) const { return Byte[0]&0x80; } // is there extended header ? bool Forward(void) const { return Byte[0]&0x40; } // forward flag uint8_t Type(void) const { return Byte[0]&0x3F; } // message type uint32_t getAddr(void) const { uint32_t Addr=Byte[1]; Addr<<=8; Addr|=Byte[3]; Addr<<=8; Addr|=Byte[2]; return Addr; } // 24-bit source address uint8_t getAddrPref(void) const { return Byte[1]; } uint8_t getAddrType(void) const // address-type based on prefix { uint8_t Pref=getAddrPref(); if(Pref==0x08 || Pref==0x11 || Pref==0x20 || Pref==0xDD || Pref==0xDE || Pref==0xDF) return 2; return 3; } void setAddress(uint32_t Addr) { setAddrPref(Addr>>16); setAddrLow(Addr); } // full 24-bit address void setAddrPref(uint8_t Prefix) { Byte[1]=Prefix; } // address prefix void setAddrLow(uint16_t Addr ) { Byte[2]=Addr; Byte[3]=Addr>>8; } // lower 16-bits of the address void setHeader(uint8_t Type) { Byte[0] = 0x40 | (Type&0x3F); } void setType(uint8_t Type) { Byte[0] = (Byte[0]&0xC0) | (Type&0x3F); } // packet-type: 1=air-position uint8_t ExtHeaderLen(void) const // length ot the extended header (zero in most cases) { if(!ExtHeader()) return 0; uint8_t Len=1; if(Byte[4]&0x20) Len+=3; // if Unicast if(Byte[4]&0x10) Len+=4; // if Signature return Len; } uint8_t MsgOfs(void) const { return 4+ExtHeaderLen(); } // offset to the actual message (past the header and ext. header) uint8_t MsgLen(void) const { return Len-4-ExtHeaderLen(); } // length of the actual message const uint8_t *Msg(void) const { return Byte+MsgOfs(); } // pointer to the message, past the header uint8_t *Msg(void) { return Byte+MsgOfs(); } void setName(const char *Name) { setHeader(2); uint8_t Ofs; for(Ofs=MsgOfs(); Ofs>7; Byte[0]=Lat; Byte[1]=Lat>>8; Byte[2]=Lat>>16; } static int32_t getLon(const uint8_t *Byte) // FANET cordic units { int32_t Longitude=Byte[2]; Longitude<<=8; Longitude|=Byte[1]; Longitude<<=8; Longitude|=Byte[0]; Longitude<<=8; return Longitude; } static void setLon(uint8_t *Byte, int32_t Lon) { Lon = (Lon+0x80)>>8; Byte[0]=Lon; Byte[1]=Lon>>8; Byte[2]=Lon>>16; } static uint16_t getSpeed(uint8_t Byte) // [0.5km/h] for Type=1 { if(Byte<0x80) return Byte; return (uint16_t)5*(Byte-0x80); } static void setSpeed(uint8_t *Byte, uint16_t Speed) { if(Speed>=128) { Speed=(Speed+2)/5; if(Speed>=0x80) Speed=0x7F; Speed|=0x80; } Byte[0]=Speed; } static int16_t getClimb(uint8_t Byte) // [0.1m/s] { int16_t Climb = Byte&0x3F; if(Byte&0x40) Climb|=0xFFC0; if(Byte&0x80) return Climb*5; return Climb; } static void setClimb(uint8_t *Byte, int16_t Climb) // [0.1m/s] { if(Climb>63) { Climb = (Climb+2)/5; if(Climb>63) Climb=63; Byte[0] = 0x80 | Climb; } else if(Climb<(-63)) { Climb = (Climb-2)/5; if(Climb<(-63)) Climb=(-63); Byte[0] = 0x80 | Climb; } else { Byte[0] = Climb&0x7F; } } static uint16_t getDir(uint8_t Byte) // [deg] { uint16_t Dir = ((uint16_t)45*Byte+0x10)>>5; return Dir; } static uint16_t getPressure(const uint8_t *Byte) // [0.1hPa] { uint16_t Press = Byte[1]; Press<<=8; Press|=Byte[0]; return Press+4300; } static int16_t getTurnRate(uint8_t Byte) // [0.25deg/s] { int16_t Rate = Byte&0x7F; if(Byte&0x40) Rate|=0xFF80; if(Byte&0x80) return Rate<<2; return Rate; } static void setTurnRate(uint8_t *Byte, int16_t Rate) { if(Rate>= 64 ) { if(Rate> 254 ) Rate= 254 ; Byte[0] = 0x80 | ((Rate+2)>>2); return; } if(Rate<(-64)) { if(Rate<(-254)) Rate=(-254); Byte[0] = 0x80 | (((Rate+2)>>2)&0x7F); return; } Byte[0] = Rate&0x7F; } static int16_t getQNE(uint8_t Byte) // [m] difference between pressure altitude and GPS altitude { int16_t QNE = Byte&0x7F; if(Byte&0x40) QNE|=0xFF80; if(Byte&0x80) return QNE<<2; return QNE; } static void setQNE(uint8_t *Byte, int16_t QNE) { if(QNE>= 64 ) { if(QNE> 254 ) QNE= 254 ; Byte[0] = 0x80 | ((QNE+2)>>2); return; } if(QNE<(-64)) { if(QNE<(-254)) QNE=(-254); Byte[0] = 0x80 | (((QNE+2)>>2)&0x7F); return; } Byte[0] = QNE&0x7F; } static uint16_t getAltitude(const uint8_t *Byte) // [m] { uint16_t Alt = Byte[1]; Alt<<=8; Alt|=Byte[0]; Alt&=0x0FFF; if(Alt<0x800) return Alt; return (Alt-0x800)<<2; } void setAltitude(uint8_t *Byte, uint16_t Alt) // [m] { if(Alt>=0x800) { Alt>>=2; if(Alt>0x800) Alt=0x800; Alt|=0x800; } Byte[0]=Alt; Byte[1] = (Byte[1]&0xF0) | (Alt>>8); } // [0..7] [0..1] [FANET cordic] [FANET cordic] [m] [cordic] [0.1m/s] [0.1m/s] [0.1deg/s] void setAirPos(uint8_t AcftType, uint8_t Track, int32_t Lat, int32_t Lon, int16_t Alt, uint8_t Dir, uint16_t Speed, int16_t Climb, int16_t Turn) { setHeader(1); uint8_t Ofs=MsgOfs(); Len=Ofs+12; setLat(Byte+Ofs, Lat); // [cordic] setLon(Byte+Ofs+3, Lon); // [cordic] Byte[Ofs+7]=(AcftType<<4) | (Track<<7); if(Alt<0) Alt=0; setAltitude(Byte+Ofs+6, Alt); // [m] Speed = (Speed*23+16)>>5; // [0.1m/s] => [0.5km/h] setSpeed(Byte+Ofs+8, Speed); // [0.5km/h] setClimb(Byte+Ofs+9, Climb); // [0.1m/s] Byte[Ofs+10] = Dir; // [cordic] setTurnRate(Byte+Ofs+11, Turn*2/5); } // [0.25deg/s] void setQNE(int32_t StdAltitude) // [m] only for air-position { uint8_t Ofs=MsgOfs(); int32_t Alt=getAltitude(Byte+Ofs+6); if(Len<(Ofs+13)) Len=Ofs+13; setQNE(Byte+Ofs+12, StdAltitude-Alt); } // [0..15] [0..1] [FANET cordic] [FANET cordic] void setGndPos(uint8_t Status, uint8_t Track, int32_t Lat, int32_t Lon) { setHeader(7); uint8_t Ofs=MsgOfs(); Len=11; setLat(Byte+Ofs, Lat); setLon(Byte+Ofs+3, Lon); Byte[Ofs+6] = (Status<<4) | Track; } /* * $FNNGB,manufacturer(hex),id(hex),name(up to 32bytes),type/status,latitude,longitude,altitude,climb,speed,heading*checksum * manufacturer: 1-2 chars hex * id: 1-4 chars hex * name: string up to 32 chars * type/status: while airborne: aircraft type: 0-7 (3D tracking), else: status: 0-15 (2D tracking) +10 -> 10-25 * latitude: %.5f in degree * longitude: %.5f in degree * altitude: %.f in meter, -1000 for ground * climb: %.1f in m/s * speed: %.1f in km/h * heading: %.f in degree * * for the types please see: https://github.com/3s1d/fanet-stm32/blob/master/Src/fanet/radio/protocol.txt * */ uint8_t WriteFNNGB(char *Out) { return 0; } int DecodePosition(float &Lat, float &Lon, int &Alt) { uint8_t Idx=MsgOfs(); if(Type()==1) { Lat = FloatCoord(getLat(Byte+Idx)); Lon = FloatCoord(getLon(Byte+Idx+3)); Alt = getAltitude(Byte+Idx+6); return 3; } if(Type()==7) { Lat = FloatCoord(getLat(Byte+Idx)); Lon = FloatCoord(getLon(Byte+Idx+3)); Alt = 0; return 2; } return 0; } void Print(const char *Name=0) const { if(Name) printf("%s ", Name); printf("[%2d:%d:%2d] FNT%06X", Len, Type(), MsgLen(), getAddr()); if(Type()==2) // Name { printf(" "); for(uint8_t Idx=MsgOfs(); Idx>4; int32_t Lat=getLat(Byte+Idx); // [FANET cordic] int32_t Lon=getLon(Byte+Idx+3); // [FANET cordic] uint16_t Alt=getAltitude(Byte+Idx+6); // [m] uint16_t Speed=getSpeed(Byte[Idx+8]); // [0.5km/h] int16_t Climb=getClimb(Byte[Idx+9]); // [0.1m/s] printf(" [%+09.5f,%+010.5f] %dm %3.1fkm/h %03.0fdeg %+4.1fm/s a%X%c", FloatCoord(Lat), FloatCoord(Lon), Alt, 0.5*Speed, (180.0/128)*Byte[Idx+10], 0.1*Climb, AcftType&0x07, AcftType&0x08?'T':'H'); if((Idx+11)>5)&0x0F, 2019+((Fw>>9)&0x3F), Fw&0x8000?'d':'r'); printf("\n"); return; } printf("\n"); } uint8_t Read(const char *Inp) // read packet from a hex dump { for( Len=0; Len>30; } // convert FANET-cordic to UBX 1e-7deg units // ((int64_t)900000000*Coord+0x20000000)>>30; // this is the exact formula, but FANET is not exact here static int Format_Lat(char *Str, int32_t Lat, char &HighRes) // format latitude after APRS { Lat = CoordUBX(Lat); // convert from FANET cordic to UBX 1e-7 deg char Sign; if(Lat>0) { Sign='N'; } else { Sign='S'; Lat=(-Lat); } int32_t Dig; Dig=Lat/100000000; (*Str++)='0'+Dig; Lat-=Dig*100000000; Dig=Lat/10000000; (*Str++)='0'+Dig; Lat-=Dig*10000000; Lat*=60; Dig=Lat/100000000; (*Str++)='0'+Dig; Lat-=Dig*100000000; Dig=Lat/10000000; (*Str++)='0'+Dig; Lat-=Dig*10000000; (*Str++)='.'; Dig=Lat/1000000; (*Str++)='0'+Dig; Lat-=Dig*1000000; Dig=Lat/100000; (*Str++)='0'+Dig; Lat-=Dig*100000; Dig=Lat/10000; HighRes ='0'+Dig; Lat-=Dig*10000; (*Str++)=Sign; return 8; } static int Format_Lon(char *Str, int32_t Lon, char &HighRes) // format longitude after APRS { Lon = CoordUBX(Lon); // convert from FANET cordic to UBX 1e-7 deg char Sign; if(Lon>0) { Sign='E'; } else { Sign='W'; Lon=(-Lon); } int32_t Dig; Dig=Lon/1000000000; (*Str++)='0'+Dig; Lon-=Dig*1000000000; Dig=Lon/100000000; (*Str++)='0'+Dig; Lon-=Dig*100000000; Dig=Lon/10000000; (*Str++)='0'+Dig; Lon-=Dig*10000000; Lon*=60; Dig=Lon/100000000; (*Str++)='0'+Dig; Lon-=Dig*100000000; Dig=Lon/10000000; (*Str++)='0'+Dig; Lon-=Dig*10000000; (*Str++)='.'; Dig=Lon/1000000; (*Str++)='0'+Dig; Lon-=Dig*1000000; Dig=Lon/100000; (*Str++)='0'+Dig; Lon-=Dig*100000; Dig=Lon/10000; HighRes ='0'+Dig; Lon-=Dig*10000; (*Str++)=Sign; return 9; } } ; class FANET_RxPacket: public FANET_Packet { public: uint32_t sTime; // [ s] reception time uint16_t msTime; // [ms] int16_t FreqOfs; // [ 10Hz] int8_t SNR; // [0.25dB] int8_t RSSI; // [dBm] uint8_t BitErr; // number of bit errors uint8_t CodeErr; // number of block errors uint8_t Sync; // sync symbols used: 0xF1 for sx127x but 0x12 for sx1262 ? public: void setTime(double RxTime) { sTime=floor(RxTime); msTime=floor(1000.0*(RxTime-sTime)); } double getTime(void) const { return (double)sTime+0.001*msTime; } uint32_t SlotTime(void) const { uint32_t Slot=sTime; if(msTime<100) Slot--; return Slot; } void Print(char *Name=0) const { char HHMMSS[8]; Format_HHMMSS(HHMMSS, SlotTime()); HHMMSS[6]='h'; HHMMSS[7]=0; printf("%s CR%c%c%c %3.1fdB/%de %+3.1fkHz ", HHMMSS, '0'+CR, hasCRC?'c':'_', badCRC?'-':'+', 0.25*SNR, BitErr, 1e-2*FreqOfs); FANET_Packet::Print(Name); } int PrintJSON(char *JSON, uint8_t AddrType=0) const { const uint8_t *Msg = this->Msg(); uint8_t MsgLen = this->MsgLen(); uint8_t Type = this->Type(); if(Type!=1 && Type!=7) { JSON[0]=0; return 0; } int Len=0; JSON[Len++]='{'; uint32_t Address = getAddr(); if(AddrType==0) AddrType = getAddrType(); Len+=Format_String(JSON+Len, "\"Address\":\""); Len+=Format_Hex(JSON+Len, Byte[1]); Len+=Format_Hex(JSON+Len, Byte[3]); Len+=Format_Hex(JSON+Len, Byte[2]); Len+=Format_String(JSON+Len, "\", \"AddrType\":"); JSON[Len++] = '0'+AddrType; Len+=Format_String(JSON+Len, "\", \"ID\":\""); Len+=Format_Hex(JSON+Len, Address | ((uint32_t)AddrType<<24)); uint32_t Time = SlotTime(); // sTime; if(msTime<100) Time--; Len+=Format_String(JSON+Len, "\", \"Time\":"); Len+=Format_UnsDec(JSON+Len, Time); if(Type==1) { const uint8_t OGNtype[8] = { 0, 7, 6, 0xB, 1, 8, 3, 0xD } ; // OGN aircraft types uint8_t AcftType=Msg[7]>>4; // get the aircraft-type and online-track flag Len+=Format_String(JSON+Len, ", \"AcftType\":"); Len+=Format_UnsDec(JSON+Len, OGNtype[AcftType&0x7]); uint32_t Alt=getAltitude(Msg+6); // [m] decode the altitude uint32_t Speed=getSpeed(Msg[8]); // [0.5km/h] ground speed Speed = (Speed*355+0x80)>>8; // [0.5km/h] => [0.1m/s] convert int32_t Climb=getClimb(Msg[9]); // [0.1m/s] climb rate uint16_t Dir=getDir(Msg[10]); // [deg] Len+=Format_String(JSON+Len, ", \"Alt\":"); Len+=Format_UnsDec(JSON+Len, Alt); Len+=Format_String(JSON+Len, ", \"Track\":"); Len+=Format_UnsDec(JSON+Len, Dir); Len+=Format_String(JSON+Len, ", \"Speed\":"); Len+=Format_UnsDec(JSON+Len, Speed, 2, 1); Len+=Format_String(JSON+Len, ", \"Climb\":"); Len+=Format_SignDec(JSON+Len, Climb, 2, 1, 1); if(MsgLen>11) { int16_t Turn=getTurnRate(Msg[11]); Len+=Format_String(JSON+Len, ", \"Turn\":"); Len+=Format_SignDec(JSON+Len, Turn*10/4, 2, 1, 1); } if(MsgLen>12) { int32_t AltStd=Alt; Alt+=getQNE(Msg[12]); Len+=Format_String(JSON+Len, ", \"StdAlt\":"); Len+=Format_SignDec(JSON+Len, AltStd, 1, 0, 1); } } if(Type==1 || Type==7) { int32_t Lat = getLat(Msg); // [cordic] decode the latitude int32_t Lon = getLon(Msg+3); // [cordic] decode the longitude Len+=Format_String(JSON+Len, ", \"Lat\":"); Len+=Format_SignDec(JSON+Len, CoordUBX(Lat), 8, 7, 1); Len+=Format_String(JSON+Len, ", \"Lon\":"); Len+=Format_SignDec(JSON+Len, CoordUBX(Lon), 8, 7, 1); } Len+=Format_String(JSON+Len, ", \"RxProt\":\"FNT\""); if(SNR>0) { Len+=Format_String(JSON+Len, ", \"RxSNR\":"); Len+=Format_SignDec(JSON+Len, ((int16_t)SNR*10+2-843)>>2, 2, 1, 1); } Len+=Format_String(JSON+Len, ", \"RxErr\":"); Len+=Format_UnsDec(JSON+Len, BitErr); Len+=Format_String(JSON+Len, ", \"RxFreqOfs\":"); Len+=Format_SignDec(JSON+Len, FreqOfs/10, 1, 1); JSON[Len++]=' '; JSON[Len++]='}'; JSON[Len]=0; return Len; } int WriteStxJSON(char *JSON, uint8_t AddrType=0) const { int Len=0; Len+=Format_String(JSON+Len, "\"addr\":\""); Len+=Format_Hex(JSON+Len, Byte[1]); Len+=Format_Hex(JSON+Len, Byte[3]); Len+=Format_Hex(JSON+Len, Byte[2]); JSON[Len++]='\"'; JSON[Len++]=','; Len+=Format_String(JSON+Len, "\"addr_type\":"); if(AddrType==0) AddrType = getAddrType(); JSON[Len++] = '0'+AddrType; const uint8_t *Msg = this->Msg(); uint8_t MsgLen = this->MsgLen(); uint8_t Type = this->Type(); uint32_t Time = SlotTime(); // sTime; if(msTime<100) Time--; Len+=Format_String(JSON+Len, ",\"time\":"); Len+=Format_UnsDec(JSON+Len, Time); int64_t RxTime=(int64_t)sTime-Time; RxTime*=1000; RxTime+=msTime; Len+=Format_String(JSON+Len, ",\"rx_time\":"); Len+=Format_SignDec(JSON+Len, RxTime, 4, 3, 1); if(Type==2) { Len+=Format_String(JSON+Len, ",\"Name\":\""); for(int Idx=0; Idx>4; // get the aircraft-type and online-track flag Len+=Format_String(JSON+Len, ",\"acft_type\":\""); JSON[Len++] = HexDigit(AcftType&0x7); JSON[Len++]='\"'; Len+=Format_String(JSON+Len, ",\"acft_cat\":\""); // GDL90 aircraft category // no-info, para-glider, hang-glider, balloon, glider, powered, heli, UAV const uint8_t AcftCat[8] = { 0, 12, 12, 10, 9, 1, 7, 14 } ; Len+=Format_Hex(JSON+Len, AcftCat[AcftType&0x07]); JSON[Len++]='\"'; Len+=Format_String(JSON+Len, ",\"no_track\":"); JSON[Len++]='0' + (AcftType>>3); } if(Type==4) // for service/weather { uint8_t Service=Msg[0]; // Len+=Format_String(JSON+Len, ",\"service\":\""); if(Service&0x80) JSON[Len++]='I'; if(Service&0x40) JSON[Len++]='T'; if(Service&0x20) JSON[Len++]='W'; if(Service&0x10) JSON[Len++]='H'; if(Service&0x08) JSON[Len++]='B'; // if(Service&0x04) JSON[Len++]='R'; if(Service&0x02) JSON[Len++]='C'; JSON[Len++]='\"'; int32_t Lat = getLat(Msg+1); // [cordic] decode the latitude int32_t Lon = getLon(Msg+4); // [cordic] decode the longitude Len+=Format_String(JSON+Len, ",\"lat_deg\":"); Len+=Format_SignDec(JSON+Len, CoordUBX(Lat), 8, 7, 1); Len+=Format_String(JSON+Len, ",\"lon_deg\":"); Len+=Format_SignDec(JSON+Len, CoordUBX(Lon), 8, 7, 1); int Idx=7; if(Service&0x40) { Len+=Format_String(JSON+Len, ",\"temp_deg\":"); Len+=Format_SignDec(JSON+Len, (int16_t)5*((int8_t)Msg[Idx++]), 2, 1, 1); } if(Service&0x20) { uint16_t Dir = Msg[Idx++]; // [cordic] Len+=Format_String(JSON+Len, ",\"wind_deg\":"); Len+=Format_UnsDec(JSON+Len, (45*Dir+16)>>5, 2, 1); uint16_t Wind = getSpeed(Msg[Idx++]); // [0.2km/h] Len+=Format_String(JSON+Len, ",\"wind_kmh\":"); Len+=Format_UnsDec(JSON+Len, 2*Wind, 2, 1); uint16_t Gust = getSpeed(Msg[Idx++]); Len+=Format_String(JSON+Len, ",\"gust_kmh\":"); Len+=Format_UnsDec(JSON+Len, 2*Gust, 2, 1); } if(Service&0x10) { Len+=Format_String(JSON+Len, ",\"hum_perc\":"); Len+=Format_UnsDec(JSON+Len, (uint16_t)4*Msg[Idx++], 2, 1); } if(Service&0x08) { Len+=Format_String(JSON+Len, ",\"press_hpa\":"); Len+=Format_UnsDec(JSON+Len, getPressure(Msg+Idx), 2, 1); Idx+=2; } } if(Type==1 || Type==7) // airborne or ground position { int32_t Lat = getLat(Msg); // [cordic] decode the latitude int32_t Lon = getLon(Msg+3); // [cordic] decode the longitude Len+=Format_String(JSON+Len, ",\"lat_deg\":"); Len+=Format_SignDec(JSON+Len, CoordUBX(Lat), 8, 7, 1); Len+=Format_String(JSON+Len, ",\"lon_deg\":"); Len+=Format_SignDec(JSON+Len, CoordUBX(Lon), 8, 7, 1); } if(Type==1) // for airpborne position { uint32_t Alt=getAltitude(Msg+6); // [m] decode the altitude uint32_t Speed=getSpeed(Msg[8]); // [0.5km/h] ground speed Speed = (Speed*355+0x80)>>8; // [0.5km/h] => [0.1m/s] convert int32_t Climb=getClimb(Msg[9]); // [0.1m/s] climb rate uint16_t Dir=getDir(Msg[10]); // [deg] Len+=Format_String(JSON+Len, ",\"alt_msl_m\":"); Len+=Format_UnsDec(JSON+Len, Alt); Len+=Format_String(JSON+Len, ",\"track_deg\":"); Len+=Format_UnsDec(JSON+Len, Dir); Len+=Format_String(JSON+Len, ",\"speed_mps\":"); Len+=Format_UnsDec(JSON+Len, Speed, 2, 1); Len+=Format_String(JSON+Len, ",\"climb_mps\":"); Len+=Format_SignDec(JSON+Len, Climb, 2, 1, 1); if(MsgLen>11) { int16_t Turn=getTurnRate(Msg[11]); Len+=Format_String(JSON+Len, ",\"turn_dps\":"); Len+=Format_SignDec(JSON+Len, Turn*10/4, 2, 1, 1); } if(MsgLen>12) { int32_t AltStd=Alt; Alt+=getQNE(Msg[12]); Len+=Format_String(JSON+Len, ",\"alt_std_m\":"); Len+=Format_SignDec(JSON+Len, AltStd, 1, 0, 1); } Len+=Format_String(JSON+Len, ",\"on_ground\":0"); } if(Type==7) // for ground position { uint8_t Status = Msg[6]; Len+=Format_String(JSON+Len, ",\"no_track\":1"); JSON[Len++]='0' + (Status&1); Len+=Format_String(JSON+Len, ",\"on_ground\":1"); } return Len; } int WriteAPRS(char *Out, uint8_t AddrType=0) { bool Report=0; if(AddrType==0) AddrType = getAddrType(); // 2 (FLARM) or 3 (OGN) int Len=0; bool isPosition = Type()==1 || Type()==4 || Type()==7; Len+=Format_String(Out+Len, "FNT"); Len+=Format_Hex(Out+Len, Byte[1]); Len+=Format_Hex(Out+Len, Byte[3]); Len+=Format_Hex(Out+Len, Byte[2]); Len+=Format_String(Out+Len, ">OGNFNT,qOR:"); Out[Len++]=isPosition?'/':'>'; Len+=Format_HHMMSS(Out+Len, SlotTime()); Out[Len++]='h'; const uint8_t *Msg = this->Msg(); uint8_t MsgLen = this->MsgLen(); switch(Type()) { case 2: // Name: pilot or weather station { Len+=Format_String(Out+Len, " Name=\""); for(int Idx=0; Idx>3, 3); // [0.2km/h -> mph] Out[Len++]='g'; Len+=Format_UnsDec(Out+Len, (Gust+4)>>3, 3); // [0.2km/h -> mph] } else Len+=Format_String(Out+Len, ".../...g..."); Out[Len++]='t'; if(Service&0x40) { // int16_t Fahr=Temp; Fahr+=4*Temp/5; Fahr/=2; Fahr+=32; // int16_t Fahr = (((int16_t)Temp*115+64)>>7) + 32; // [0.5degC] => [degF] if(Fahr>=0) Len+=Format_UnsDec(Out+Len, Fahr, 3); else Len+=Format_SignDec(Out+Len, Fahr, 2); } else Len+=Format_String(Out+Len, "..."); if(Service&0x10) { Out[Len++]='h'; Hum = (Hum*4+5)/10; if(Hum>=100) Hum=00; Len+=Format_UnsDec(Out+Len, Hum, 2); } if(Service&0x08) { Out[Len++]='b'; Len+=Format_UnsDec(Out+Len, Press, 5); } Report=Byte[1]!=0x06; break; } // don't report Burnair weather reports case 1: // airborne position { const char *AcftIcon[8] = { "/z", "/g", "/g", "/O", "/'", "\\^", "/X", "/'" } ; // APRS icons for aircraft types const uint8_t OGNtype[8] = { 0, 7, 6, 0xB, 1, 8, 3, 0xD } ; // OGN aircraft types uint8_t AcftType=Msg[7]>>4; // aircraft-type and online-tracking flag const char *Icon = AcftIcon[AcftType&7]; // APRS icon uint32_t ID = (OGNtype[AcftType&7]<<2) | AddrType; // acft-type and addr-type bool Track = AcftType&0x08; // online tracking flag if(!Track) ID|=0x80; // if no online tracking the set as stealth flag ID<<=24; ID |= getAddr(); // address int32_t Lat = getLat(Msg); // [cordic] int32_t Lon = getLon(Msg+3); // [cordic] uint32_t Alt=getAltitude(Msg+6); // [m] uint32_t Feet = ((int32_t)3360*Alt+512)>>10; // [feet] uint32_t Speed=getSpeed(Msg[8]); // [0.5km/h] uint32_t Knots=(Speed*553+1024)>>11; // knots int32_t Climb=getClimb(Msg[9]); // [0.1m/s] int32_t ClimbFeet = ((int32_t)1968*Climb+50)/100; // [fpm] uint16_t Dir=getDir(Msg[10]); // [deg] int16_t Turn=getQNE(Msg[11]); // [0.25deg/s] int16_t QNE=getQNE(Msg[12]); // [m] int32_t StdAlt=Alt+QNE; if(StdAlt<0) StdAlt=0; // [m] uint32_t StdFeet = ((int32_t)3360*StdAlt+512)>>10; // [feet] char hLat, hLon; Len+=Format_Lat(Out+Len, Lat, hLat); Out[Len++]=Icon[0]; Len+=Format_Lon(Out+Len, Lon, hLon); Out[Len++]=Icon[1]; Len+=Format_UnsDec(Out+Len, Dir, 3); Out[Len++]='/'; Len+=Format_UnsDec(Out+Len, Knots, 3); Len+=Format_String(Out+Len, "/A="); Len+=Format_UnsDec(Out+Len, Feet, 6); Len+=Format_String(Out+Len, " !W"); Out[Len++]=hLat; Out[Len++]=hLon; Out[Len++]='!'; Len+=Format_String(Out+Len, " id"); Len+=Format_Hex(Out+Len, ID); Out[Len++]=' '; Len+=Format_SignDec(Out+Len, ClimbFeet); Len+=Format_String(Out+Len, "fpm"); if(MsgLen>11) { Out[Len++]=' '; Len+=Format_SignDec(Out+Len, Turn*5/6, 2, 1); Len+=Format_String(Out+Len, "rot"); } if(MsgLen>12) { Len+=Format_String(Out+Len, " FL"); Len+=Format_UnsDec(Out+Len, StdFeet, 5, 2); } Len+=Format_String(Out+Len, " FNT1"); Out[Len++]='0'+(AcftType&7); Report=1; break; } case 7: // ground position { // const char *StatusMsg[16] = { 0, "Walking", "Vehicle", "Bike", "Boot", 0, 0, 0, // "Need-ride", "Landed-well", 0, 0, "Need-technical", // "Need-medical", "Distress(man)", "Distress(auto)" } ; uint8_t Status = Msg[6]; bool Track = Status&1; Status>>=4; const char *Icon = "\\n"; // static object if(Status>=13) Icon = "\\!"; // Emergency // const char *StatMsg = StatusMsg[Status]; uint8_t AcftType = 15; // uint32_t ID = (AcftType<<2) | AddrType; // acft-type and addr-type if(!Track) ID|=0x80; // stealth flag ID<<=24; ID |= getAddr(); // address int32_t Lat = getLat(Msg); // [cordic] int32_t Lon = getLon(Msg+3); // [cordic] char hLat, hLon; Len+=Format_Lat(Out+Len, Lat, hLat); Out[Len++]=Icon[0]; Len+=Format_Lon(Out+Len, Lon, hLon); Out[Len++]=Icon[1]; Len+=Format_String(Out+Len, " !W"); Out[Len++]=hLat; Out[Len++]=hLon; Out[Len++]='!'; Len+=Format_String(Out+Len, " id"); Len+=Format_Hex(Out+Len, ID); // if(StatMsg) // { Out[Len++]=' '; Len+=Format_String(Out+Len, StatMsg); } Len+=Format_String(Out+Len, " FNT7"); Out[Len++]=HexDigit(Status); Report=1; break; } } Out[Len++]=' '; Out[Len++]='s'; Len+=Format_Hex(Out+Len, Sync); if(SNR>0) { Out[Len++]=' '; Len+=Format_SignDec(Out+Len, ((int16_t)SNR*10+2-843)>>2, 2, 1, 1); Out[Len++]='d'; Out[Len++]='B'; } Out[Len++]=' '; Len+=Format_SignDec(Out+Len, FreqOfs/10, 2, 1); Len+=Format_String(Out+Len,"kHz"); if(BitErr) { Out[Len++]=' '; Len+=Format_UnsDec(Out+Len, BitErr); Out[Len++]='e'; } if(!Report) Len=0; // if not to be reported Out[Len]=0; return Len; } } ; // ========================================================================================= #ifndef ARDUINO class FANET_Name { public: static const int MaxSize = 32; uint32_t Time; // uint8_t Type; char Name[MaxSize]; public: FANET_Name() { Time=0; Name[0]=0; } int Copy(const uint8_t *Src, int Size) { if(Size>=MaxSize) Size=MaxSize-1; memcpy(Name, Src, Size); Name[Size]=0; return Size; } } ; #include class FANET_NameList { public: std::map List; public: int Update(FANET_RxPacket &Packet) { if(Packet.Type()!=2) return 0; uint32_t Addr = Packet.getAddr(); FANET_Name &Name = List[Addr]; Name.Time = Packet.SlotTime(); Name.Copy(Packet.Msg(), Packet.MsgLen()); return 1; } } ; #endif // =============================================================================================== #endif // __FANET_H__