From 54ca2c7fb2de006a09825e9c189cbe845762cc19 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Aug 2016 14:34:14 +0100 Subject: [PATCH] WIP big rewrite of message decoding / aircraft tracking. --- demod_2400.c | 1 - dump1090.c | 2 +- dump1090.h | 182 +++++---- interactive.c | 37 +- mode_ac.c | 15 +- mode_s.c | 1029 ++++++++++++++++++++++++++++--------------------- net_io.c | 443 ++++++++++++--------- track.c | 395 ++++++++++--------- track.h | 142 +++++-- 9 files changed, 1300 insertions(+), 946 deletions(-) diff --git a/demod_2400.c b/demod_2400.c index fd734b3..35b45bd 100644 --- a/demod_2400.c +++ b/demod_2400.c @@ -306,7 +306,6 @@ void demodulate2400(struct mag_buf *mag) normalize_timespec(&mm.sysTimestampMsg); mm.score = bestscore; - mm.bFlags = mm.correctedbits = 0; // Decode the received message { diff --git a/dump1090.c b/dump1090.c index 4da1ed8..25b3737 100644 --- a/dump1090.c +++ b/dump1090.c @@ -1019,7 +1019,7 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j],"--metric")) { Modes.metric = 1; } else if (!strcmp(argv[j],"--hae") || !strcmp(argv[j],"--gnss")) { - Modes.use_hae = 1; + Modes.use_gnss = 1; } else if (!strcmp(argv[j],"--aggressive")) { #ifdef ALLOW_AGGRESSIVE Modes.nfix_crc = MODES_MAX_BITERRORS; diff --git a/dump1090.h b/dump1090.h index 89ca13d..462f38c 100644 --- a/dump1090.h +++ b/dump1090.h @@ -2,7 +2,7 @@ // // dump1090.h: main program header // -// Copyright (c) 2014,2015 Oliver Jowett +// Copyright (c) 2014-2016 Oliver Jowett // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the @@ -135,40 +135,48 @@ typedef struct rtlsdr_dev rtlsdr_dev_t; #define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256) #define MODES_OUT_FLUSH_INTERVAL (60000) -#define MODES_UNIT_FEET 0 -#define MODES_UNIT_METERS 1 - #define MODES_USER_LATLON_VALID (1<<0) -#define MODES_ACFLAGS_LATLON_VALID (1<<0) // Aircraft Lat/Lon is decoded -#define MODES_ACFLAGS_ALTITUDE_VALID (1<<1) // Aircraft altitude is known -#define MODES_ACFLAGS_HEADING_VALID (1<<2) // Aircraft heading is known -#define MODES_ACFLAGS_SPEED_VALID (1<<3) // Aircraft speed is known -#define MODES_ACFLAGS_VERTRATE_VALID (1<<4) // Aircraft vertical rate is known -#define MODES_ACFLAGS_SQUAWK_VALID (1<<5) // Aircraft Mode A Squawk is known -#define MODES_ACFLAGS_CALLSIGN_VALID (1<<6) // Aircraft Callsign Identity -#define MODES_ACFLAGS_EWSPEED_VALID (1<<7) // Aircraft East West Speed is known -#define MODES_ACFLAGS_NSSPEED_VALID (1<<8) // Aircraft North South Speed is known -#define MODES_ACFLAGS_AOG (1<<9) // Aircraft is On the Ground -#define MODES_ACFLAGS_LLEVEN_VALID (1<<10) // Aircraft Even Lot/Lon is known -#define MODES_ACFLAGS_LLODD_VALID (1<<11) // Aircraft Odd Lot/Lon is known -#define MODES_ACFLAGS_AOG_VALID (1<<12) // MODES_ACFLAGS_AOG is valid -#define MODES_ACFLAGS_FS_VALID (1<<13) // Aircraft Flight Status is known -#define MODES_ACFLAGS_NSEWSPD_VALID (1<<14) // Aircraft EW and NS Speed is known -#define MODES_ACFLAGS_LATLON_REL_OK (1<<15) // Indicates it's OK to do a relative CPR -#define MODES_ACFLAGS_REL_CPR_USED (1<<16) // Lat/lon derived from relative CPR -#define MODES_ACFLAGS_CATEGORY_VALID (1<<17) // Aircraft category is known -#define MODES_ACFLAGS_FROM_MLAT (1<<18) // Data was derived from multilateration -#define MODES_ACFLAGS_ALTITUDE_HAE_VALID (1<<19) // altitude_hae is valid -#define MODES_ACFLAGS_HAE_DELTA_VALID (1<<20) // hae_delta is valid -#define MODES_ACFLAGS_FROM_TISB (1<<21) // Data was derived from TIS-B messages - -#define MODES_ACFLAGS_LLEITHER_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID) -#define MODES_ACFLAGS_LLBOTH_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID) -#define MODES_ACFLAGS_AOG_GROUND (MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG) - #define INVALID_ALTITUDE (-9999) +/* Where did a bit of data arrive from? In order of increasing priority */ +typedef enum { + SOURCE_INVALID, /* data is not valid */ + SOURCE_MLAT, /* derived from mlat */ + SOURCE_MODE_S, /* data from a Mode S message, no full CRC */ + SOURCE_MODE_S_CHECKED, /* data from a Mode S message with full CRC */ + SOURCE_TISB, /* data from a TIS-B extended squitter message */ + SOURCE_ADSB, /* data from a ADS-B extended squitter message */ +} datasource_t; + +typedef enum { + UNIT_FEET, + UNIT_METERS +} altitude_unit_t; + +typedef enum { + ALTITUDE_BARO, + ALTITUDE_GNSS +} altitude_source_t; + +typedef enum { + AG_INVALID, + AG_GROUND, + AG_AIRBORNE, + AG_UNCERTAIN +} airground_t; + +typedef enum { + SPEED_GROUNDSPEED, + SPEED_IAS, + SPEED_TAS +} speed_source_t; + +typedef enum { + HEADING_TRUE, + HEADING_MAGNETIC +} heading_source_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -310,7 +318,7 @@ struct { // Internal state int stats_range_histo; // Collect/show a range histogram? int onlyaddr; // Print only ICAO addresses int metric; // Use metric units - int use_hae; // Use HAE altitudes with H suffix when available + int use_gnss; // Use GNSS altitudes with H suffix ("HAE", though it isn't always) when available int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 char *json_dir; // Path to json base directory, or NULL not to write json. @@ -360,42 +368,86 @@ struct modesMessage { double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power int score; // Scoring from scoreModesMessage, if used - // DF 11, DF 17 - int ca; // Responder capabilities - int iid; - - // DF 17, DF 18 - int metype; // Extended squitter message type. - int mesub; // Extended squitter message subtype. - int heading; // Reported by aircraft, or computed from from EW and NS velocity - int raw_latitude; // Non decoded latitude. - int raw_longitude; // Non decoded longitude. - unsigned nuc_p; // NUCp value implied by message type - double fLat; // Coordinates obtained from CPR encoded data if/when decoded - double fLon; // Coordinates obtained from CPR encoded data if/when decoded - char flight[16]; // 8 chars flight number. - int ew_velocity; // E/W velocity. - int ns_velocity; // N/S velocity. - int vert_rate; // Vertical rate. - int velocity; // Reported by aircraft, or computed from from EW and NS velocity + datasource_t source; // Characterizes the overall message source + + // Raw data, just extracted directly from the message + // The names reflect the field names in Annex 4 + unsigned IID; // extracted from CRC of DF11s + unsigned AA; + unsigned AC; + unsigned CA; + unsigned CC; + unsigned CF; + unsigned DR; + unsigned FS; + unsigned ID; + unsigned KE; + unsigned ND; + unsigned RI; + unsigned SL; + unsigned UM; + unsigned VS; + unsigned char MB[7]; + unsigned char MD[10]; + unsigned char ME[7]; + unsigned char MV[7]; + + // Decoded data + unsigned altitude_valid : 1; + unsigned heading_valid : 1; + unsigned speed_valid : 1; + unsigned vert_rate_valid : 1; + unsigned squawk_valid : 1; + unsigned callsign_valid : 1; + unsigned ew_velocity_valid : 1; + unsigned ns_velocity_valid : 1; + unsigned cpr_valid : 1; + unsigned cpr_odd : 1; + unsigned cpr_decoded : 1; + unsigned cpr_relative : 1; + unsigned category_valid : 1; + unsigned gnss_delta_valid : 1; + unsigned from_mlat : 1; + unsigned from_tisb : 1; + unsigned spi_valid : 1; + unsigned spi : 1; + unsigned alert_valid : 1; + unsigned alert : 1; + + unsigned metype; // DF17/18 ME type + unsigned mesub; // DF17/18 ME subtype + + // valid if altitude_valid: + int altitude; // Altitude in either feet or meters + altitude_unit_t altitude_unit; // the unit used for altitude + altitude_source_t altitude_source; // whether the altitude is a barometric altude or a GNSS height + // valid if gnss_delta_valid: + int gnss_delta; // difference between GNSS and baro alt + // valid if heading_valid: + unsigned heading; // Reported by aircraft, or computed from from EW and NS velocity + heading_source_t heading_source; // what "heading" is measuring (true or magnetic heading) + // valid if speed_valid: + unsigned speed; // in kts, reported by aircraft, or computed from from EW and NS velocity + speed_source_t speed_source; // what "speed" is measuring (groundspeed / IAS / TAS) + // valid if vert_rate_valid: + int vert_rate; // vertical rate in feet/minute + altitude_source_t vert_rate_source; // the altitude source used for vert_rate + // valid if squawk_valid: + unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits + // valid if callsign_valid + char callsign[9]; // 8 chars flight number + // valid if category_valid unsigned category; // A0 - D7 encoded as a single hex byte - int altitude_hae; // altitude reported as GNSS HAE - int hae_delta; // difference between HAE and baro alt - - // DF 18 - int cf; // Control Field - - // DF4, DF5, DF20, DF21 - int fs; // Flight status for DF4,5,20,21 - int modeA; // 13 bits identity (Squawk). + // valid if cpr_valid + unsigned cpr_lat; // Non decoded latitude. + unsigned cpr_lon; // Non decoded longitude. + unsigned cpr_nucp; // NUCp/NIC value implied by message type - // DF20, DF21 - int bds; // BDS value implied if overlay control was used + airground_t airground; // air/ground state - // Fields used by multiple message types. - int altitude; - int unit; - int bFlags; // Flags related to fields in this structure + // valid if cpr_decoded: + double decoded_lat; + double decoded_lon; }; // This one needs modesMessage: diff --git a/interactive.c b/interactive.c index 41f4e64..98e4c3f 100644 --- a/interactive.c +++ b/interactive.c @@ -122,25 +122,28 @@ void interactiveShowData(void) { char strTt[5] = " "; char strGs[5] = " "; - if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - snprintf(strSquawk,5,"%04x", a->modeA);} + if (trackDataValid(&a->squawk_valid)) { + snprintf(strSquawk,5,"%04x", a->squawk); + } - if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) { - snprintf (strGs, 5,"%3d", convert_speed(a->speed));} + if (trackDataValid(&a->speed_valid)) { + snprintf (strGs, 5,"%3d", convert_speed(a->speed)); + } - if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { - snprintf (strTt, 5,"%03d", a->track);} + if (trackDataValid(&a->heading_valid)) { + snprintf (strTt, 5,"%03d", a->heading); + } if (msgs > 99999) { - msgs = 99999;} + msgs = 99999; + } if (Modes.interactive_rtl1090) { // RTL1090 display mode - - if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - snprintf(strFl,6,"F%03d",(a->altitude/100)); + if (trackDataValid(&a->altitude_valid)) { + snprintf(strFl,6,"F%03d",((a->altitude+50)/100)); } printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2.0f\n", - a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (now - a->seen)/1000.0); + a->addr, a->callsign, strFl, strGs, strTt, strSquawk, msgs, (now - a->seen)/1000.0); } else { // Dump1090 display mode char strMode[5] = " "; @@ -158,22 +161,22 @@ void interactiveShowData(void) { if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';} if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';} - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { + if (trackDataValid(&a->position_valid)) { snprintf(strLat, 8,"%7.03f", a->lat); snprintf(strLon, 9,"%8.03f", a->lon); } - if (a->bFlags & MODES_ACFLAGS_AOG) { + if (trackDataValid(&a->airground_valid) && a->airground == AG_GROUND) { snprintf(strFl, 7," grnd"); - } else if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) { - snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_hae)); - } else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + } else if (Modes.use_gnss && trackDataValid(&a->altitude_gnss_valid)) { + snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_gnss)); + } else if (trackDataValid(&a->altitude_valid)) { snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude)); } printf("%s%06X %-4s %-4s %-8s %6s %3s %3s %7s %8s %5.1f %5d %2.0f\n", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : " ", (a->addr & 0xffffff), - strMode, strSquawk, a->flight, strFl, strGs, strTt, + strMode, strSquawk, a->callsign, strFl, strGs, strTt, strLat, strLon, 10 * log10(signalAverage), msgs, (now - a->seen)/1000.0); } count++; diff --git a/mode_ac.c b/mode_ac.c index 02ad281..f1af6c5 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -89,18 +89,21 @@ void decodeModeAMessage(struct modesMessage *mm, int ModeA) mm->addr = (ModeA & 0x0000FF7F) | MODES_NON_ICAO_ADDRESS; // Set the Identity field to ModeA - mm->modeA = ModeA & 0x7777; - mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; + mm->squawk = ModeA & 0x7777; + mm->squawk_valid = 1; // Flag ident in flight status - mm->fs = ModeA & 0x0080; + mm->spi = (ModeA & 0x0080) ? 1 : 0; + mm->spi_valid = 1; // Decode an altitude if this looks like a possible mode C - if (!mm->fs) { + if (!mm->spi) { int modeC = ModeAToModeC(ModeA); - if (modeC >= -12) { + if (modeC != INVALID_ALTITUDE) { mm->altitude = modeC * 100; - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + mm->altitude_unit = UNIT_FEET; + mm->altitude_source = ALTITUDE_BARO; + mm->altitude_valid = 1; } } diff --git a/mode_s.c b/mode_s.c index 8e9e355..6cc94c1 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2,7 +2,7 @@ // // mode_s.c: Mode S message decoding. // -// Copyright (c) 2014,2015 Oliver Jowett +// Copyright (c) 2014-2016 Oliver Jowett // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the @@ -115,14 +115,14 @@ static int decodeID13Field(int ID13Field) { //========================================================================= // // Decode the 13 bit AC altitude field (in DF 20 and others). -// Returns the altitude, and set 'unit' to either MODES_UNIT_METERS or MDOES_UNIT_FEETS. +// Returns the altitude, and set 'unit' to either UNIT_METERS or UNIT_FEET. // -static int decodeAC13Field(int AC13Field, int *unit) { +static int decodeAC13Field(int AC13Field, altitude_unit_t *unit) { int m_bit = AC13Field & 0x0040; // set = meters, clear = feet int q_bit = AC13Field & 0x0010; // set = 25 ft encoding, clear = Gillham Mode C encoding if (!m_bit) { - *unit = MODES_UNIT_FEET; + *unit = UNIT_FEET; if (q_bit) { // N is the 11 bit integer resulting from the removal of bit Q and M int n = ((AC13Field & 0x1F80) >> 2) | @@ -140,20 +140,21 @@ static int decodeAC13Field(int AC13Field, int *unit) { return (100 * n); } } else { - *unit = MODES_UNIT_METERS; + *unit = UNIT_METERS; // TODO: Implement altitude when meter unit is selected return INVALID_ALTITUDE; } } + // //========================================================================= // // Decode the 12 bit AC altitude field (in DF 17 and others). // -static int decodeAC12Field(int AC12Field, int *unit) { +static int decodeAC12Field(int AC12Field, altitude_unit_t *unit) { int q_bit = AC12Field & 0x10; // Bit 48 = Q - *unit = MODES_UNIT_FEET; + *unit = UNIT_FEET; if (q_bit) { /// N is the 11 bit integer resulting from the removal of bit Q at bit 4 int n = ((AC12Field & 0x0FE0) >> 1) | @@ -172,6 +173,7 @@ static int decodeAC12Field(int AC12Field, int *unit) { return (100 * n); } } + // //========================================================================= // @@ -193,90 +195,6 @@ static int decodeMovementField(int movement) { return (gspeed); } -// -//========================================================================= -// -// Capability table -static const char *ca_str[8] = { - /* 0 */ "Level 1", - /* 1 */ "reserved", - /* 2 */ "reserved", - /* 3 */ "reserved", - /* 4 */ "Level 2+, ground", - /* 5 */ "Level 2+, airborne", - /* 6 */ "Level 2+", - /* 7 */ "DR/Alert/SPI active" -}; - -// DF 18 Control field table. -static const char *cf_str[8] = { - /* 0 */ "ADS-B ES/NT device with ICAO 24-bit address", - /* 1 */ "ADS-B ES/NT device with other address", - /* 2 */ "Fine format TIS-B", - /* 3 */ "Coarse format TIS-B", - /* 4 */ "TIS-B management message", - /* 5 */ "TIS-B relay of ADS-B message with other address", - /* 6 */ "ADS-B rebroadcast using DF-17 message format", - /* 7 */ "Reserved" -}; - -// Flight status table -static const char *fs_str[8] = { - /* 0 */ "Normal, Airborne", - /* 1 */ "Normal, On the ground", - /* 2 */ "ALERT, Airborne", - /* 3 */ "ALERT, On the ground", - /* 4 */ "ALERT & Special Position Identification. Airborne or Ground", - /* 5 */ "Special Position Identification. Airborne or Ground", - /* 6 */ "Reserved", - /* 7 */ "Not assigned" -}; - -// Emergency state table -// from https://www.ll.mit.edu/mission/aviation/publications/publication-files/atc-reports/Grappel_2007_ATC-334_WW-15318.pdf -// and 1090-DO-260B_FRAC -char *es_str[8] = { - /* 0 */ "No emergency", - /* 1 */ "General emergency (squawk 7700)", - /* 2 */ "Lifeguard/Medical", - /* 3 */ "Minimum fuel", - /* 4 */ "No communications (squawk 7600)", - /* 5 */ "Unlawful interference (squawk 7500)", - /* 6 */ "Reserved", - /* 7 */ "Reserved" -}; -// -//========================================================================= -// -static char *getMEDescription(int metype, int mesub) { - char *mename = "Unknown"; - - if (metype >= 1 && metype <= 4) - mename = "Aircraft Identification and Category"; - else if (metype >= 5 && metype <= 8) - mename = "Surface Position"; - else if (metype >= 9 && metype <= 18) - mename = "Airborne Position (Baro Altitude)"; - else if (metype == 19 && mesub >=1 && mesub <= 4) - mename = "Airborne Velocity"; - else if (metype >= 20 && metype <= 22) - mename = "Airborne Position (GNSS Height)"; - else if (metype == 23 && mesub == 0) - mename = "Test Message"; - else if (metype == 23 && mesub == 7) - mename = "Test Message -- Squawk"; - else if (metype == 24 && mesub == 1) - mename = "Surface System Status"; - else if (metype == 28 && mesub == 1) - mename = "Extended Squitter Aircraft Status (Emergency)"; - else if (metype == 28 && mesub == 2) - mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)"; - else if (metype == 29 && (mesub == 0 || mesub == 1)) - mename = "Target State and Status Message"; - else if (metype == 31 && (mesub == 0 || mesub == 1)) - mename = "Aircraft Operational Status Message"; - return mename; -} // Correct a decoded native-endian Address Announced field // (from bits 8-31) if it is affected by the given error @@ -480,6 +398,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) if (!icaoFilterTest(mm->crc)) { return -1; } + mm->source = SOURCE_MODE_S; mm->addr = mm->crc; break; @@ -490,7 +409,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // however! CL + IC only occupy the lower 7 bits of the CRC. So if we ignore those bits when testing // the CRC we can still try to detect/correct errors. - mm->iid = mm->crc & 0x7f; + mm->IID = mm->crc & 0x7f; if (mm->crc & 0xffff80) { int addr; struct errorinfo *ei = modesChecksumDiagnose(mm->crc & 0xffff80, mm->msgbits); @@ -515,6 +434,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) return -1; } } + mm->source = SOURCE_MODE_S_CHECKED; break; case 17: // Extended squitter @@ -524,25 +444,25 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // These message types use Parity/Interrogator, but are specified to set II=0 - if (mm->crc == 0) - break; // all good - - ei = modesChecksumDiagnose(mm->crc, mm->msgbits); - if (!ei) { - return -2; // couldn't fix it - } + if (mm->crc != 0) { + ei = modesChecksumDiagnose(mm->crc, mm->msgbits); + if (!ei) { + return -2; // couldn't fix it + } - addr1 = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); - mm->correctedbits = ei->errors; - modesChecksumFix(msg, ei); - addr2 = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + addr1 = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->correctedbits = ei->errors; + modesChecksumFix(msg, ei); + addr2 = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); - // we are conservative here: only accept corrected messages that - // match an existing aircraft. - if (addr1 != addr2 && !icaoFilterTest(addr2)) { - return -1; + // we are conservative here: only accept corrected messages that + // match an existing aircraft. + if (addr1 != addr2 && !icaoFilterTest(addr2)) { + return -1; + } } + mm->source = SOURCE_ADSB; // TIS-B decoding will override this if needed break; } @@ -556,22 +476,12 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // Try an exact match if (icaoFilterTest(mm->crc)) { // OK. + mm->source = SOURCE_MODE_S; mm->addr = mm->crc; - mm->bds = 0; // unknown break; } -#if 0 - // This doesn't seem useful, as we mistake a lot of CRC errors - // for overlay control - - // Try a fuzzy match - if ( (mm->addr = icaoFilterTestFuzzy(mm->crc)) != 0) { - // We have an address that would match, assume it's correct - mm->bds = (mm->crc ^ mm->addr) >> 16; // derive the BDS value based on what we think the address is - break; - } -#endif + // BDS / overlay control just doesn't work out. return -1; // no good @@ -582,23 +492,19 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // decode the bulk of the message - mm->bFlags = 0; - - if (mm->remote && mm->timestampMsg == MAGIC_MLAT_TIMESTAMP) - mm->bFlags |= MODES_ACFLAGS_FROM_MLAT; - // AA (Address announced) if (mm->msgtype == 11 || mm->msgtype == 17 || mm->msgtype == 18) { - mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->AA = mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); } // AC (Altitude Code) if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 16 || mm->msgtype == 20) { - int AC13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; - if (AC13Field) { // Only attempt to decode if a valid (non zero) altitude is present - mm->altitude = decodeAC13Field(AC13Field, &mm->unit); + mm->AC = ((msg[2] << 8) | msg[3]) & 0x1FFF; + if (mm->AC) { // Only attempt to decode if a valid (non zero) altitude is present + mm->altitude = decodeAC13Field(mm->AC, &mm->altitude_unit); if (mm->altitude != INVALID_ALTITUDE) - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; + mm->altitude_valid = 1; + mm->altitude_source = ALTITUDE_BARO; } } @@ -606,73 +512,146 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // CA (Capability) if (mm->msgtype == 11 || mm->msgtype == 17) { - mm->ca = (msg[0] & 0x07); - if (mm->ca == 4) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; - } else if (mm->ca == 5) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; + mm->CA = (msg[0] & 0x07); + + switch (mm->CA) { + case 0: + mm->airground = AG_UNCERTAIN; + break; + case 4: + mm->airground = AG_GROUND; + break; + case 5: + mm->airground = AG_AIRBORNE; + break; + case 6: + mm->airground = AG_UNCERTAIN; + break; + case 7: + mm->airground = AG_UNCERTAIN; + break; } } - // CC (Cross-link capability) not decoded + // CC (Cross-link capability) + if (mm->msgtype == 0) { + mm->CC = (msg[0] & 0x02) ? 1 : 0; + } // CF (Control field) if (mm->msgtype == 18) { - mm->cf = msg[0] & 7; + mm->CF = msg[0] & 7; } - // DR (Downlink Request) not decoded + // DR (Downlink Request) + if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 20 || mm->msgtype == 21) { + mm->DR = (msg[1] >> 3) & 0x1F; + } // FS (Flight Status) if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 20 || mm->msgtype == 21) { - mm->bFlags |= MODES_ACFLAGS_FS_VALID; - mm->fs = msg[0] & 7; - if (mm->fs <= 3) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - if (mm->fs & 1) - mm->bFlags |= MODES_ACFLAGS_AOG; + mm->FS = msg[0] & 7; + mm->alert_valid = 1; + mm->spi_valid = 1; + + switch (mm->FS) { + case 0: + mm->airground = AG_UNCERTAIN; + break; + case 1: + mm->airground = AG_GROUND; + break; + case 2: + mm->airground = AG_UNCERTAIN; + mm->alert = 1; + break; + case 3: + mm->airground = AG_GROUND; + mm->alert = 1; + break; + case 4: + mm->airground = AG_UNCERTAIN; + mm->alert = 1; + mm->spi = 1; + break; + case 5: + mm->airground = AG_UNCERTAIN; + mm->spi = 1; + break; + default: + mm->spi_valid = 0; + mm->alert_valid = 0; + break; } } // ID (Identity) if (mm->msgtype == 5 || mm->msgtype == 21) { // Gillham encoded Squawk - int ID13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF; - if (ID13Field) { - mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; - mm->modeA = decodeID13Field(ID13Field); + mm->ID = ((msg[2] << 8) | msg[3]) & 0x1FFF; + if (mm->ID) { + mm->squawk = decodeID13Field(mm->ID); + mm->squawk_valid = 1; } } - // KE (Control, ELM) not decoded + // KE (Control, ELM) + if (mm->msgtype >= 24 && mm->msgtype <= 31) { + mm->KE = (msg[0] & 0x10) ? 1 : 0; + } // MB (messsage, Comm-B) if (mm->msgtype == 20 || mm->msgtype == 21) { + memcpy(mm->MB, &msg[4], 7); decodeCommB(mm); } - // MD (message, Comm-D) not decoded + // MD (message, Comm-D) + if (mm->msgtype >= 24 && mm->msgtype <= 31) { + memcpy(mm->MD, &msg[1], 10); + } // ME (message, extended squitter) - if (mm->msgtype == 17 || // Extended squitter - mm->msgtype == 18) { // Extended squitter/non-transponder: + if (mm->msgtype == 17 || mm->msgtype == 18) { + memcpy(mm->ME, &msg[4], 7); decodeExtendedSquitter(mm); } - // MV (message, ACAS) not decoded - // ND (number of D-segment) not decoded - // RI (Reply information) not decoded - // SL (Sensitivity level, ACAS) not decoded - // UM (Utility Message) not decoded + // MV (message, ACAS) + if (mm->msgtype == 16) { + memcpy(mm->MV, &msg[4], 7); + } + + // ND (number of D-segment, Comm-D) + if (mm->msgtype >= 24 && mm->msgtype <= 31) { + mm->ND = msg[0] & 0x0F; + } + + // RI (Reply information, ACAS) + if (mm->msgtype == 0 || mm->msgtype == 16) { + mm->RI = ((msg[2] & 0x07) << 1) | ((msg[3] >> 7) & 0x01); + } + + // SL (Sensitivity level, ACAS) + if (mm->msgtype == 0 || mm->msgtype == 16) { + mm->SL = (msg[1] >> 5) & 0x07; + } + + // UM (Utility Message) + if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 20 || mm->msgtype == 21) { + mm->UM = ((msg[1] & 0x07) << 3) | ((msg[2] >> 5) & 0x07); + } // VS (Vertical Status) if (mm->msgtype == 0 || mm->msgtype == 16) { - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - if (msg[0] & 0x04) - mm->bFlags |= MODES_ACFLAGS_AOG; + mm->VS = (msg[0] & 0x04) ? 1 : 0; + if (mm->VS) + mm->airground = AG_GROUND; + else + mm->airground = AG_UNCERTAIN; } - if (!mm->correctedbits && (mm->msgtype == 17 || mm->msgtype == 18 || (mm->msgtype == 11 && mm->iid == 0))) { + if (!mm->correctedbits && (mm->msgtype == 17 || mm->msgtype == 18 || (mm->msgtype == 11 && mm->IID == 0))) { // No CRC errors seen, and either it was an DF17/18 extended squitter // or a DF11 acquisition squitter with II = 0. We probably have the right address. @@ -683,6 +662,10 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) icaoFilterAdd(mm->addr); } + // MLAT overrides all other sources + if (mm->remote && mm->timestampMsg == MAGIC_MLAT_TIMESTAMP) + mm->source = SOURCE_MLAT; + // all done return 0; } @@ -701,19 +684,19 @@ static void decodeBDS20(struct modesMessage *mm) if (chars1 == 0 && chars2 == 0) return; - mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; + mm->callsign_valid = 1; - mm->flight[3] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; - mm->flight[2] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; - mm->flight[1] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; - mm->flight[0] = ais_charset[chars1 & 0x3F]; + mm->callsign[3] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; + mm->callsign[2] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; + mm->callsign[1] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; + mm->callsign[0] = ais_charset[chars1 & 0x3F]; - mm->flight[7] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; - mm->flight[6] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; - mm->flight[5] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; - mm->flight[4] = ais_charset[chars2 & 0x3F]; + mm->callsign[7] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; + mm->callsign[6] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; + mm->callsign[5] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; + mm->callsign[4] = ais_charset[chars2 & 0x3F]; - mm->flight[8] = '\0'; + mm->callsign[8] = '\0'; } static void decodeExtendedSquitter(struct modesMessage *mm) @@ -726,7 +709,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // Check CF on DF18 to work out the format of the ES and whether we need to look for an IMF bit if (mm->msgtype == 18) { - switch (mm->cf) { + switch (mm->CF) { case 0: // ADS-B ES/NT devices that report the ICAO 24-bit address in the AA field break; @@ -735,20 +718,20 @@ static void decodeExtendedSquitter(struct modesMessage *mm) break; case 2: // Fine TIS-B message (formats are close enough to DF17 for our purposes) - mm->bFlags |= MODES_ACFLAGS_FROM_TISB; + mm->source = SOURCE_TISB; check_imf = 1; break; case 3: // Coarse TIS-B airborne position and velocity. // TODO: decode me. // For now we only look at the IMF bit. - mm->bFlags |= MODES_ACFLAGS_FROM_TISB; + mm->source = SOURCE_TISB; if (msg[4] & 0x80) mm->addr |= MODES_NON_ICAO_ADDRESS; return; case 5: // TIS-B messages that relay ADS-B Messages using anonymous 24-bit addresses (format not explicitly defined, but it seems to follow DF17) - mm->bFlags |= MODES_ACFLAGS_FROM_TISB; + mm->source = SOURCE_TISB; mm->addr |= MODES_NON_ICAO_ADDRESS; break; @@ -762,8 +745,6 @@ static void decodeExtendedSquitter(struct modesMessage *mm) } } - - switch (metype) { case 1: case 2: case 3: case 4: { // Aircraft Identification and Category @@ -775,24 +756,23 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // A common failure mode seems to be to intermittently send // all zeros. Catch that here. if (chars1 != 0 || chars2 != 0) { - mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID; + mm->callsign_valid = 1; - mm->flight[3] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; - mm->flight[2] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; - mm->flight[1] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; - mm->flight[0] = ais_charset[chars1 & 0x3F]; + mm->callsign[3] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; + mm->callsign[2] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; + mm->callsign[1] = ais_charset[chars1 & 0x3F]; chars1 = chars1 >> 6; + mm->callsign[0] = ais_charset[chars1 & 0x3F]; - mm->flight[7] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; - mm->flight[6] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; - mm->flight[5] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; - mm->flight[4] = ais_charset[chars2 & 0x3F]; + mm->callsign[7] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; + mm->callsign[6] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; + mm->callsign[5] = ais_charset[chars2 & 0x3F]; chars2 = chars2 >> 6; + mm->callsign[4] = ais_charset[chars2 & 0x3F]; - mm->flight[8] = '\0'; + mm->callsign[8] = '\0'; } mm->category = ((0x0E - metype) << 4) | mesub; - mm->bFlags |= MODES_ACFLAGS_CATEGORY_VALID; - + mm->category_valid = 1; break; } @@ -800,9 +780,6 @@ static void decodeExtendedSquitter(struct modesMessage *mm) if (check_imf && (msg[5] & 0x80)) mm->addr |= MODES_NON_ICAO_ADDRESS; - // Presumably airborne if we get an Airborne Velocity Message - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - if ( (mesub >= 1) && (mesub <= 4) ) { int vert_rate = ((msg[8] & 0x07) << 6) | (msg[9] >> 2); if (vert_rate) { @@ -810,66 +787,58 @@ static void decodeExtendedSquitter(struct modesMessage *mm) if (msg[8] & 0x08) {vert_rate = 0 - vert_rate;} mm->vert_rate = vert_rate * 64; - mm->bFlags |= MODES_ACFLAGS_VERTRATE_VALID; + mm->vert_rate_valid = 1; } + + mm->vert_rate_source = (msg[8] & 0x10 ? ALTITUDE_GNSS : ALTITUDE_BARO); } if ((mesub == 1) || (mesub == 2)) { - int ew_raw = ((msg[5] & 0x03) << 8) | msg[6]; - int ew_vel = ew_raw - 1; - int ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5); - int ns_vel = ns_raw - 1; - - if (mesub == 2) { // If (supersonic) unit is 4 kts - ns_vel = ns_vel << 2; - ew_vel = ew_vel << 2; - } - - if (ew_raw) { // Do East/West - mm->bFlags |= MODES_ACFLAGS_EWSPEED_VALID; - if (msg[5] & 0x04) - {ew_vel = 0 - ew_vel;} - mm->ew_velocity = ew_vel; - } - - if (ns_raw) { // Do North/South - mm->bFlags |= MODES_ACFLAGS_NSSPEED_VALID; - if (msg[7] & 0x80) - {ns_vel = 0 - ns_vel;} - mm->ns_velocity = ns_vel; - } + unsigned ew_raw = ((msg[5] & 0x03) << 8) | msg[6]; + unsigned ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5); if (ew_raw && ns_raw) { + int ew_vel = (ew_raw - 1) * ((msg[5] & 0x04) ? -1 : 1) * ((mesub == 2) ? 4 : 1); + int ns_vel = (ns_raw - 1) * ((msg[7] & 0x80) ? -1 : 1) * ((mesub == 2) ? 4 : 1); + // Compute velocity and angle from the two speed components - mm->bFlags |= (MODES_ACFLAGS_SPEED_VALID | MODES_ACFLAGS_HEADING_VALID | MODES_ACFLAGS_NSEWSPD_VALID); - mm->velocity = (int) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel)); + mm->speed = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); + mm->speed_valid = 1; - if (mm->velocity) { - mm->heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI); + if (mm->speed) { + int heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI + 0.5); // We don't want negative values but a 0-360 scale - if (mm->heading < 0) mm->heading += 360; + if (heading < 0) + heading += 360; + mm->heading = (unsigned) heading; + mm->heading_source = HEADING_TRUE; + mm->heading_valid = 1; } + + mm->speed_source = SPEED_GROUNDSPEED; } - } else if (mesub == 3 || mesub == 4) { - int airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5); + unsigned airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5); if (airspeed) { - mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; --airspeed; - if (mesub == 4) // If (supersonic) unit is 4 kts - {airspeed = airspeed << 2;} - mm->velocity = airspeed; + if (mesub == 4) { // If (supersonic) unit is 4 kts + airspeed *= 4; + } + mm->speed = airspeed; + mm->speed_source = (msg[7] & 0x80) ? SPEED_TAS : SPEED_IAS; + mm->speed_valid = 1; } - + if (msg[5] & 0x04) { - mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; mm->heading = ((((msg[5] & 0x03) << 8) | msg[6]) * 45) >> 7; + mm->heading_source = HEADING_MAGNETIC; + mm->heading_valid = 1; } } - if (msg[10] != 0) { - mm->bFlags |= MODES_ACFLAGS_HAE_DELTA_VALID; - mm->hae_delta = ((msg[10] & 0x80) ? -25 : 25) * ((msg[10] & 0x7f) - 1); + if (msg[10] & 0x7f) { + mm->gnss_delta_valid = 1; + mm->gnss_delta = ((msg[10] & 0x80) ? -25 : 25) * ((msg[10] & 0x7f) - 1); } break; @@ -882,45 +851,48 @@ static void decodeExtendedSquitter(struct modesMessage *mm) if (check_imf && (msg[6] & 0x08)) mm->addr |= MODES_NON_ICAO_ADDRESS; - mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG; - mm->raw_latitude = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); - mm->raw_longitude = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); - mm->bFlags |= (mm->msg[6] & 0x04) ? MODES_ACFLAGS_LLODD_VALID - : MODES_ACFLAGS_LLEVEN_VALID; + mm->airground = AG_GROUND; // definitely. + mm->cpr_lat = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); + mm->cpr_lon = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); + if (mm->msg[6] & 0x04) + mm->cpr_odd = 1; + mm->cpr_nucp = (14 - metype); + mm->cpr_valid = 1; movement = ((msg[4] << 4) | (msg[5] >> 4)) & 0x007F; if ((movement) && (movement < 125)) { - mm->bFlags |= MODES_ACFLAGS_SPEED_VALID; - mm->velocity = decodeMovementField(movement); + mm->speed_valid = 1; + mm->speed = decodeMovementField(movement); + mm->speed_source = SPEED_GROUNDSPEED; } if (msg[5] & 0x08) { - mm->bFlags |= MODES_ACFLAGS_HEADING_VALID; + mm->heading_valid = 1; + mm->heading_source = HEADING_TRUE; mm->heading = ((((msg[5] << 4) | (msg[6] >> 4)) & 0x007F) * 45) >> 4; } - mm->nuc_p = (14 - metype); break; } case 0: // Airborne position, baro altitude only case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: // Airborne position, baro - case 20: case 21: case 22: { // Airborne position, GNSS HAE + case 20: case 21: case 22: { // Airborne position, GNSS altitude (HAE or MSL) int AC12Field = ((msg[5] << 4) | (msg[6] >> 4)) & 0x0FFF; if (check_imf && (msg[4] & 0x01)) mm->addr |= MODES_NON_ICAO_ADDRESS; - mm->bFlags |= MODES_ACFLAGS_AOG_VALID; - - if (metype != 0) { + if (metype == 0) { + mm->cpr_nucp = 0; + } else { // Catch some common failure modes and don't mark them as valid // (so they won't be used for positioning) - mm->raw_latitude = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); - mm->raw_longitude = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); + mm->cpr_lat = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1); + mm->cpr_lon = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]); - if (AC12Field == 0 && mm->raw_longitude == 0 && (mm->raw_latitude & 0x0fff) == 0 && mm->metype == 15) { + if (AC12Field == 0 && mm->cpr_lon == 0 && (mm->cpr_lat & 0x0fff) == 0 && mm->metype == 15) { // Seen from at least: // 400F3F (Eurocopter ECC155 B1) - Bristow Helicopters // 4008F3 (BAE ATP) - Atlantic Airlines @@ -930,33 +902,28 @@ static void decodeExtendedSquitter(struct modesMessage *mm) Modes.stats_current.cpr_filtered++; } else { // Otherwise, assume it's valid. - mm->bFlags |= (mm->msg[6] & 0x04) ? MODES_ACFLAGS_LLODD_VALID - : MODES_ACFLAGS_LLEVEN_VALID; + mm->cpr_valid = 1; + if (mm->msg[6] & 0x04) + mm->cpr_odd = 1; + + if (metype == 18 || metype == 22) + mm->cpr_nucp = 0; + else if (metype < 18) + mm->cpr_nucp = (18 - metype); + else + mm->cpr_nucp = (29 - metype); } } if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present - if (metype == 20 || metype == 21 || metype == 22) { - // Position reported as HAE - mm->altitude_hae = decodeAC12Field(AC12Field, &mm->unit); - if (mm->altitude_hae != INVALID_ALTITUDE) { - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_HAE_VALID; - } - } else { - mm->altitude = decodeAC12Field(AC12Field, &mm->unit); - if (mm->altitude != INVALID_ALTITUDE) { - mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; - } + mm->altitude = decodeAC12Field(AC12Field, &mm->altitude_unit); + if (mm->altitude != INVALID_ALTITUDE) { + mm->altitude_valid = 1; } + + mm->altitude_source = (metype == 20 || metype == 21 || metype == 22) ? ALTITUDE_GNSS : ALTITUDE_BARO; } - if (metype == 0 || metype == 18 || metype == 22) - mm->nuc_p = 0; - else if (metype < 18) - mm->nuc_p = (18 - metype); - else - mm->nuc_p = (29 - metype); - break; } @@ -964,8 +931,8 @@ static void decodeExtendedSquitter(struct modesMessage *mm) if (mesub == 7) { // (see 1090-WP-15-20) int ID13Field = (((msg[5] << 8) | msg[6]) & 0xFFF1)>>3; if (ID13Field) { - mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; - mm->modeA = decodeID13Field(ID13Field); + mm->squawk_valid = 1; + mm->squawk = decodeID13Field(ID13Field); } } break; @@ -978,8 +945,8 @@ static void decodeExtendedSquitter(struct modesMessage *mm) if (mesub == 1) { // Emergency status squawk field int ID13Field = (((msg[5] << 8) | msg[6]) & 0x1FFF); if (ID13Field) { - mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; - mm->modeA = decodeID13Field(ID13Field); + mm->squawk_valid = 1; + mm->squawk = decodeID13Field(ID13Field); } if (check_imf && (msg[10] & 0x01)) @@ -1014,104 +981,201 @@ static void decodeCommB(struct modesMessage *mm) } } -// -//========================================================================= -// -// These functions gets a decoded Mode S Message and prints it on the screen -// in a human readable format. -// -static void displayExtendedSquitter(struct modesMessage *mm) { - printf(" Extended Squitter Type: %d\n", mm->metype); - printf(" Extended Squitter Sub : %d\n", mm->mesub); - printf(" Extended Squitter Name: %s\n", getMEDescription(mm->metype, mm->mesub)); - - // Decode the extended squitter message - if (mm->metype >= 1 && mm->metype <= 4) { // Aircraft identification - printf(" Aircraft Type : %02X\n", mm->category); - printf(" Identification : %s\n", (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) ? mm->flight : "invalid"); - } else if (mm->metype == 19) { // Airborne Velocity - if (mm->mesub == 1 || mm->mesub == 2) { - printf(" EW status : %s\n", (mm->bFlags & MODES_ACFLAGS_EWSPEED_VALID) ? "Valid" : "Unavailable"); - printf(" EW velocity : %d\n", mm->ew_velocity); - printf(" NS status : %s\n", (mm->bFlags & MODES_ACFLAGS_NSSPEED_VALID) ? "Valid" : "Unavailable"); - printf(" NS velocity : %d\n", mm->ns_velocity); - printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable"); - printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1)); - printf(" Vertical rate : %d\n", mm->vert_rate); - } else if (mm->mesub == 3 || mm->mesub == 4) { - printf(" Heading status : %s\n", (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) ? "Valid" : "Unavailable"); - printf(" Heading : %d\n", mm->heading); - printf(" Airspeed status : %s\n", (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) ? "Valid" : "Unavailable"); - printf(" Airspeed : %d\n", mm->velocity); - printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable"); - printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1)); - printf(" Vertical rate : %d\n", mm->vert_rate); - } else { - printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub); - } +static const char *df_names[33] = { + /* 0 */ "Short Air-Air Surveillance", + /* 1 */ NULL, + /* 2 */ NULL, + /* 3 */ NULL, + /* 4 */ "Survelliance, Altitude Reply", + /* 5 */ "Survelliance, Identity Reply", + /* 6 */ NULL, + /* 7 */ NULL, + /* 8 */ NULL, + /* 9 */ NULL, + /* 10 */ NULL, + /* 11 */ "All Call Reply", + /* 12 */ NULL, + /* 13 */ NULL, + /* 14 */ NULL, + /* 15 */ NULL, + /* 16 */ "Long Air-Air ACAS", + /* 17 */ "Extended Squitter", + /* 18 */ "Extended Squitter (Non-Transponder)", + /* 19 */ "Extended Squitter (Military)", + /* 20 */ "Comm-B, Altitude Reply", + /* 21 */ "Comm-B, Identity Reply", + /* 22 */ "Military Use", + /* 23 */ NULL, + /* 24 */ "Comm-D Extended Length Message", + /* 25 */ "Comm-D Extended Length Message", + /* 26 */ "Comm-D Extended Length Message", + /* 27 */ "Comm-D Extended Length Message", + /* 28 */ "Comm-D Extended Length Message", + /* 29 */ "Comm-D Extended Length Message", + /* 30 */ "Comm-D Extended Length Message", + /* 31 */ "Comm-D Extended Length Message", + /* 32 */ "Mode A/C Reply", +}; - if (mm->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID) { - printf(" HAE/Baro offset : %d ft\n", mm->hae_delta); - } else { - printf(" HAE/Baro offset : not valid\n"); +static const char *df_to_string(unsigned df) { + if (df > 32) + return "out of range"; + if (!df_names[df]) + return "reserved"; + return df_names[df]; +} + +static const char *altitude_unit_to_string(altitude_unit_t unit) { + switch (unit) { + case UNIT_FEET: + return "ft"; + case UNIT_METERS: + return "m"; + default: + return "(unknown altitude unit)"; + } +} + +static const char *altitude_source_to_string(altitude_source_t source) { + switch (source) { + case ALTITUDE_BARO: + return "barometric"; + case ALTITUDE_GNSS: + return "GNSS"; + default: + return "(unknown altitude source)"; + } +} + +static const char *airground_to_string(airground_t airground) { + switch (airground) { + case AG_GROUND: + return "ground"; + case AG_AIRBORNE: + return "airborne"; + case AG_INVALID: + return "invalid"; + case AG_UNCERTAIN: + return "airborne?"; + default: + return "(unknown airground state)"; + } +} + +static const char *speed_source_to_string(speed_source_t speed) { + switch (speed) { + case SPEED_GROUNDSPEED: + return "groundspeed"; + case SPEED_IAS: + return "IAS"; + case SPEED_TAS: + return "TAS"; + default: + return "(unknown speed type)"; + } +} + +static void print_hex_bytes(unsigned char *data, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + printf("%02X", (unsigned)data[i]); + } +} + +static int esTypeHasSubtype(unsigned metype) +{ + if (metype <= 18) { + return 0; + } + + if (metype >= 20 && metype <= 22) { + return 0; + } + + return 1; +} + +static const char *esTypeName(unsigned metype, unsigned mesub) +{ + switch (metype) { + case 0: + return "No position information (airborne or surface)"; + + case 1: case 2: case 3: case 4: + return "Aircraft identification and category"; + + case 5: case 6: case 7: case 8: + return "Surface position"; + + case 9: case 10: case 11: case 12: + case 13: case 14: case 15: case 16: + case 17: case 18: + return "Airborne position (barometric altitude)"; + + case 19: + switch (mesub) { + case 1: + return "Airborne velocity over ground, subsonic"; + case 2: + return "Airborne velocity over ground, supersonic"; + case 3: + return "Airspeed and heading, subsonic"; + case 4: + return "Airspeed and heading, supersonic"; + default: + return "Unknown"; } - } else if (mm->metype >= 5 && mm->metype <= 22) { // Airborne position Baro - printf(" F flag : %s\n", (mm->msg[6] & 0x04) ? "odd" : "even"); - printf(" T flag : %s\n", (mm->msg[6] & 0x08) ? "UTC" : "non-UTC"); - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) - printf(" Altitude : %d feet barometric\n", mm->altitude); - else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID) - printf(" Altitude : %d feet HAE\n", mm->altitude_hae); - else - printf(" Altitude : not valid\n"); - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) { - if (mm->bFlags & MODES_ACFLAGS_REL_CPR_USED) - printf(" Local CPR decoding used.\n"); - else - printf(" Global CPR decoding used.\n"); - printf(" Latitude : %f (%d)\n", mm->fLat, mm->raw_latitude); - printf(" Longitude: %f (%d)\n", mm->fLon, mm->raw_longitude); - printf(" NUCp: %u\n", mm->nuc_p); - } else { - if (!(mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID)) - printf(" Bad position data, not decoded.\n"); - printf(" Latitude : %d (not decoded)\n", mm->raw_latitude); - printf(" Longitude: %d (not decoded)\n", mm->raw_longitude); - printf(" NUCp: %u\n", mm->nuc_p); + + case 20: case 21: case 22: + return "Airborne position (GNSS altitude)"; + + case 23: + switch (mesub) { + case 0: + return "Test message"; + case 7: + return "National use / 1090-WP-15-20 Mode A squawk"; + default: + return "Unknown"; } - } else if (mm->metype == 28) { // Extended Squitter Aircraft Status - if (mm->mesub == 1) { - printf(" Emergency State: %s\n", es_str[(mm->msg[5] & 0xE0) >> 5]); - printf(" Squawk: %04x\n", mm->modeA); - } else { - printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub); + + case 24: + return "Reserved for surface system status"; + + case 27: + return "Reserved for trajectory change"; + + case 28: + switch (mesub) { + case 1: + return "Emergency/priority status"; + case 2: + return "ACAS RA broadcast"; + default: + return "Unknown"; } - } else if (mm->metype == 23) { // Test Message - if (mm->mesub == 7) { - printf(" Squawk: %04x\n", mm->modeA); - } else { - printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub); + + case 29: + switch (mesub) { + case 1: + return "Target state and status"; + default: + return "Unknown"; } - } else { - printf(" Unrecognized ME type: %d subtype: %d\n", mm->metype, mm->mesub); - } -} -static void displayCommB(struct modesMessage *mm) -{ - if (mm->bds != 0) - printf(" Comm-B BDS : %02x (maybe)\n", mm->bds); + case 31: // Aircraft Operational Status + switch (mesub) { + case 0: + return "Aircraft operational status (airborne)"; + case 1: + return "Aircraft operational status (surface)"; + default: + return "Unknown"; + } - // Decode the Comm-B message - if (mm->msg[4] == 0x20 && (mm->bds == 0 || mm->bds == 0x20)) { // BDS 2,0 Aircraft identification - printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight); - } else { - int i; - printf(" Comm-B MB : "); - for (i = 4; i < 11; ++i) - printf("%02x", mm->msg[i]); - printf("\n"); - } + default: + return "Unknown"; + } } void displayModesMessage(struct modesMessage *mm) { @@ -1153,99 +1217,174 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->timestampMsg == MAGIC_MLAT_TIMESTAMP) printf("This is a synthetic MLAT message.\n"); else - printf("Time: %.2fus (phase: %u)\n", mm->timestampMsg / 12.0, (unsigned int) (360 * (mm->timestampMsg % 6) / 6)); + printf("Time: %.2fus\n", mm->timestampMsg / 12.0); } - if (mm->msgtype == 0) { // DF 0 - printf("DF 0: Short Air-Air Surveillance.\n"); - printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne"); - printf(" CC : %d\n", ((mm->msg[0] & 0x02) >> 1)); - printf(" SL : %d\n", ((mm->msg[1] & 0xE0) >> 5)); - printf(" Altitude : %d %s\n", mm->altitude, - (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); - printf(" ICAO Address : %06x\n", mm->addr); - - } else if (mm->msgtype == 4 || mm->msgtype == 20) { - printf("DF %d: %s, Altitude Reply.\n", mm->msgtype, - (mm->msgtype == 4) ? "Surveillance" : "Comm-B"); - printf(" Flight Status : %s\n", fs_str[mm->fs]); - printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F)); - printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5))); - printf(" Altitude : %d %s\n", mm->altitude, - (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); - printf(" ICAO Address : %06x\n", mm->addr); - - if (mm->msgtype == 20) { - displayCommB(mm); - } - } else if (mm->msgtype == 5 || mm->msgtype == 21) { - printf("DF %d: %s, Identity Reply.\n", mm->msgtype, - (mm->msgtype == 5) ? "Surveillance" : "Comm-B"); - printf(" Flight Status : %s\n", fs_str[mm->fs]); - printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F)); - printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5))); - printf(" Squawk : %04x\n", mm->modeA); - printf(" ICAO Address : %06x\n", mm->addr); - - if (mm->msgtype == 21) { - displayCommB(mm); - } - } else if (mm->msgtype == 11) { // DF 11 - printf("DF 11: All Call Reply.\n"); - printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); - printf(" ICAO Address: %06x\n", mm->addr); - if (mm->iid > 16) - {printf(" IID : SI-%02d\n", mm->iid-16);} - else - {printf(" IID : II-%02d\n", mm->iid);} - - } else if (mm->msgtype == 16) { // DF 16 - printf("DF 16: Long Air to Air ACAS\n"); - printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne"); - printf(" CC : %d\n", ((mm->msg[0] & 0x02) >> 1)); - printf(" SL : %d\n", ((mm->msg[1] & 0xE0) >> 5)); - printf(" Altitude : %d %s\n", mm->altitude, - (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); - printf(" ICAO Address : %06x\n", mm->addr); - - } else if (mm->msgtype == 17) { // DF 17 - printf("DF 17: ADS-B message.\n"); - printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); - printf(" ICAO Address : %06x\n", mm->addr); - displayExtendedSquitter(mm); - } else if (mm->msgtype == 18) { // DF 18 - printf("DF 18: Extended Squitter.\n"); - printf(" Control Field : %d (%s)\n", mm->cf, cf_str[mm->cf]); - if (mm->addr & MODES_NON_ICAO_ADDRESS) { - printf(" Other Address : %06x\n", mm->addr); - } else { - printf(" ICAO Address : %06x\n", mm->addr); - } - if ((mm->cf == 0) || (mm->cf == 1) || (mm->cf == 2) || (mm->cf == 5) || (mm->cf == 6)) { - displayExtendedSquitter(mm); - } + switch (mm->msgtype) { + case 0: + printf("DF:0 addr:%06X VS:%u CC:%u SL:%u RI:%u AC:%u\n", + mm->addr, mm->VS, mm->CC, mm->SL, mm->RI, mm->AC); + break; + + case 4: + printf("DF:4 addr:%06X FS:%u DR:%u UM:%u AC:%u\n", + mm->addr, mm->FS, mm->DR, mm->UM, mm->AC); + break; + + case 5: + printf("DF:5 addr:%06X FS:%u DR:%u UM:%u ID:%u\n", + mm->addr, mm->FS, mm->DR, mm->UM, mm->ID); + break; + + case 11: + printf("DF:11 AA:%06X IID:%u CA:%u\n", + mm->AA, mm->IID, mm->CA); + break; + + case 16: + printf("DF:16 addr:%06x VS:%u SL:%u RI:%u AC:%u MV:", + mm->addr, mm->VS, mm->SL, mm->RI, mm->AC); + print_hex_bytes(mm->MV, sizeof(mm->MV)); + printf("\n"); + break; - } else if (mm->msgtype == 19) { // DF 19 - printf("DF 19: Military Extended Squitter.\n"); + case 17: + printf("DF:17 AA:%06X CA:%u ME:", + mm->AA, mm->CA); + print_hex_bytes(mm->ME, sizeof(mm->ME)); + printf("\n"); + break; - } else if (mm->msgtype == 22) { // DF 22 - printf("DF 22: Military Use.\n"); + case 18: + printf("DF:18 AA:%06X CF:%u ME:", + mm->AA, mm->CF); + print_hex_bytes(mm->ME, sizeof(mm->ME)); + printf("\n"); + break; + + case 20: + printf("DF:20 addr:%06X FS:%u DR:%u UM:%u AC:%u MB:", + mm->addr, mm->FS, mm->DR, mm->UM, mm->AC); + print_hex_bytes(mm->MB, sizeof(mm->MB)); + printf("\n"); + break; - } else if (mm->msgtype == 24) { // DF 24 - printf("DF 24: Comm D Extended Length Message.\n"); + case 21: + printf("DF:21 addr:%06x FS:%u DR:%u UM:%u ID:%u MB:", + mm->addr, mm->FS, mm->DR, mm->UM, mm->ID); + print_hex_bytes(mm->MB, sizeof(mm->MB)); + printf("\n"); + break; + + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + printf("DF:24 addr:%06x KE:%u ND:%u MD:", + mm->addr, mm->KE, mm->ND); + print_hex_bytes(mm->MD, sizeof(mm->MD)); + printf("\n"); + break; + } - } else if (mm->msgtype == 32) { // DF 32 is special code we use for Mode A/C - printf("SSR : Mode A/C Reply.\n"); - if (mm->fs & 0x0080) { - printf(" Mode A : %04x IDENT\n", mm->modeA); + printf(" %s", df_to_string(mm->msgtype)); + if (mm->msgtype == 17 || mm->msgtype == 18) { + if (esTypeHasSubtype(mm->metype)) { + printf(" %s (%u/%u)", + esTypeName(mm->metype, mm->mesub), + mm->metype, + mm->mesub); } else { - printf(" Mode A : %04x\n", mm->modeA); - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) - {printf(" Mode C : %d feet\n", mm->altitude);} + printf(" %s (%u)", + esTypeName(mm->metype, mm->mesub), + mm->metype); } + } + printf("\n"); + if (mm->addr & MODES_NON_ICAO_ADDRESS) { + printf(" Other Address: %06X\n", mm->addr); } else { - printf("DF %d: Unknown DF Format.\n", mm->msgtype); + printf(" ICAO Address: %06X\n", mm->addr); + } + + if (mm->airground != AG_INVALID) { + printf(" Air/Ground: %s\n", + airground_to_string(mm->airground)); + } + + if (mm->altitude_valid) { + printf(" Altitude: %d %s %s\n", + mm->altitude, + altitude_unit_to_string(mm->altitude_unit), + altitude_source_to_string(mm->altitude_source)); + } + + if (mm->gnss_delta_valid) { + printf(" GNSS delta: %d ft\n", + mm->gnss_delta); + } + + if (mm->heading_valid) { + printf(" Heading: %u\n", mm->heading); + } + + if (mm->speed_valid) { + printf(" Speed: %u kt %s\n", + mm->speed, + speed_source_to_string(mm->speed_source)); + } + + if (mm->vert_rate_valid) { + printf(" Vertical rate: %d ft/min %s\n", + mm->vert_rate, + altitude_source_to_string(mm->vert_rate_source)); + } + + if (mm->squawk_valid) { + printf(" Squawk: %04x\n", + mm->squawk); + } + + if (mm->callsign_valid) { + printf(" Ident: %s\n", + mm->callsign); + } + + if (mm->category_valid) { + printf(" Category: %02X\n", + mm->category); + } + + if (mm->msgtype == 17 || mm->msgtype == 18) { + } + + if (mm->cpr_valid) { + printf(" CPR odd flag: %s\n" + " CPR NUCp/NIC: %u\n", + mm->cpr_odd ? "odd" : "even", + mm->cpr_nucp); + + if (mm->cpr_decoded) { + printf(" CPR latitude: %.5f (%u)\n" + " CPR longitude: %.5f (%u)\n" + " CPR decoding: %s\n", + mm->decoded_lat, + mm->cpr_lat, + mm->decoded_lon, + mm->cpr_lon, + mm->cpr_relative ? "local" : "global"); + } else { + printf(" CPR latitude: (%u)\n" + " CPR longitude: (%u)\n" + " CPR decoding: none\n", + mm->cpr_lat, + mm->cpr_lon); + } } printf("\n"); diff --git a/net_io.c b/net_io.c index 96df46b..4d88cd1 100644 --- a/net_io.c +++ b/net_io.c @@ -2,7 +2,7 @@ // // net_io.c: network handling. // -// Copyright (c) 2014,2015 Oliver Jowett +// Copyright (c) 2014-2016 Oliver Jowett // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the @@ -493,7 +493,6 @@ static void send_raw_heartbeat(struct net_service *service) //========================================================================= // // Write SBS output to TCP clients -// The message structure mm->bFlags tells us what has been updated by this message // static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { char *p; @@ -515,38 +514,48 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { // // Decide on the basic SBS Message Type - if ((mm->msgtype == 4) || (mm->msgtype == 20)) { + switch (mm->msgtype) { + case 4: + case 20: msgType = 5; - } else if ((mm->msgtype == 5) || (mm->msgtype == 21)) { + break; + break; + + case 5: + case 21: msgType = 6; - } else if ((mm->msgtype == 0) || (mm->msgtype == 16)) { + break; + + case 0: + case 16: msgType = 7; - } else if (mm->msgtype == 11) { + break; + + case 11: msgType = 8; - } else if ((mm->msgtype != 17) && (mm->msgtype != 18)) { - return; - } else if ((mm->metype >= 1) && (mm->metype <= 4)) { - msgType = 1; - } else if ((mm->metype >= 5) && (mm->metype <= 8)) { - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) - {msgType = 2;} - else - {msgType = 7;} - } else if ((mm->metype >= 9) && (mm->metype <= 18)) { - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) - {msgType = 3;} - else - {msgType = 7;} - } else if (mm->metype != 19) { - return; - } else if ((mm->mesub == 1) || (mm->mesub == 2)) { - msgType = 4; - } else { + break; + + case 17: + case 18: + if (mm->metype >= 1 && mm->metype <= 4) { + msgType = 1; + } else if (mm->metype >= 5 && mm->metype <= 8) { + msgType = 2; + } else if (mm->metype >= 9 && mm->metype <= 18) { + msgType = 3; + } else if (mm->metype == 19) { + msgType = 4; + } else { + return; + } + break; + + default: return; } // Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff - p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr); + p += sprintf(p, "MSG,%d,1,1,%06X,1,", msgType, mm->addr); // Find current system time clock_gettime(CLOCK_REALTIME, &now); @@ -564,53 +573,70 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { p += sprintf(p, "%02d:%02d:%02d.%03u", stTime_now.tm_hour, stTime_now.tm_min, stTime_now.tm_sec, (unsigned) (now.tv_nsec / 1000000U)); // Field 11 is the callsign (if we have it) - if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);} - else {p += sprintf(p, ",");} - - // Field 12 is the altitude (if we have it) - force to zero if we're on the ground - if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) { - p += sprintf(p, ",0"); - } else if (Modes.use_hae && (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) { - p += sprintf(p, ",%dH", mm->altitude_hae); - } else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) { - p += sprintf(p, ",%dH", mm->altitude + a->hae_delta); + if (mm->callsign_valid) {p += sprintf(p, ",%s", mm->callsign);} + else {p += sprintf(p, ",");} + + // Field 12 is the altitude (if we have it) + if (mm->altitude_valid) { + if (Modes.use_gnss) { + if (mm->altitude_source == ALTITUDE_GNSS) { + p += sprintf(p, ",%dH", mm->altitude); + } else if (trackDataValid(&a->gnss_delta_valid)) { + p += sprintf(p, ",%dH", mm->altitude + a->gnss_delta); + } else { + p += sprintf(p, ",%d", mm->altitude); + } } else { - p += sprintf(p, ",%d", mm->altitude); + if (mm->altitude_source == ALTITUDE_BARO) { + p += sprintf(p, ",%d", mm->altitude); + } else if (trackDataValid(&a->gnss_delta_valid)) { + p += sprintf(p, ",%d", mm->altitude - a->gnss_delta); + } else { + p += sprintf(p, ",,"); + } } } else { p += sprintf(p, ","); } // Field 13 is the ground Speed (if we have it) - if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { - p += sprintf(p, ",%d", mm->velocity); + if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED) { + p += sprintf(p, ",%d", mm->speed); } else { p += sprintf(p, ","); } // Field 14 is the ground Heading (if we have it) - if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { + if (mm->heading_valid && mm->heading_source == HEADING_TRUE) { p += sprintf(p, ",%d", mm->heading); } else { p += sprintf(p, ","); } // Fields 15 and 16 are the Lat/Lon (if we have it) - if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);} - else {p += sprintf(p, ",,");} + if (mm->cpr_decoded) { + p += sprintf(p, ",%1.5f,%1.5f", mm->decoded_lat, mm->decoded_lon); + } else { + p += sprintf(p, ",,"); + } // Field 17 is the VerticalRate (if we have it) - if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);} - else {p += sprintf(p, ",");} + if (mm->vert_rate_valid) { + p += sprintf(p, ",%d", mm->vert_rate); + } else { + p += sprintf(p, ","); + } // Field 18 is the Squawk (if we have it) - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);} - else {p += sprintf(p, ",");} + if (mm->squawk_valid) { + p += sprintf(p, ",%04x", mm->squawk); + } else { + p += sprintf(p, ","); + } // Field 19 is the Squawk Changing Alert flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { - if ((mm->fs >= 2) && (mm->fs <= 4)) { + if (mm->alert_valid) { + if (mm->alert) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); @@ -620,8 +646,8 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { } // Field 20 is the Squawk Emergency flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) { + if (mm->squawk_valid) { + if ((mm->squawk == 0x7500) || (mm->squawk == 0x7600) || (mm->squawk == 0x7700)) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); @@ -631,8 +657,8 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { } // Field 21 is the Squawk Ident flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { - if ((mm->fs >= 4) && (mm->fs <= 5)) { + if (mm->spi_valid) { + if (mm->spi) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); @@ -642,14 +668,16 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { } // Field 22 is the OnTheGround flag (if we have it) - if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) { - if (mm->bFlags & MODES_ACFLAGS_AOG) { - p += sprintf(p, ",-1"); - } else { - p += sprintf(p, ",0"); - } - } else { + switch (mm->airground) { + case AG_GROUND: + p += sprintf(p, ",-1"); + break; + case AG_AIRBORNE: + p += sprintf(p, ",0"); + break; + default: p += sprintf(p, ","); + break; } p += sprintf(p, "\r\n"); @@ -678,7 +706,7 @@ static void send_sbs_heartbeat(struct net_service *service) //========================================================================= // void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { - int is_mlat = ((mm->bFlags & MODES_ACFLAGS_FROM_MLAT) != 0); + int is_mlat = (mm->source == SOURCE_MLAT); if (!is_mlat && mm->correctedbits < 2) { // Don't ever forward 2-bit-corrected messages via SBS output. @@ -928,24 +956,24 @@ static const char *jsonEscapeString(const char *str) { return buf; } -static char *append_flags(char *p, char *end, int flags) +static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t source) { p += snprintf(p, end-p, "["); - if (flags & MODES_ACFLAGS_SQUAWK_VALID) + if (a->squawk_valid.source == source) p += snprintf(p, end-p, "\"squawk\","); - if (flags & MODES_ACFLAGS_CALLSIGN_VALID) + if (a->callsign_valid.source == source) p += snprintf(p, end-p, "\"callsign\","); - if (flags & MODES_ACFLAGS_LATLON_VALID) + if (a->position_valid.source == source) p += snprintf(p, end-p, "\"lat\",\"lon\","); - if (flags & MODES_ACFLAGS_ALTITUDE_VALID) + if (a->altitude_valid.source == source) p += snprintf(p, end-p, "\"altitude\","); - if (flags & MODES_ACFLAGS_HEADING_VALID) + if (a->heading_valid.source == source) p += snprintf(p, end-p, "\"track\","); - if (flags & MODES_ACFLAGS_SPEED_VALID) + if (a->speed_valid.source == source) p += snprintf(p, end-p, "\"speed\","); - if (flags & MODES_ACFLAGS_VERTRATE_VALID) + if (a->vert_rate_valid.source == source) p += snprintf(p, end-p, "\"vert_rate\","); - if (flags & MODES_ACFLAGS_CATEGORY_VALID) + if (a->category_valid.source == source) p += snprintf(p, end-p, "\"category\","); if (p[-1] != '[') --p; @@ -984,32 +1012,29 @@ char *generateAircraftJson(const char *url_path, int *len) { *p++ = ','; p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); - if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) - p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->modeA); - if (a->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) - p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->flight)); - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) - p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->seenLatLon)/1000.0); - if ((a->bFlags & MODES_ACFLAGS_AOG_VALID) && (a->bFlags & MODES_ACFLAGS_AOG)) + if (trackDataValid(&a->squawk_valid)) + p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk); + if (trackDataValid(&a->callsign_valid)) + p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign)); + if (trackDataValid(&a->position_valid)) + p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->position_valid.updated)/1000.0); + if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND) p += snprintf(p, end-p, ",\"altitude\":\"ground\""); - else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) + else if (trackDataValid(&a->altitude_valid)) p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude); - if (a->bFlags & MODES_ACFLAGS_VERTRATE_VALID) + if (trackDataValid(&a->vert_rate_valid)) p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate); - if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) - p += snprintf(p, end-p, ",\"track\":%d", a->track); - if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) + if (trackDataValid(&a->heading_valid)) + p += snprintf(p, end-p, ",\"track\":%d", a->heading); + if (trackDataValid(&a->speed_valid)) p += snprintf(p, end-p, ",\"speed\":%d", a->speed); - if (a->bFlags & MODES_ACFLAGS_CATEGORY_VALID) + if (trackDataValid(&a->category_valid)) p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category); - if (a->mlatFlags) { - p += snprintf(p, end-p, ",\"mlat\":"); - p = append_flags(p, end, a->mlatFlags); - } - if (a->tisbFlags) { - p += snprintf(p, end-p, ",\"tisb\":"); - p = append_flags(p, end, a->tisbFlags); - } + + p += snprintf(p, end-p, ",\"mlat\":"); + p = append_flags(p, end, a, SOURCE_MLAT); + p += snprintf(p, end-p, ",\"tisb\":"); + p = append_flags(p, end, a, SOURCE_TISB); p += snprintf(p, end-p, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}", a->messages, (now - a->seen)/1000.0, @@ -1471,11 +1496,12 @@ static int handleHTTPRequest(struct client *c, char *p) { // Send header and content. #ifndef _WIN32 if ( (write(c->fd, hdr, hdrlen) != hdrlen) - || (write(c->fd, content, clen) != clen) ) { + || (write(c->fd, content, clen) != clen) ) #else if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen) - || (send(c->fd, content, clen, 0) != clen) ) { + || (send(c->fd, content, clen, 0) != clen) ) #endif + { free(content); return 1; } @@ -1536,10 +1562,11 @@ static void modesReadFromClient(struct client *c) { } #ifndef _WIN32 - if (nread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { // No data available (not really an error) + if (nread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) // No data available (not really an error) #else - if (nread < 0 && errno == EWOULDBLOCK) { // No data available (not really an error) + if (nread < 0 && errno == EWOULDBLOCK) // No data available (not really an error) #endif + { return; } @@ -1645,29 +1672,22 @@ static void writeFATSV() for (a = Modes.aircrafts; a; a = a->next) { int altValid = 0; - int alt = 0; - uint64_t altAge = 999999; - - int groundValid = 0; - int ground = 0; - - int latlonValid = 0; - uint64_t latlonAge = 999999; - + int altGNSSValid = 0; + int positionValid = 0; int speedValid = 0; - uint64_t speedAge = 999999; - - int trackValid = 0; - uint64_t trackAge = 999999; + int speedIASValid = 0; + int speedTASValid = 0; + int headingValid = 0; + int headingMagValid = 0; + int airgroundValid = 0; - uint64_t emittedAge; + uint64_t minAge; int useful = 0; int changed = 0; char *p, *end; - int flags; int used_tisb = 0; // skip non-ICAO @@ -1682,82 +1702,70 @@ static void writeFATSV() continue; } - emittedAge = (now - a->fatsv_last_emitted); - - // ignore all mlat-derived data - flags = a->bFlags & ~a->mlatFlags; - - if (flags & MODES_ACFLAGS_ALTITUDE_VALID) { - alt = a->altitude; - altAge = now - a->seenAltitude; - altValid = (altAge <= 30000); - used_tisb |= (a->tisbFlags & MODES_ACFLAGS_ALTITUDE_VALID); - } - - if (flags & MODES_ACFLAGS_AOG_VALID) { - groundValid = 1; - - if (flags & MODES_ACFLAGS_AOG) { - // force zero altitude on ground - alt = 0; - altValid = 1; - altAge = 0; - ground = 1; - } - - used_tisb |= (a->tisbFlags & MODES_ACFLAGS_AOG_VALID); - } - - if (flags & MODES_ACFLAGS_LATLON_VALID) { - latlonAge = now - a->seenLatLon; - latlonValid = (latlonAge <= 30000); - used_tisb |= (a->tisbFlags & MODES_ACFLAGS_LATLON_VALID); - } - - if (flags & MODES_ACFLAGS_HEADING_VALID) { - trackAge = now - a->seenTrack; - trackValid = (trackAge <= 30000); - used_tisb |= (a->tisbFlags & MODES_ACFLAGS_HEADING_VALID); - } - - if (flags & MODES_ACFLAGS_SPEED_VALID) { - speedAge = now - a->seenSpeed; - speedValid = (speedAge <= 30000); - used_tisb |= (a->tisbFlags & MODES_ACFLAGS_SPEED_VALID); - } - - // don't send mode S very often - if (!latlonValid && emittedAge < 30000) { - continue; - } + altValid = trackDataValidEx(&a->altitude_valid, now, 15000, SOURCE_MODE_S); // for non-ADS-B transponders, DF0/4/16/20 are the only sources of altitude data + altGNSSValid = trackDataValidEx(&a->altitude_gnss_valid, now, 15000, SOURCE_MODE_S_CHECKED); + airgroundValid = trackDataValidEx(&a->airground_valid, now, 15000, SOURCE_MODE_S_CHECKED); // for non-ADS-B transponders, only trust DF11 CA field + positionValid = trackDataValidEx(&a->position_valid, now, 15000, SOURCE_MODE_S_CHECKED); + headingValid = trackDataValidEx(&a->heading_valid, now, 15000, SOURCE_MODE_S_CHECKED); + headingMagValid = trackDataValidEx(&a->heading_magnetic_valid, now, 15000, SOURCE_MODE_S_CHECKED); + speedValid = trackDataValidEx(&a->speed_valid, now, 15000, SOURCE_MODE_S_CHECKED); + speedIASValid = trackDataValidEx(&a->speed_ias_valid, now, 15000, SOURCE_MODE_S_CHECKED); + speedTASValid = trackDataValidEx(&a->speed_tas_valid, now, 15000, SOURCE_MODE_S_CHECKED); + + // If we are definitely on the ground, suppress any unreliable altitude info. + // When on the ground, ADS-B transponders don't emit an ADS-B message that includes + // altitude, so a corrupted Mode S altitude response from some other in-the-air AC + // might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000". + if (airgroundValid && a->airground == AG_GROUND && a->altitude_valid.source < SOURCE_MODE_S_CHECKED) + altValid = 0; // if it hasn't changed altitude, heading, or speed much, // don't update so often changed = 0; - if (trackValid && abs(a->track - a->fatsv_emitted_track) >= 2) { + if (altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) { + changed = 1; + } + if (altGNSSValid && abs(a->altitude_gnss - a->fatsv_emitted_altitude_gnss) >= 50) { + changed = 1; + } + if (headingValid && abs(a->heading - a->fatsv_emitted_heading) >= 2) { + changed = 1; + } + if (headingMagValid && abs(a->heading_magnetic - a->fatsv_emitted_heading_magnetic) >= 2) { changed = 1; } if (speedValid && abs(a->speed - a->fatsv_emitted_speed) >= 25) { changed = 1; } - if (altValid && abs(alt - a->fatsv_emitted_altitude) >= 50) { + if (speedIASValid && abs(a->speed_ias - a->fatsv_emitted_speed_ias) >= 25) { + changed = 1; + } + if (speedTASValid && abs(a->speed_tas - a->fatsv_emitted_speed_tas) >= 25) { changed = 1; } - if (!altValid || alt < 10000) { + if (airgroundValid && ((a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || + (a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE))) { + // Air-ground transition, handle it immediately. + minAge = 0; + } else if (!positionValid) { + // don't send mode S very often + minAge = 30000; + } else if ((airgroundValid && a->airground == AG_GROUND) || + (altValid && a->altitude < 500 && speedValid && a->speed < 100)) { + // we are probably on the ground, increase the update rate + minAge = 1000; + } else if (!altValid || a->altitude < 10000) { // Below 10000 feet, emit up to every 5s when changing, 10s otherwise - if (changed && emittedAge < 5000) - continue; - if (!changed && emittedAge < 10000) - continue; + minAge = (changed ? 5000 : 10000); } else { // Above 10000 feet, emit up to every 10s when changing, 30s otherwise - if (changed && emittedAge < 10000) - continue; - if (!changed && emittedAge < 30000) - continue; + minAge = (changed ? 10000 : 30000); } + if ((now - a->fatsv_last_emitted) < minAge) + continue; + p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); if (!p) return; @@ -1766,47 +1774,101 @@ static void writeFATSV() # define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) p += snprintf(p, bufsize(p,end), "clock\t%ld\thexid\t%06X", (long)(a->seen / 1000), a->addr); - if (*a->flight != '\0') { - p += snprintf(p, bufsize(p,end), "\tident\t%s", a->flight); + if (trackDataValidEx(&a->callsign_valid, now, 120000, SOURCE_MODE_S_CHECKED)) { // we accept quite old idents as they shouldn't change often + p += snprintf(p, bufsize(p,end), "\tident\t%s", a->callsign); + if (a->callsign_valid.source == SOURCE_TISB) { + used_tisb = 1; + } } - if (flags & MODES_ACFLAGS_SQUAWK_VALID) { - p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->modeA); + if (trackDataValidEx(&a->squawk_valid, now, 120000, SOURCE_MODE_S)) { // we accept quite old squawks as they shouldn't change often + p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->squawk); + if (a->squawk_valid.source == SOURCE_TISB) { + used_tisb = 1; + } } // only emit alt, speed, latlon, track if they have been received since the last time // and are not stale - if (altValid && altAge < emittedAge) { - p += snprintf(p, bufsize(p,end), "\talt\t%d", alt); + if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude); + a->fatsv_emitted_altitude = a->altitude; + if (a->altitude_valid.source == SOURCE_TISB) { + used_tisb = 1; + } useful = 1; } - if (speedValid && speedAge < emittedAge) { + if (altGNSSValid && a->altitude_gnss_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\talt_gnss\t%d", a->altitude_gnss); + a->fatsv_emitted_altitude_gnss = a->altitude_gnss; + if (a->altitude_gnss_valid.source == SOURCE_TISB) { + used_tisb = 1; + } + useful = 1; + } + + if (speedValid && a->speed_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed); + a->fatsv_emitted_speed = a->speed; + if (a->speed_valid.source == SOURCE_TISB) { + used_tisb = 1; + } useful = 1; } - if (groundValid) { - if (ground) { - p += snprintf(p, bufsize(p,end), "\tairGround\tG"); - } else { - p += snprintf(p, bufsize(p,end), "\tairGround\tA"); + if (speedIASValid && a->speed_ias_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tspeed_ias\t%d", a->speed_ias); + a->fatsv_emitted_speed_ias = a->speed_ias; + if (a->speed_ias_valid.source == SOURCE_TISB) { + used_tisb = 1; + } + useful = 1; + } + + if (speedTASValid && a->speed_tas_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tspeed_tas\t%d", a->speed_tas); + a->fatsv_emitted_speed_tas = a->speed_tas; + if (a->speed_tas_valid.source == SOURCE_TISB) { + used_tisb = 1; } + useful = 1; } - if (latlonValid && latlonAge < emittedAge) { + if (positionValid && a->position_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon); + if (a->position_valid.source == SOURCE_TISB) { + used_tisb = 1; + } + useful = 1; + } + + if (headingValid && a->heading_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\theading\t%d", a->heading); + a->fatsv_emitted_heading = a->heading; + if (a->heading_valid.source == SOURCE_TISB) { + used_tisb = 1; + } useful = 1; } - if (trackValid && trackAge < emittedAge) { - p += snprintf(p, bufsize(p,end), "\theading\t%d", a->track); + if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\theading_magnetic\t%d", a->heading); + a->fatsv_emitted_heading_magnetic = a->heading_magnetic; + if (a->heading_valid.source == SOURCE_TISB) { + used_tisb = 1; + } useful = 1; } - if (used_tisb) { - p += snprintf(p, bufsize(p,end), "\ttisb\t1"); + if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE)) { + p += snprintf(p, bufsize(p,end), "\tairGround\t%s", a->airground == AG_GROUND ? "G+" : "A+"); + a->fatsv_emitted_airground = a->airground; + if (a->airground_valid.source == SOURCE_TISB) { + used_tisb = 1; + } + useful = 1; } // if we didn't get at least an alt or a speed or a latlon or @@ -1816,6 +1878,10 @@ static void writeFATSV() continue; } + if (used_tisb) { + p += snprintf(p, bufsize(p,end), "\ttisb\t1"); + } + p += snprintf(p, bufsize(p,end), "\n"); if (p <= end) @@ -1825,9 +1891,6 @@ static void writeFATSV() # undef bufsize a->fatsv_last_emitted = now; - a->fatsv_emitted_altitude = alt; - a->fatsv_emitted_track = a->track; - a->fatsv_emitted_speed = a->speed; } } @@ -1840,7 +1903,7 @@ void modesNetPeriodicWork(void) { uint64_t now = mstime(); int need_flush = 0; - // Accept new connetions + // Accept new connections modesAcceptClients(); // Read from clients diff --git a/track.c b/track.c index e209562..d8f832b 100644 --- a/track.c +++ b/track.c @@ -2,7 +2,7 @@ // // track.c: aircraft state tracking // -// Copyright (c) 2014,2015 Oliver Jowett +// Copyright (c) 2014-2016 Oliver Jowett // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the @@ -48,6 +48,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "dump1090.h" +#include /* #define DEBUG_CPR_CHECKS */ @@ -74,7 +75,7 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { // time this ModeA/C is received again in the future if (mm->msgtype == 32) { a->modeACflags = MODEAC_MSG_FLAG; - if (!(mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID)) { + if (!mm->altitude_valid) { a->modeACflags |= MODEAC_MSG_MODEA_ONLY; } } @@ -103,6 +104,51 @@ struct aircraft *trackFindAircraft(uint32_t addr) { return (NULL); } +// Should we accept some new data from the given source? +// If so, update the validity and return 1 +static int accept_data(data_validity *d, datasource_t source, uint64_t now) +{ + if (source < d->source && now < d->stale) + return 0; + + d->source = source; + d->updated = now; + d->stale = now + 60000; + d->expires = now + 70000; + return 1; +} + +// Given two datasources, produce a third datasource for data combined from them. +static void combine_validity(data_validity *to, const data_validity *from1, const data_validity *from2) { + if (from1->source == SOURCE_INVALID) { + *to = *from2; + return; + } + + if (from2->source == SOURCE_INVALID) { + *to = *from1; + return; + } + + to->source = (from1->source < from2->source) ? from1->source : from2->source; // the worse of the two input sources + to->updated = (from1->updated > from2->updated) ? from1->updated : from2->updated; // the *later* of the two update times + to->stale = (from1->stale < from2->stale) ? from1->stale : from2->stale; // the earlier of the two stale times + to->expires = (from1->expires < from2->expires) ? from1->expires : from2->expires; // the earlier of the two expiry times +} + +static int compare_validity(const data_validity *lhs, const data_validity *rhs, uint64_t now) { + if (now < lhs->stale && lhs->source > rhs->source) + return 1; + else if (now < rhs->stale && lhs->source < rhs->source) + return -1; + else if (lhs->updated > rhs->updated) + return 1; + else if (lhs->updated < rhs->updated) + return -1; + else + return 0; +} + // // CPR position updating // @@ -149,7 +195,7 @@ static void update_range_histogram(double lat, double lon) // return true if it's OK for the aircraft to have travelled from its last known position // to a new position at (lat,lon,surface) at a time of now. -static int speed_check(struct aircraft *a, struct modesMessage *mm, double lat, double lon, uint64_t now, int surface) +static int speed_check(struct aircraft *a, double lat, double lon, uint64_t now, int surface) { uint64_t elapsed; double distance; @@ -157,17 +203,17 @@ static int speed_check(struct aircraft *a, struct modesMessage *mm, double lat, int speed; int inrange; - if (!(a->bFlags & MODES_ACFLAGS_LATLON_VALID)) + if (!trackDataValid(&a->position_valid)) return 1; // no reference, assume OK - elapsed = now - a->seenLatLon; + elapsed = trackDataAge(&a->position_valid, now); - if ((mm->bFlags & MODES_ACFLAGS_SPEED_VALID) && (a->bFlags & MODES_ACFLAGS_SPEED_VALID)) - speed = (mm->velocity + a->speed) / 2; - else if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) - speed = mm->velocity; - else if ((a->bFlags & MODES_ACFLAGS_SPEED_VALID) && (now - a->seenSpeed) < 30000) + if (trackDataValid(&a->speed_valid)) speed = a->speed; + else if (trackDataValid(&a->speed_ias_valid)) + speed = a->speed_ias * 4 / 3; + else if (trackDataValid(&a->speed_tas_valid)) + speed = a->speed_tas * 4 / 3; else speed = surface ? 100 : 600; // guess @@ -207,17 +253,17 @@ static int speed_check(struct aircraft *a, struct modesMessage *mm, double lat, static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double *lat, double *lon, unsigned *nuc) { int result; - int fflag = (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) != 0; - int surface = (mm->bFlags & MODES_ACFLAGS_AOG) != 0; + int fflag = mm->cpr_odd; + int surface = (mm->airground == AG_GROUND); - *nuc = (a->even_cprnuc < a->odd_cprnuc ? a->even_cprnuc : a->odd_cprnuc); // worst of the two positions + *nuc = (a->cpr_even_nuc < a->cpr_odd_nuc ? a->cpr_even_nuc : a->cpr_odd_nuc); // worst of the two positions if (surface) { // surface global CPR // find reference location double reflat, reflon; - if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first + if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID) <= 50000) { // Ok to try aircraft relative first reflat = a->lat; reflon = a->lon; if (a->pos_nuc < *nuc) @@ -231,27 +277,25 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now } result = decodeCPRsurface(reflat, reflon, - a->even_cprlat, a->even_cprlon, - a->odd_cprlat, a->odd_cprlon, + a->cpr_even_lat, a->cpr_even_lon, + a->cpr_odd_lat, a->cpr_odd_lon, fflag, lat, lon); } else { // airborne global CPR - result = decodeCPRairborne(a->even_cprlat, a->even_cprlon, - a->odd_cprlat, a->odd_cprlon, + result = decodeCPRairborne(a->cpr_even_lat, a->cpr_even_lon, + a->cpr_odd_lat, a->cpr_odd_lon, fflag, lat, lon); } if (result < 0) { #ifdef DEBUG_CPR_CHECKS - if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) { - fprintf(stderr, "CPR: decode failure from MLAT (%06X) (%d).\n", a->addr, result); - fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n", - a->even_cprlat, a->even_cprlon, - a->odd_cprlat, a->odd_cprlon, - fflag ? "odd" : "even"); - } + fprintf(stderr, "CPR: decode failure for %06X (%d).\n", a->addr, result); + fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n", + a->cpr_even_lat, a->cpr_even_lon, + a->cpr_odd_lat, a->cpr_odd_lon, + fflag ? "odd" : "even"); #endif return result; } @@ -271,11 +315,11 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now } // for mlat results, skip the speed check - if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) + if (mm->source == SOURCE_MLAT) return result; // check speed limit - if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && a->pos_nuc >= *nuc && !speed_check(a, mm, *lat, *lon, now, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, now, surface)) { Modes.stats_current.cpr_global_speed_checks++; return -2; } @@ -290,12 +334,12 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double reflat, reflon; double range_limit = 0; int result; - int fflag = (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) != 0; - int surface = (mm->bFlags & MODES_ACFLAGS_AOG) != 0; + int fflag = mm->cpr_odd; + int surface = (mm->airground == AG_GROUND); - *nuc = mm->nuc_p; + *nuc = mm->cpr_nucp; - if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { + if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID)) { reflat = a->lat; reflon = a->lon; @@ -331,12 +375,13 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, } result = decodeCPRrelative(reflat, reflon, - mm->raw_latitude, - mm->raw_longitude, + mm->cpr_lat, + mm->cpr_lon, fflag, surface, lat, lon); - if (result < 0) + if (result < 0) { return result; + } // check range limit if (range_limit > 0) { @@ -348,7 +393,10 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, } // check speed limit - if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && a->pos_nuc >= *nuc && !speed_check(a, mm, *lat, *lon, now, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, now, surface)) { +#ifdef DEBUG_CPR_CHECKS + fprintf(stderr, "Speed check for %06X with local decoding failed\n", a->addr); +#endif Modes.stats_current.cpr_local_speed_checks++; return -1; } @@ -356,73 +404,62 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, return 0; } +static uint64_t time_between(uint64_t t1, uint64_t t2) +{ + if (t1 >= t2) + return t1 - t2; + else + return t2 - t1; +} + static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t now) { int location_result = -1; - int max_elapsed; + uint64_t max_elapsed; double new_lat = 0, new_lon = 0; unsigned new_nuc = 0; + int surface; - if (mm->bFlags & MODES_ACFLAGS_AOG) + surface = (mm->airground == AG_GROUND); + + if (surface) { ++Modes.stats_current.cpr_surface; - else - ++Modes.stats_current.cpr_airborne; - if (mm->bFlags & MODES_ACFLAGS_AOG) { // Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise - - if ((mm->bFlags & MODES_ACFLAGS_SPEED_VALID) && mm->velocity <= 25) + if (mm->speed_valid && mm->speed <= 25) max_elapsed = 50000; else max_elapsed = 25000; } else { + ++Modes.stats_current.cpr_airborne; + // Airborne: 10 seconds max_elapsed = 10000; } - if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) { - a->odd_cprnuc = mm->nuc_p; - a->odd_cprlat = mm->raw_latitude; - a->odd_cprlon = mm->raw_longitude; - a->odd_cprtime = now; - } else { - a->even_cprnuc = mm->nuc_p; - a->even_cprlat = mm->raw_latitude; - a->even_cprlon = mm->raw_longitude; - a->even_cprtime = now; - } - // If we have enough recent data, try global CPR - if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= max_elapsed) { + if (trackDataValid(&a->cpr_odd_valid) && trackDataValid(&a->cpr_even_valid) && + a->cpr_odd_valid.source == a->cpr_even_valid.source && + a->cpr_odd_airground == a->cpr_even_airground && + time_between(a->cpr_odd_valid.updated, a->cpr_even_valid.updated) <= max_elapsed) { + location_result = doGlobalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc); if (location_result == -2) { #ifdef DEBUG_CPR_CHECKS - if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) { - fprintf(stderr, "CPR failure from MLAT (%06X).\n", a->addr); - } + fprintf(stderr, "global CPR failure (invalid) for (%06X).\n", a->addr); #endif // Global CPR failed because the position produced implausible results. // This is bad data. Discard both odd and even messages and wait for a fresh pair. // Also disable aircraft-relative positions until we have a new good position (but don't discard the // recorded position itself) Modes.stats_current.cpr_global_bad++; - a->bFlags &= ~(MODES_ACFLAGS_LATLON_REL_OK | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID); - - // Also discard the current message's data as it is suspect - we don't want - // to update any of the aircraft state from this. - mm->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID | - MODES_ACFLAGS_ALTITUDE_VALID | - MODES_ACFLAGS_SPEED_VALID | - MODES_ACFLAGS_HEADING_VALID | - MODES_ACFLAGS_NSEWSPD_VALID | - MODES_ACFLAGS_VERTRATE_VALID | - MODES_ACFLAGS_AOG_VALID | - MODES_ACFLAGS_AOG); + a->cpr_odd_valid.source = a->cpr_even_valid.source = a->position_valid.source = SOURCE_INVALID; + return; } else if (location_result == -1) { #ifdef DEBUG_CPR_CHECKS - if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) { + if (mm->source == SOURCE_MLAT) { fprintf(stderr, "CPR skipped from MLAT (%06X).\n", a->addr); } #endif @@ -431,6 +468,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t Modes.stats_current.cpr_global_skipped++; } else { Modes.stats_current.cpr_global_ok++; + combine_validity(&a->position_valid, &a->cpr_even_valid, &a->cpr_odd_valid); } } @@ -438,30 +476,30 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t if (location_result == -1) { location_result = doLocalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc); - if (location_result == -1) { + if (location_result < 0) { Modes.stats_current.cpr_local_skipped++; } else { Modes.stats_current.cpr_local_ok++; - if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) - Modes.stats_current.cpr_local_aircraft_relative++; - else - Modes.stats_current.cpr_local_receiver_relative++; - mm->bFlags |= MODES_ACFLAGS_REL_CPR_USED; + mm->cpr_relative = 1; + + if (mm->cpr_odd) { + a->position_valid = a->cpr_odd_valid; + } else { + a->position_valid = a->cpr_even_valid; + } } } if (location_result == 0) { // If we sucessfully decoded, back copy the results to mm so that we can print them in list output - mm->bFlags |= MODES_ACFLAGS_LATLON_VALID; - mm->fLat = new_lat; - mm->fLon = new_lon; + mm->cpr_decoded = 1; + mm->decoded_lat = new_lat; + mm->decoded_lon = new_lon; // Update aircraft state - a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); a->lat = new_lat; a->lon = new_lon; a->pos_nuc = new_nuc; - a->seenLatLon = a->seen; update_range_histogram(new_lat, new_lon); } @@ -493,126 +531,100 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->seen = now; a->messages++; - // if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags - if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) { - a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG); - } - - // If we've got a new cprlat or cprlon - if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) { - updatePosition(a, mm, now); - } + if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) { + unsigned modeC = (a->altitude + 49) / 100; + if (modeC != a->altitude_modeC) { + a->modeCcount = 0; //....zero the hit count + a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; + } - // If a (new) CALLSIGN has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) { - memcpy(a->flight, mm->flight, sizeof(a->flight)); + a->altitude = mm->altitude; + a->altitude_modeC = modeC; } - // If a (new) ALTITUDE has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { - if ( (a->modeCcount) // if we've a modeCcount already - && (a->altitude != mm->altitude ) ) // and Altitude has changed -// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet -// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet - { - a->modeCcount = 0; //....zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; - } - - // If we received an altitude in a (non-mlat) DF17/18 squitter recently, ignore - // DF0/4/16/20 altitudes as single-bit errors can attribute them to the wrong - // aircraft - if ((a->bFlags & ~a->mlatFlags & MODES_ACFLAGS_ALTITUDE_VALID) && - (now - a->seenAltitude) < 15000 && - (a->bFlags & ~a->mlatFlags & MODES_ACFLAGS_LATLON_VALID) && - (now - a->seenLatLon) < 15000 && - mm->msgtype != 17 && - mm->msgtype != 18) { - Modes.stats_current.suppressed_altitude_messages++; - } else { - a->altitude = mm->altitude; - a->modeC = (mm->altitude + 49) / 100; - a->seenAltitude = now; - - // reporting of HAE and baro altitudes is mutually exclusive - // so if we see a baro altitude, assume the HAE altitude is invalid - // we will recalculate it from baro + HAE delta below, where possible - a->bFlags &= ~MODES_ACFLAGS_ALTITUDE_HAE_VALID; + if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source, now)) { + if (mm->squawk != a->squawk) { + a->modeAcount = 0; //....zero the hit count + a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; } + a->squawk = mm->squawk; } - // If a (new) HAE altitude has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID) { - a->altitude_hae = mm->altitude_hae; + if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GNSS && accept_data(&a->altitude_gnss_valid, mm->source, now)) { + a->altitude_gnss = mm->altitude; + } - // reporting of HAE and baro altitudes is mutually exclusive - // if you have both, you're meant to report baro and a HAE delta, - // so if we see explicit HAE then assume the delta is invalid too - a->bFlags &= ~(MODES_ACFLAGS_ALTITUDE_VALID | MODES_ACFLAGS_HAE_DELTA_VALID); + if (mm->gnss_delta_valid && accept_data(&a->gnss_delta_valid, mm->source, now)) { + a->gnss_delta = mm->gnss_delta; } - // If a (new) HAE/barometric difference has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID) { - a->hae_delta = mm->hae_delta; + if (mm->heading_valid && mm->heading_source == HEADING_TRUE && accept_data(&a->heading_valid, mm->source, now)) { + a->heading = mm->heading; + } - // reporting of HAE and baro altitudes is mutually exclusive - // if you have both, you're meant to report baro and a HAE delta, - // so if we see a HAE delta then assume the HAE altitude is invalid - a->bFlags &= ~MODES_ACFLAGS_ALTITUDE_HAE_VALID; + if (mm->heading_valid && mm->heading_source == HEADING_MAGNETIC && accept_data(&a->heading_magnetic_valid, mm->source, now)) { + a->heading_magnetic = mm->heading; } - // If a (new) SQUAWK has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - if (a->modeA != mm->modeA) { - a->modeAcount = 0; // Squawk has changed, so zero the hit count - a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; - } - a->modeA = mm->modeA; + if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED && accept_data(&a->speed_valid, mm->source, now)) { + a->speed = mm->speed; } - // If a (new) HEADING has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { - a->track = mm->heading; - a->seenTrack = now; + if (mm->speed_valid && mm->speed_source == SPEED_IAS && accept_data(&a->speed_ias_valid, mm->source, now)) { + a->speed_ias = mm->speed; } - // If a (new) SPEED has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { - a->speed = mm->velocity; - a->seenSpeed = now; + if (mm->speed_valid && mm->speed_source == SPEED_TAS && accept_data(&a->speed_tas_valid, mm->source, now)) { + a->speed_tas = mm->speed; } - // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { + if (mm->vert_rate_valid && accept_data(&a->vert_rate_valid, mm->source, now)) { a->vert_rate = mm->vert_rate; + a->vert_rate_source = mm->vert_rate_source; } - // If a (new) category has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_CATEGORY_VALID) { + if (mm->category_valid && accept_data(&a->category_valid, mm->source, now)) { a->category = mm->category; } - // Update the aircrafts a->bFlags to reflect the newly received mm->bFlags; - a->bFlags |= mm->bFlags; + if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source, now)) { + a->airground = mm->airground; + } - // If we have a baro altitude and a HAE delta from baro, calculate the HAE altitude - if ((a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) { - a->altitude_hae = a->altitude + a->hae_delta; - a->bFlags |= MODES_ACFLAGS_ALTITUDE_HAE_VALID; + if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source, now)) { + memcpy(a->callsign, mm->callsign, sizeof(a->callsign)); } - // Update mlat flags. The mlat flags indicate which bits in bFlags - // were last set based on a mlat-derived message. - if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) - a->mlatFlags = (a->mlatFlags & a->bFlags) | mm->bFlags; - else - a->mlatFlags = (a->mlatFlags & a->bFlags) & ~mm->bFlags; + // CPR, even + if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) { + a->cpr_even_airground = mm->airground; + a->cpr_even_lat = mm->cpr_lat; + a->cpr_even_lon = mm->cpr_lon; + a->cpr_even_nuc = mm->cpr_nucp; + } - // Same for TIS-B - if (mm->bFlags & MODES_ACFLAGS_FROM_TISB) - a->tisbFlags = (a->tisbFlags & a->bFlags) | mm->bFlags; - else - a->tisbFlags = (a->tisbFlags & a->bFlags) & ~mm->bFlags; + // CPR, odd + if (mm->cpr_valid && mm->cpr_odd && accept_data(&a->cpr_odd_valid, mm->source, now)) { + a->cpr_odd_airground = mm->airground; + a->cpr_odd_lat = mm->cpr_lat; + a->cpr_odd_lon = mm->cpr_lon; + a->cpr_odd_nuc = mm->cpr_nucp; + } + + // Now handle derived data + + // derive GNSS if we have baro + delta + if (compare_validity(&a->altitude_valid, &a->altitude_gnss_valid, now) > 0 && + compare_validity(&a->gnss_delta_valid, &a->altitude_gnss_valid, now) > 0) { + // Baro and delta are both more recent than GNSS, derive GNSS from baro + delta + a->altitude_gnss = a->altitude + a->gnss_delta; + combine_validity(&a->altitude_gnss_valid, &a->altitude_valid, &a->gnss_delta_valid); + } + + // If we've got a new cprlat or cprlon + if (mm->cpr_valid) { + updatePosition(a, mm, now); + } if (mm->msgtype == 32) { int flags = a->modeACflags; @@ -670,9 +682,9 @@ static void trackUpdateAircraftModeA(struct aircraft *a) if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) { // skip any fudged ICAO records // If both (a) and (b) have valid squawks... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) { + if (trackDataValid(&a->squawk_valid) && trackDataValid(&b->squawk_valid)) { // ...check for Mode-A == Mode-S Squawk matches - if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk + if (a->squawk == b->squawk) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk b->modeAcount = a->messages; b->modeACflags |= MODEAC_MSG_MODEA_HIT; a->modeACflags |= MODEAC_MSG_MODEA_HIT; @@ -684,11 +696,11 @@ static void trackUpdateAircraftModeA(struct aircraft *a) } // If both (a) and (b) have valid altitudes... - if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) { + if (trackDataValid(&a->altitude_valid) && trackDataValid(&b->altitude_valid)) { // ... check for Mode-C == Mode-S Altitude matches - if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude - || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft - || (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft + if ( (a->altitude_modeC == b->altitude_modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude + || (a->altitude_modeC == b->altitude_modeC + 1) // or this Mode-C - 100 ft + || (a->altitude_modeC + 1 == b->altitude_modeC ) ) { // or this Mode-C + 100 ft b->modeCcount = a->messages; b->modeACflags |= MODEAC_MSG_MODEC_HIT; a->modeACflags |= MODEAC_MSG_MODEC_HIT; @@ -748,10 +760,25 @@ static void trackRemoveStaleAircraft(uint64_t now) prev->next = a->next; free(a); a = prev->next; } } else { - if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && (now - a->seenLatLon) > TRACK_AIRCRAFT_POSITION_TTL) { - /* Position is too old and no longer valid */ - a->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); - } + +#define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0) + EXPIRE(callsign); + EXPIRE(altitude); + EXPIRE(altitude_gnss); + EXPIRE(gnss_delta); + EXPIRE(speed); + EXPIRE(speed_ias); + EXPIRE(speed_tas); + EXPIRE(heading); + EXPIRE(heading_magnetic); + EXPIRE(vert_rate); + EXPIRE(squawk); + EXPIRE(category); + EXPIRE(airground); + EXPIRE(cpr_odd); + EXPIRE(cpr_even); + EXPIRE(position); + prev = a; a = a->next; } } diff --git a/track.h b/track.h index 21becb5..1e0ef23 100644 --- a/track.h +++ b/track.h @@ -2,7 +2,7 @@ // // track.h: aircraft state tracking prototypes // -// Copyright (c) 2014,2015 Oliver Jowett +// Copyright (c) 2014-2016 Oliver Jowett // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the @@ -59,63 +59,131 @@ /* Maximum validity of an aircraft position */ #define TRACK_AIRCRAFT_POSITION_TTL 60000 +typedef struct { + datasource_t source; /* where the data came from */ + uint64_t updated; /* when it arrived */ + uint64_t stale; /* when it will become stale */ + uint64_t expires; /* when it will expire */ +} data_validity; + /* Structure used to describe the state of one tracked aircraft */ struct aircraft { uint32_t addr; // ICAO address - char flight[16]; // Flight number + + uint64_t seen; // Time (millis) at which the last packet was received + long messages; // Number of Mode S messages received + double signalLevel[8]; // Last 8 Signal Amplitudes int signalNext; // next index of signalLevel to use - int altitude; // Altitude (Baro) - int altitude_hae; // Altitude (HAE) - int hae_delta; // Difference between HAE and Baro altitudes - int speed; // Velocity - int track; // Angle of flight - int vert_rate; // Vertical rate. - uint64_t seen; // Time (millis) at which the last packet was received - uint64_t seenLatLon; // Time (millis) at which lat, lon was measured - uint64_t seenAltitude; // Time (millis) at which altitude was measured - uint64_t seenSpeed; // Time (millis) at which speed was measured - uint64_t seenTrack; // Time (millis) at which track was measured + data_validity callsign_valid; + char callsign[9]; // Flight number - int mlatFlags; // Data derived from mlat messages - int tisbFlags; // Data derived from TIS-B messages + data_validity altitude_valid; + int altitude; // Altitude (Baro) + unsigned altitude_modeC; // (as a Mode C value) - long messages; // Number of Mode S messages received - int modeA; // Squawk - int modeC; // Altitude - long modeAcount; // Mode A Squawk hit Count - long modeCcount; // Mode C Altitude hit Count - int modeACflags; // Flags for mode A/C recognition + data_validity altitude_gnss_valid; + int altitude_gnss; // Altitude (GNSS) + + data_validity gnss_delta_valid; + int gnss_delta; // Difference between GNSS and Baro altitudes + + data_validity speed_valid; + unsigned speed; + + data_validity speed_ias_valid; + unsigned speed_ias; + + data_validity speed_tas_valid; + unsigned speed_tas; - int fatsv_emitted_altitude; // last FA emitted altitude - int fatsv_emitted_track; // last FA emitted track - int fatsv_emitted_speed; // last FA emitted speed - uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted + data_validity heading_valid; + unsigned heading; // Heading (OK it's really the track) - // Encoded latitude and longitude as extracted by odd and even CPR encoded messages - uint64_t odd_cprtime; - int odd_cprlat; - int odd_cprlon; - unsigned odd_cprnuc; + data_validity heading_magnetic_valid; + unsigned heading_magnetic; // Heading - uint64_t even_cprtime; - int even_cprlat; - int even_cprlon; - unsigned even_cprnuc; + data_validity vert_rate_valid; + int vert_rate; // Vertical rate + altitude_source_t vert_rate_source; + data_validity squawk_valid; + unsigned squawk; // Squawk + + data_validity category_valid; + unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte + + data_validity airground_valid; + airground_t airground; // air/ground status + + data_validity cpr_odd_valid; // Last seen even CPR message + airground_t cpr_odd_airground; + unsigned cpr_odd_lat; + unsigned cpr_odd_lon; + unsigned cpr_odd_nuc; + + data_validity cpr_even_valid; // Last seen odd CPR message + airground_t cpr_even_airground; + unsigned cpr_even_lat; + unsigned cpr_even_lon; + unsigned cpr_even_nuc; + + data_validity position_valid; double lat, lon; // Coordinated obtained from CPR encoded data unsigned pos_nuc; // NUCp of last computed position - unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte + long modeAcount; // Mode A Squawk hit Count + long modeCcount; // Mode C Altitude hit Count + int modeACflags; // Flags for mode A/C recognition + + int fatsv_emitted_altitude; // last FA emitted altitude + int fatsv_emitted_altitude_gnss; // -"- GNSS altitude + int fatsv_emitted_heading; // -"- true track + int fatsv_emitted_heading_magnetic; // -"- magnetic heading + int fatsv_emitted_speed; // -"- groundspeed + int fatsv_emitted_speed_ias; // -"- IAS + int fatsv_emitted_speed_tas; // -"- TAS + airground_t fatsv_emitted_airground; // -"- air/ground state + + uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted - int bFlags; // Flags related to valid fields in this structure struct aircraft *next; // Next aircraft in our linked list struct modesMessage first_message; // A copy of the first message we received for this aircraft. }; - +/* is this bit of data valid? */ +static inline int trackDataValid(const data_validity *v) +{ + return (v->source != SOURCE_INVALID); +} + +/* .. with these constraints? */ +static inline int trackDataValidEx(const data_validity *v, + uint64_t now, + uint64_t maxAge, + datasource_t minSource) +{ + if (v->source == SOURCE_INVALID) + return 0; + if (v->source < minSource) + return 0; + if (v->updated < now && (now - v->updated) > maxAge) + return 0; + return 1; +} + +/* what's the age of this data? */ +static inline uint64_t trackDataAge(const data_validity *v, + uint64_t now) +{ + if (v->source == SOURCE_INVALID) + return ~(uint64_t)0; + if (v->updated >= now) + return 0; + return (now - v->updated); +} /* Update aircraft state from data in the provided mesage. * Return the tracked aircraft.