From 54ca2c7fb2de006a09825e9c189cbe845762cc19 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Aug 2016 14:34:14 +0100 Subject: [PATCH 01/18] WIP big rewrite of message decoding / aircraft tracking. --- demod_2400.c | 1 - dump1090.c | 2 +- dump1090.h | 180 ++++++--- interactive.c | 37 +- mode_ac.c | 15 +- mode_s.c | 1029 ++++++++++++++++++++++++++++--------------------- net_io.c | 453 ++++++++++++---------- track.c | 407 ++++++++++--------- track.h | 148 +++++-- 9 files changed, 1313 insertions(+), 959 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; + datasource_t source; // Characterizes the overall message source - // 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 + // 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 + // 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 - // DF 18 - int cf; // Control Field + airground_t airground; // air/ground state - // DF4, DF5, DF20, DF21 - int fs; // Flight status for DF4,5,20,21 - int modeA; // 13 bits identity (Squawk). - - // DF20, DF21 - int bds; // BDS value implied if overlay control was used - - // 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 + if (mm->crc != 0) { + ei = modesChecksumDiagnose(mm->crc, mm->msgbits); + if (!ei) { + return -2; // couldn't fix it + } - 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)); +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", +}; - // 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_to_string(unsigned df) { + if (df > 32) + return "out of range"; + if (!df_names[df]) + return "reserved"; + return df_names[df]; +} - 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"); - } - } 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); - } - } 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); - } - } 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); - } - } else { - printf(" Unrecognized ME type: %d subtype: %d\n", mm->metype, mm->mesub); +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 void displayCommB(struct modesMessage *mm) -{ - if (mm->bds != 0) - printf(" Comm-B BDS : %02x (maybe)\n", mm->bds); +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)"; + } +} - // 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"); - } +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"; + } + + 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"; + } + + 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"; + } + + case 29: + switch (mesub) { + case 1: + return "Target state and status"; + default: + return "Unknown"; + } + + case 31: // Aircraft Operational Status + switch (mesub) { + case 0: + return "Aircraft operational status (airborne)"; + case 1: + return "Aircraft operational status (surface)"; + default: + return "Unknown"; + } + + 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); + 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; - } 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); + 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; - 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); + 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; - 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);} + case 11: + printf("DF:11 AA:%06X IID:%u CA:%u\n", + mm->AA, mm->IID, mm->CA); + break; - } 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); + 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 == 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); + 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; + + 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; + + 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; + } + + 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(" ICAO Address : %06x\n", mm->addr); - } - if ((mm->cf == 0) || (mm->cf == 1) || (mm->cf == 2) || (mm->cf == 5) || (mm->cf == 6)) { - displayExtendedSquitter(mm); - } - - } else if (mm->msgtype == 19) { // DF 19 - printf("DF 19: Military Extended Squitter.\n"); - - } else if (mm->msgtype == 22) { // DF 22 - printf("DF 22: Military Use.\n"); - - } else if (mm->msgtype == 24) { // DF 24 - printf("DF 24: Comm D Extended Length Message.\n"); - - } 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); - } 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, ",");} + if (mm->callsign_valid) {p += sprintf(p, ",%s", mm->callsign);} + 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); + // 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 speedIASValid = 0; + int speedTASValid = 0; + int headingValid = 0; + int headingMagValid = 0; + int airgroundValid = 0; - int trackValid = 0; - uint64_t trackAge = 999999; - - 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); + 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); - // 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; - } + // 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); - useful = 1; - } - - if (speedValid && speedAge < emittedAge) { - p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed); - useful = 1; - } - - if (groundValid) { - if (ground) { - p += snprintf(p, bufsize(p,end), "\tairGround\tG"); - } else { - p += snprintf(p, bufsize(p,end), "\tairGround\tA"); + 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 (latlonValid && latlonAge < 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 (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 (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 (trackValid && trackAge < emittedAge) { - p += snprintf(p, bufsize(p,end), "\theading\t%d", a->track); + 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 (used_tisb) { - p += snprintf(p, bufsize(p,end), "\ttisb\t1"); + 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 (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); + a->cpr_odd_valid.source = a->cpr_even_valid.source = a->position_valid.source = SOURCE_INVALID; - // 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); 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 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)); - } - - // 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 + 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 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; } + + a->altitude = mm->altitude; + a->altitude_modeC = modeC; } - // 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; - - // 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 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; - - // 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 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 + 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->modeA = mm->modeA; + a->squawk = mm->squawk; } - // 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->altitude_valid && mm->altitude_source == ALTITUDE_GNSS && accept_data(&a->altitude_gnss_valid, mm->source, now)) { + a->altitude_gnss = mm->altitude; } - // 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->gnss_delta_valid && accept_data(&a->gnss_delta_valid, mm->source, now)) { + a->gnss_delta = mm->gnss_delta; } - // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure - if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { + if (mm->heading_valid && mm->heading_source == HEADING_TRUE && accept_data(&a->heading_valid, mm->source, now)) { + a->heading = mm->heading; + } + + if (mm->heading_valid && mm->heading_source == HEADING_MAGNETIC && accept_data(&a->heading_magnetic_valid, mm->source, now)) { + a->heading_magnetic = mm->heading; + } + + if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED && accept_data(&a->speed_valid, mm->source, now)) { + a->speed = mm->speed; + } + + if (mm->speed_valid && mm->speed_source == SPEED_IAS && accept_data(&a->speed_ias_valid, mm->source, now)) { + a->speed_ias = mm->speed; + } + + if (mm->speed_valid && mm->speed_source == SPEED_TAS && accept_data(&a->speed_tas_valid, mm->source, now)) { + a->speed_tas = mm->speed; + } + + 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 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->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source, now)) { + a->airground = mm->airground; } - // 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; + if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source, now)) { + memcpy(a->callsign, mm->callsign, sizeof(a->callsign)); + } - // 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, 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; + } + + // 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 - 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 - - int mlatFlags; // Data derived from mlat messages - int tisbFlags; // Data derived from TIS-B messages - long messages; // Number of Mode S messages received - int modeA; // Squawk - int modeC; // Altitude + + double signalLevel[8]; // Last 8 Signal Amplitudes + int signalNext; // next index of signalLevel to use + + data_validity callsign_valid; + char callsign[9]; // Flight number + + data_validity altitude_valid; + int altitude; // Altitude (Baro) + unsigned altitude_modeC; // (as a Mode C value) + + 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; + + data_validity heading_valid; + unsigned heading; // Heading (OK it's really the track) + + data_validity heading_magnetic_valid; + unsigned heading_magnetic; // Heading + + 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 + 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_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 + 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 - // 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; + uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted - uint64_t even_cprtime; - int even_cprlat; - int even_cprlon; - unsigned even_cprnuc; - - 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 - - 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. From 954034855a71ab2e69cc43564461c075bd798a00 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 28 Aug 2016 01:29:37 +0100 Subject: [PATCH 02/18] Make faup1090 emit some interesting messages when they occur. --- net_io.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ track.c | 3 +++ track.h | 3 +++ 3 files changed, 88 insertions(+) diff --git a/net_io.c b/net_io.c index 4d88cd1..10e7166 100644 --- a/net_io.c +++ b/net_io.c @@ -77,6 +77,8 @@ static void send_raw_heartbeat(struct net_service *service); static void send_beast_heartbeat(struct net_service *service); static void send_sbs_heartbeat(struct net_service *service); +static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a); + // //========================================================================= // @@ -725,6 +727,10 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) { // Forward mlat messages via beast output only if --forward-mlat is set modesSendBeastOutput(mm); } + + if (!is_mlat) { + writeFATSVEvent(mm, a); + } } // //========================================================================= @@ -1652,6 +1658,82 @@ static void modesReadFromClient(struct client *c) { #define TSV_MAX_PACKET_SIZE 180 +static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafield, unsigned char *data, size_t len) +{ + char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); + if (!p) + return; + + char *end = p + TSV_MAX_PACKET_SIZE; +# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) + + p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64 "\thexid\t%06X\t%s\t", mstime() / 1000, mm->addr, datafield); + + for (size_t i = 0; i < len; ++i) { + p += snprintf(p, bufsize(p, end), "%02X", data[i]); + } + + p += snprintf(p, bufsize(p, end), "\n"); + + if (p <= end) + completeWrite(&Modes.fatsv_out, p); + else + fprintf(stderr, "fatsv: output too large (max %d, overran by %d)\n", TSV_MAX_PACKET_SIZE, (int) (p - end)); +# undef bufsize +} + +static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) +{ + // Write event records for a couple of message types. + + if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) { + return; // not enabled or no active connections + } + + // skip non-ICAO + if (mm->addr & MODES_NON_ICAO_ADDRESS) + return; + + if (a->messages < 2) // basic filter for bad decodes + return; + + switch (mm->msgtype) { + case 20: + case 21: + if (mm->correctedbits > 0) + break; // only messages we trust a little more + + // DF 20/21: Comm-B: emit if they've changed since we last sent them + // + // BDS 1,0: data link capability report + // BDS 3,0: ACAS RA report + if (mm->MB[0] == 0x10 && memcmp(mm->MB, a->fatsv_emitted_bds_10, 7) != 0) { + memcpy(a->fatsv_emitted_bds_10, mm->MB, 7); + writeFATSVEventMessage(mm, "datalink_caps", mm->MB, 7); + } + + else if (mm->MB[0] == 0x30 && memcmp(mm->MB, a->fatsv_emitted_bds_30, 7) != 0) { + memcpy(a->fatsv_emitted_bds_30, mm->MB, 7); + writeFATSVEventMessage(mm, "acas_ra", mm->MB, 7); + } + + break; + + case 17: + // DF 17: extended squitter + // type 28 subtype 2: ACAS RA report + // first byte has the type/subtype, remaining bytes match the BDS 3,0 format + if (mm->metype == 28 && mm->mesub == 2 && memcmp(&mm->ME[1], &a->fatsv_emitted_bds_30[1], 6) != 0) { + memcpy(a->fatsv_emitted_bds_30, &mm->ME[1], 6); + writeFATSVEventMessage(mm, "acas_ra", mm->ME, 7); + } else if (mm->metype == 31 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_status, 7) != 0) { + memcpy(a->fatsv_emitted_es_status, mm->ME, 7); + writeFATSVEventMessage(mm, "es_op_status", mm->ME, 7); + } + break; + } +} + static void writeFATSV() { struct aircraft *a; diff --git a/track.c b/track.c index d8f832b..02fd026 100644 --- a/track.c +++ b/track.c @@ -70,6 +70,9 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { a->signalLevel[i] = 1e-5; a->signalNext = 0; + // start off with the "last emitted" ACAS RA being blank (just the BDS 3,0 code) + a->fatsv_emitted_bds_30[0] = 0x30; + // mm->msgtype 32 is used to represent Mode A/C. These values can never change, so // set them once here during initialisation, and don't bother to set them every // time this ModeA/C is received again in the future diff --git a/track.h b/track.h index 1e0ef23..0e27fc1 100644 --- a/track.h +++ b/track.h @@ -145,6 +145,9 @@ struct aircraft { int fatsv_emitted_speed_ias; // -"- IAS int fatsv_emitted_speed_tas; // -"- TAS airground_t fatsv_emitted_airground; // -"- air/ground state + unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message + unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message + unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted From 08887642eab8414a371f3236ff589564628eeaa2 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 28 Aug 2016 12:51:03 +0100 Subject: [PATCH 03/18] Rename the ACAS stuff a bit, emit ES target state. --- net_io.c | 7 +++++-- track.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/net_io.c b/net_io.c index 10e7166..35ee7e6 100644 --- a/net_io.c +++ b/net_io.c @@ -1714,7 +1714,7 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) else if (mm->MB[0] == 0x30 && memcmp(mm->MB, a->fatsv_emitted_bds_30, 7) != 0) { memcpy(a->fatsv_emitted_bds_30, mm->MB, 7); - writeFATSVEventMessage(mm, "acas_ra", mm->MB, 7); + writeFATSVEventMessage(mm, "commb_acas_ra", mm->MB, 7); } break; @@ -1725,10 +1725,13 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) // first byte has the type/subtype, remaining bytes match the BDS 3,0 format if (mm->metype == 28 && mm->mesub == 2 && memcmp(&mm->ME[1], &a->fatsv_emitted_bds_30[1], 6) != 0) { memcpy(a->fatsv_emitted_bds_30, &mm->ME[1], 6); - writeFATSVEventMessage(mm, "acas_ra", mm->ME, 7); + writeFATSVEventMessage(mm, "es_acas_ra", mm->ME, 7); } else if (mm->metype == 31 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_status, 7) != 0) { memcpy(a->fatsv_emitted_es_status, mm->ME, 7); writeFATSVEventMessage(mm, "es_op_status", mm->ME, 7); + } else if (mm->metype == 29 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_target, 7) != 0) { + memcpy(a->fatsv_emitted_es_target, mm->ME, 7); + writeFATSVEventMessage(mm, "es_target", mm->ME, 7); } break; } diff --git a/track.h b/track.h index 0e27fc1..1cab266 100644 --- a/track.h +++ b/track.h @@ -148,6 +148,7 @@ struct aircraft { unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message + unsigned char fatsv_emitted_es_target[7]; // -"- ES target status message uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted From 3e08de91edc4b4d40e69e5df1a475e6b9b00b817 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 29 Aug 2016 11:11:04 +0100 Subject: [PATCH 04/18] WIP on decoding operational status & target state messages. --- dump1090.h | 65 ++++++++++++++++++ mode_s.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) diff --git a/dump1090.h b/dump1090.h index 462f38c..0a4cb73 100644 --- a/dump1090.h +++ b/dump1090.h @@ -177,6 +177,10 @@ typedef enum { HEADING_MAGNETIC } heading_source_t; +typedef enum { + SIL_PER_SAMPLE, SIL_PER_HOUR +} sil_type_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -448,6 +452,67 @@ struct modesMessage { // valid if cpr_decoded: double decoded_lat; double decoded_lon; + + // Operational Status + struct { + unsigned valid : 1; + unsigned version : 3; + + unsigned om_acas_ra : 1; + unsigned om_ident : 1; + unsigned om_atc : 1; + unsigned om_saf : 1; + unsigned om_sda : 2; + + unsigned cc_acas : 1; + unsigned cc_cdti : 1; + unsigned cc_1090_in : 1; + unsigned cc_arv : 1; + unsigned cc_ts : 1; + unsigned cc_tc : 2; + unsigned cc_uat_in : 1; + unsigned cc_poa : 1; + unsigned cc_b2_low : 1; + unsigned cc_nac_v : 3; + unsigned cc_nic_supp_c : 1; + unsigned cc_lw_valid : 1; + + unsigned nic_supp_a : 1; + unsigned nac_p : 4; + unsigned gva : 2; + unsigned sil : 2; + unsigned nic_baro : 1; + + sil_type_t sil_type; + enum { ANGLE_HEADING, ANGLE_TRACK } track_angle; + heading_source_t hrd; + + unsigned cc_lw; + unsigned cc_antenna_offset; + } opstatus; + + // Target State & Status (ADS-B V2 only) + struct { + unsigned valid : 1; + unsigned altitude_valid : 1; + unsigned baro_valid : 1; + unsigned heading_valid : 1; + unsigned mode_valid : 1; + unsigned mode_autopilot : 1; + unsigned mode_vnav : 1; + unsigned mode_alt_hold : 1; + unsigned mode_approach : 1; + unsigned acas_operational : 1; + unsigned nac_p : 4; + unsigned nic_baro : 1; + unsigned sil : 2; + + sil_type_t sil_type; + enum { TSS_ALTITUDE_MCP, TSS_ALTITUDE_FMS } altitude_type; + unsigned altitude; + float baro; + unsigned heading; + } tss; }; // This one needs modesMessage: diff --git a/mode_s.c b/mode_s.c index 6cc94c1..1ffee1f 100644 --- a/mode_s.c +++ b/mode_s.c @@ -702,6 +702,7 @@ static void decodeBDS20(struct modesMessage *mm) static void decodeExtendedSquitter(struct modesMessage *mm) { unsigned char *msg = mm->msg; + unsigned char *me = mm->ME; int metype = mm->metype = msg[4] >> 3; // Extended squitter message type int mesub = mm->mesub = (metype == 29 ? ((msg[4]&6)>>1) : (msg[4] & 7)); // Extended squitter message subtype @@ -956,6 +957,48 @@ static void decodeExtendedSquitter(struct modesMessage *mm) } case 29: // Aircraft Trajectory Intent + if (mesub == 1) { // Target state and status, V2 + mm->tss.valid = 1; + mm->tss.sil_type = (me[0] & 0x01) ? SIL_PER_SAMPLE : SIL_PER_HOUR; + mm->tss.altitude_type = (me[1] & 0x80) ? TSS_ALTITUDE_FMS : TSS_ALTITUDE_MCP; + + unsigned alt_bits = ((me[1] << 4) | (me[2] >> 4)) & 0x7FF; + if (alt_bits == 0) { + mm->tss.altitude_valid = 0; + } else { + mm->tss.altitude_valid = 1; + mm->tss.altitude = (alt_bits - 1) * 32; + } + + unsigned baro_bits = ((me[2] << 5) | (me[3] >> 3)) & 0x1FF; + if (baro_bits == 0) { + mm->tss.baro_valid = 0; + } else { + mm->tss.baro_valid = 1; + mm->tss.baro = 800.0 + (baro_bits - 1) * 0.8; + } + + mm->tss.heading_valid = (me[3] & 0x04) != 0; + if (mm->tss.heading_valid) { + // two's complement -180..+180, which is conveniently + // also the same as unsigned 0..360 + unsigned heading_bits = ((me[3] << 7) | (me[4] >> 1)) & 0x1ff; + mm->tss.heading = heading_bits * 180 / 256; + } + + mm->tss.nac_p = ((me[4] << 3) | (me[5] >> 5)) & 0x0f; + mm->tss.nic_baro = (me[5] & 0x10) ? 1 : 0; + mm->tss.sil = (me[5] >> 2) & 0x03; + mm->tss.mode_valid = (me[5] & 0x02) ? 1 : 0; + if (mm->tss.mode_valid) { + mm->tss.mode_autopilot = (me[5] & 0x01) != 0; + mm->tss.mode_vnav = (me[6] & 0x80) != 0; + mm->tss.mode_alt_hold = (me[6] & 0x40) != 0; + mm->tss.mode_approach = (me[6] & 0x10) != 0; + } + + mm->tss.acas_operational = (me[6] & 0x08) != 0; + } break; case 30: // Aircraft Operational Coordination @@ -964,6 +1007,94 @@ static void decodeExtendedSquitter(struct modesMessage *mm) case 31: // Aircraft Operational Status if (check_imf && (msg[10] & 0x01)) mm->addr |= MODES_NON_ICAO_ADDRESS; + + if (mm->mesub == 0 || mm->mesub == 1) { + mm->opstatus.valid = 1; + mm->opstatus.version = (me[5] >> 5) & 0x07; + + switch (mm->opstatus.version) { + case 0: + break; + + case 1: + if ((me[3] & 0xC0) == 0) { + mm->opstatus.om_acas_ra = (me[3] & 0x20) != 0; + mm->opstatus.om_ident = (me[3] & 0x10) != 0; + mm->opstatus.om_atc = (me[3] & 0x08) != 0; + } + + if (mm->mesub == 0 && (me[1] & 0xCC) == 0) { + // airborne + mm->opstatus.cc_acas = (me[1] & 0x20) == 0; + mm->opstatus.cc_cdti = (me[1] & 0x10) != 0; + mm->opstatus.cc_arv = (me[1] & 0x02) != 0; + mm->opstatus.cc_ts = (me[1] & 0x01) != 0; + mm->opstatus.cc_tc = (me[2] >> 6) & 0x03; + } else if (mm->mesub == 1 && (me[1] & 0xCC) == 0) { + // surface + mm->opstatus.cc_poa = (me[1] & 0x20) != 0; + mm->opstatus.cc_cdti = (me[1] & 0x10) != 0; + mm->opstatus.cc_b2_low = (me[1] & 0x02) != 0; + mm->opstatus.cc_lw_valid = 1; + mm->opstatus.cc_lw = me[2] & 0x0F; + } + + mm->opstatus.nic_supp_a = (me[5] & 0x10) ? 1 : 0; + mm->opstatus.nac_p = me[5] & 0x0F; + mm->opstatus.sil = (me[6] >> 4) & 0x03; + if (mm->mesub == 0) { + mm->opstatus.nic_baro = (me[6] & 0x08) ? 1 : 0; + } else { + mm->opstatus.track_angle = (me[6] & 0x08) ? ANGLE_TRACK : ANGLE_HEADING; + } + mm->opstatus.hrd = (me[6] & 0x04) ? HEADING_MAGNETIC : HEADING_TRUE; + break; + + case 2: + default: + if ((me[3] & 0xC0) == 0) { + mm->opstatus.om_acas_ra = (me[3] & 0x20) != 0; + mm->opstatus.om_ident = (me[3] & 0x10) != 0; + mm->opstatus.om_atc = (me[3] & 0x08) != 0; + mm->opstatus.om_saf = (me[3] & 0x04) != 0; + mm->opstatus.om_sda = (me[3] & 0x03); + } + + if (mm->mesub == 0 && (me[1] & 0xCC) == 0) { + // airborne + mm->opstatus.cc_acas = (me[1] & 0x20) != 0; + mm->opstatus.cc_1090_in = (me[1] & 0x10) != 0; + mm->opstatus.cc_arv = (me[1] & 0x02) != 0; + mm->opstatus.cc_ts = (me[1] & 0x01) != 0; + mm->opstatus.cc_tc = (me[2] >> 6) & 0x03; + mm->opstatus.cc_uat_in = (me[2] & 0x20) != 0; + } else if (mm->mesub == 1 && (me[1] & 0xCC) == 0) { + // surface + mm->opstatus.cc_poa = (me[1] & 0x20) != 0; + mm->opstatus.cc_1090_in = (me[1] & 0x10) != 0; + mm->opstatus.cc_b2_low = (me[1] & 0x02) != 0; + mm->opstatus.cc_uat_in = (me[1] & 0x01) != 0; + mm->opstatus.cc_nac_v = (me[2] >> 5) & 0x07; + mm->opstatus.cc_nic_supp_c = (me[2] & 0x10) ? 1 : 0; + mm->opstatus.cc_lw_valid = 1; + mm->opstatus.cc_lw = me[2] & 0x0F; + mm->opstatus.cc_antenna_offset = me[3]; + } + + mm->opstatus.nic_supp_a = (me[5] & 0x10) ? 1 : 0; + mm->opstatus.nac_p = me[5] & 0x0F; + mm->opstatus.sil = (me[6] >> 4) & 0x03; + if (mm->mesub == 0) { + mm->opstatus.gva = (me[6] >> 6) & 0x03; + mm->opstatus.nic_baro = (me[6] & 0x08) ? 1 : 0; + } else { + mm->opstatus.track_angle = (me[6] & 0x08) ? ANGLE_TRACK : ANGLE_HEADING; + } + mm->opstatus.hrd = (me[6] & 0x04) ? HEADING_MAGNETIC : HEADING_TRUE; + mm->opstatus.sil_type = (me[6] & 0x02) ? SIL_PER_SAMPLE : SIL_PER_HOUR; + break; + } + } break; default: @@ -1387,6 +1518,67 @@ void displayModesMessage(struct modesMessage *mm) { } } + if (mm->opstatus.valid) { + printf(" Aircraft Operational Status:\n"); + printf(" Version: %d\n", mm->opstatus.version); + + printf(" Capability classes: "); + if (mm->opstatus.cc_acas) printf("ACAS "); + if (mm->opstatus.cc_cdti) printf("CDTI "); + if (mm->opstatus.cc_1090_in) printf("1090IN "); + if (mm->opstatus.cc_arv) printf("ARV "); + if (mm->opstatus.cc_ts) printf("TS "); + if (mm->opstatus.cc_tc) printf("TC=%d ", mm->opstatus.cc_tc); + if (mm->opstatus.cc_uat_in) printf("UATIN "); + if (mm->opstatus.cc_poa) printf("POA "); + if (mm->opstatus.cc_b2_low) printf("B2-LOW "); + if (mm->opstatus.cc_nac_v) printf("NACv=%d ", mm->opstatus.cc_nac_v); + if (mm->opstatus.cc_nic_supp_c) printf("NIC-C=1 "); + if (mm->opstatus.cc_lw_valid) printf("L/W=%d ", mm->opstatus.cc_lw); + if (mm->opstatus.cc_antenna_offset) printf("GPS-OFFSET=%d ", mm->opstatus.cc_antenna_offset); + printf("\n"); + + printf(" Operational modes: "); + if (mm->opstatus.om_acas_ra) printf("ACASRA "); + if (mm->opstatus.om_ident) printf("IDENT "); + if (mm->opstatus.om_atc) printf("ATC "); + if (mm->opstatus.om_saf) printf("SAF "); + if (mm->opstatus.om_sda) printf("SDA=%d ", mm->opstatus.om_sda); + printf("\n"); + + if (mm->opstatus.nic_supp_a) printf(" NIC-A: %d\n", mm->opstatus.nic_supp_a); + if (mm->opstatus.nac_p) printf(" NACp: %d\n", mm->opstatus.nac_p); + if (mm->opstatus.gva) printf(" GVA: %d\n", mm->opstatus.gva); + if (mm->opstatus.sil) printf(" SIL: %d (%s)\n", mm->opstatus.sil, (mm->opstatus.sil_type == SIL_PER_HOUR ? "per hour" : "per sample")); + if (mm->opstatus.nic_baro) printf(" NICbaro: %d\n", mm->opstatus.nic_baro); + + if (mm->mesub == 1) + printf(" Heading type: %s\n", (mm->opstatus.track_angle == ANGLE_HEADING ? "heading" : "track angle")); + printf(" Heading reference: %s\n", (mm->opstatus.hrd == HEADING_TRUE ? "true north" : "magnetic north")); + } + + if (mm->tss.valid) { + printf(" Target State and Status:\n"); + if (mm->tss.altitude_valid) + printf(" Target altitude: %s, %d ft\n", (mm->tss.altitude_type == TSS_ALTITUDE_MCP ? "MCP" : "FMS"), mm->tss.altitude); + if (mm->tss.baro_valid) + printf(" Altimeter setting: %.1f millibars\n", mm->tss.baro); + if (mm->tss.heading_valid) + printf(" Target heading: %d\n", mm->tss.heading); + if (mm->tss.mode_valid) { + printf(" Active modes: "); + if (mm->tss.mode_autopilot) printf("autopilot "); + if (mm->tss.mode_vnav) printf("VNAV "); + if (mm->tss.mode_alt_hold) printf("altitude-hold "); + if (mm->tss.mode_approach) printf("approach "); + printf("\n"); + } + printf(" ACAS: %s\n", mm->tss.acas_operational ? "operational" : "NOT operational"); + printf(" NACp: %d\n", mm->tss.nac_p); + printf(" NICbaro: %d\n", mm->tss.nic_baro); + printf(" SIL: %d (%s)\n", mm->tss.sil, (mm->opstatus.sil_type == SIL_PER_HOUR ? "per hour" : "per sample")); + } + printf("\n"); fflush(stdout); } From 303d3c3fef39a48888468e9ade778f28253c92e8 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 1 Sep 2016 19:11:33 +0100 Subject: [PATCH 05/18] Make faup1090 emit iSource to say where the ident came from. --- net_io.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net_io.c b/net_io.c index 35ee7e6..7dc7fcb 100644 --- a/net_io.c +++ b/net_io.c @@ -1861,6 +1861,21 @@ static void writeFATSV() 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); + switch (a->callsign_valid.source) { + case SOURCE_MODE_S: + p += snprintf(p, bufsize(p,end), "\tiSource\tmodes"); + break; + case SOURCE_ADSB: + p += snprintf(p, bufsize(p,end), "\tiSource\tadsb"); + break; + case SOURCE_TISB: + p += snprintf(p, bufsize(p,end), "\tiSource\ttisb"); + break; + default: + p += snprintf(p, bufsize(p,end), "\tiSource\tunknown"); + break; + } + if (a->callsign_valid.source == SOURCE_TISB) { used_tisb = 1; } From a1fdc07db2fb2d751c3646743d8e4da85c2079ab Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 1 Sep 2016 22:10:17 +0100 Subject: [PATCH 06/18] Restructure decoding to use a helper function to extract bit ranges With forced inlining this is about as fast, and it is much less errorprone than the twisty little maze of handcoded bitshifts that it was before. (notably, at least one error - in the ACAS RI field - has been fixed) --- mode_s.c | 338 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 202 insertions(+), 136 deletions(-) diff --git a/mode_s.c b/mode_s.c index 1ffee1f..afac2d8 100644 --- a/mode_s.c +++ b/mode_s.c @@ -53,6 +53,8 @@ /* for PRIX64 */ #include +#include + // // ===================== Mode S detection and decoding =================== // @@ -218,6 +220,67 @@ static int correct_aa_field(uint32_t *addr, struct errorinfo *ei) return addr_errors; } +// The first bit (MSB of the first byte) is numbered 1, for consistency +// with how the specs number them. + +// Extract one bit from a message. +static inline __attribute__((always_inline)) unsigned getbit(unsigned char *data, unsigned bitnum) +{ + unsigned bi = bitnum - 1; + unsigned by = bi >> 3; + unsigned mask = 1 << (7 - (bi & 7)); + + return (data[by] & mask) != 0; +} + +// Extract some bits (firstbit .. lastbit inclusive) from a message. +static inline __attribute__((always_inline)) unsigned getbits(unsigned char *data, unsigned firstbit, unsigned lastbit) +{ + unsigned fbi = firstbit - 1; + unsigned lbi = lastbit - 1; + unsigned nbi = (lastbit - firstbit + 1); + + unsigned fby = fbi >> 3; + unsigned lby = lbi >> 3; + unsigned nby = (lby - fby) + 1; + + unsigned shift = 7 - (lbi & 7); + unsigned topmask = 0xFF >> (fbi & 7); + + assert (fbi <= lbi); + assert (nbi <= 32); + assert (nby <= 5); + + if (nby == 5) { + return + ((data[fby] & topmask) << (32 - shift)) | + (data[fby + 1] << (24 - shift)) | + (data[fby + 2] << (16 - shift)) | + (data[fby + 3] << (8 - shift)) | + (data[fby + 4] >> shift); + } else if (nby == 4) { + return + ((data[fby] & topmask) << (24 - shift)) | + (data[fby + 1] << (16 - shift)) | + (data[fby + 2] << (8 - shift)) | + (data[fby + 3] >> shift); + } else if (nby == 3) { + return + ((data[fby] & topmask) << (16 - shift)) | + (data[fby + 1] << (8 - shift)) | + (data[fby + 2] >> shift); + } else if (nby == 2) { + return + ((data[fby] & topmask) << (8 - shift)) | + (data[fby + 1] >> shift); + } else if (nby == 1) { + return + (data[fby] & topmask) >> shift; + } else { + return 0; + } +} + // Score how plausible this ModeS message looks. // The more positive, the more reliable the message is @@ -254,7 +317,7 @@ int scoreModesMessage(unsigned char *msg, int validbits) if (validbits < 56) return -2; - msgtype = msg[0] >> 3; // Downlink Format + msgtype = getbits(msg, 1, 5); // Downlink Format msgbits = modesMessageLenByType(msgtype); if (validbits < msgbits) @@ -283,7 +346,7 @@ int scoreModesMessage(unsigned char *msg, int validbits) case 11: // All-call reply iid = crc & 0x7f; crc = crc & 0xffff80; - addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + addr = getbits(msg, 9, 32); ei = modesChecksumDiagnose(crc, msgbits); if (!ei) @@ -318,7 +381,7 @@ int scoreModesMessage(unsigned char *msg, int validbits) return -2; // can't correct errors // fix any errors in the address field - addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + addr = getbits(msg, 9, 32); correct_aa_field(&addr, ei); if (icaoFilterTest(addr)) @@ -372,7 +435,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) msg = mm->msg; // Get the message type ASAP as other operations depend on this - mm->msgtype = msg[0] >> 3; // Downlink Format + mm->msgtype = getbits(msg, 1, 5); // Downlink Format mm->msgbits = modesMessageLenByType(mm->msgtype); mm->crc = modesChecksum(msg, mm->msgbits); mm->correctedbits = 0; @@ -429,7 +492,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // check whether the corrected message looks sensible // we are conservative here: only accept corrected messages that // match an existing aircraft. - addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + addr = getbits(msg, 9, 32); if (!icaoFilterTest(addr)) { return -1; } @@ -450,10 +513,10 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) return -2; // couldn't fix it } - addr1 = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + addr1 = getbits(msg, 9, 32); mm->correctedbits = ei->errors; modesChecksumFix(msg, ei); - addr2 = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + addr2 = getbits(msg, 9, 32); // we are conservative here: only accept corrected messages that // match an existing aircraft. @@ -494,12 +557,12 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // AA (Address announced) if (mm->msgtype == 11 || mm->msgtype == 17 || mm->msgtype == 18) { - mm->AA = mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]); + mm->AA = mm->addr = getbits(msg, 9, 32); } // AC (Altitude Code) if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 16 || mm->msgtype == 20) { - mm->AC = ((msg[2] << 8) | msg[3]) & 0x1FFF; + mm->AC = getbits(msg, 20, 32); 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) @@ -512,7 +575,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // CA (Capability) if (mm->msgtype == 11 || mm->msgtype == 17) { - mm->CA = (msg[0] & 0x07); + mm->CA = getbits(msg, 6, 8); switch (mm->CA) { case 0: @@ -535,22 +598,22 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // CC (Cross-link capability) if (mm->msgtype == 0) { - mm->CC = (msg[0] & 0x02) ? 1 : 0; + mm->CC = getbit(msg, 7); } // CF (Control field) if (mm->msgtype == 18) { - mm->CF = msg[0] & 7; + mm->CF = getbits(msg, 5, 8); } // DR (Downlink Request) if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 20 || mm->msgtype == 21) { - mm->DR = (msg[1] >> 3) & 0x1F; + mm->DR = getbits(msg, 9, 13); } // FS (Flight Status) if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 20 || mm->msgtype == 21) { - mm->FS = msg[0] & 7; + mm->FS = getbits(msg, 6, 8); mm->alert_valid = 1; mm->spi_valid = 1; @@ -588,7 +651,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // ID (Identity) if (mm->msgtype == 5 || mm->msgtype == 21) { // Gillham encoded Squawk - mm->ID = ((msg[2] << 8) | msg[3]) & 0x1FFF; + mm->ID = getbits(msg, 20, 32); if (mm->ID) { mm->squawk = decodeID13Field(mm->ID); mm->squawk_valid = 1; @@ -597,7 +660,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // KE (Control, ELM) if (mm->msgtype >= 24 && mm->msgtype <= 31) { - mm->KE = (msg[0] & 0x10) ? 1 : 0; + mm->KE = getbit(msg, 4); } // MB (messsage, Comm-B) @@ -624,27 +687,27 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // ND (number of D-segment, Comm-D) if (mm->msgtype >= 24 && mm->msgtype <= 31) { - mm->ND = msg[0] & 0x0F; + mm->ND = getbits(msg, 5, 8); } // RI (Reply information, ACAS) if (mm->msgtype == 0 || mm->msgtype == 16) { - mm->RI = ((msg[2] & 0x07) << 1) | ((msg[3] >> 7) & 0x01); + mm->RI = getbits(msg, 14, 17); } // SL (Sensitivity level, ACAS) if (mm->msgtype == 0 || mm->msgtype == 16) { - mm->SL = (msg[1] >> 5) & 0x07; + mm->SL = getbits(msg, 9, 11); } // 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); + mm->UM = getbits(msg, 14, 19); } // VS (Vertical Status) if (mm->msgtype == 0 || mm->msgtype == 16) { - mm->VS = (msg[0] & 0x04) ? 1 : 0; + mm->VS = getbit(msg, 6); if (mm->VS) mm->airground = AG_GROUND; else @@ -675,10 +738,10 @@ static void decodeBDS20(struct modesMessage *mm) { uint32_t chars1, chars2; unsigned char *msg = mm->msg; - - chars1 = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); - chars2 = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); - + + chars1 = getbits(msg, 41, 64); + chars2 = getbits(msg, 65, 88); + // A common failure mode seems to be to intermittently send // all zeros. Catch that here. if (chars1 == 0 && chars2 == 0) @@ -701,10 +764,9 @@ static void decodeBDS20(struct modesMessage *mm) static void decodeExtendedSquitter(struct modesMessage *mm) { - unsigned char *msg = mm->msg; unsigned char *me = mm->ME; - int metype = mm->metype = msg[4] >> 3; // Extended squitter message type - int mesub = mm->mesub = (metype == 29 ? ((msg[4]&6)>>1) : (msg[4] & 7)); // Extended squitter message subtype + int metype = mm->metype = getbits(me, 1, 5); + int mesub = mm->mesub = (metype == 29 ? getbits(me, 6, 7) : getbits(me, 6, 8)); // Extended squitter message subtype int check_imf = 0; @@ -727,7 +789,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // TODO: decode me. // For now we only look at the IMF bit. mm->source = SOURCE_TISB; - if (msg[4] & 0x80) + if (getbit(me, 1)) mm->addr |= MODES_NON_ICAO_ADDRESS; return; @@ -751,8 +813,8 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // Aircraft Identification and Category uint32_t chars1, chars2; - chars1 = (msg[5] << 16) | (msg[6] << 8) | (msg[7]); - chars2 = (msg[8] << 16) | (msg[9] << 8) | (msg[10]); + chars1 = getbits(me, 9, 32); + chars2 = getbits(me, 33, 56); // A common failure mode seems to be to intermittently send // all zeros. Catch that here. @@ -777,30 +839,31 @@ static void decodeExtendedSquitter(struct modesMessage *mm) break; } - case 19: { // Airborne Velocity Message - if (check_imf && (msg[5] & 0x80)) + case 19: { // Airborne Velocity Message + if (check_imf && getbit(me, 9)) mm->addr |= MODES_NON_ICAO_ADDRESS; if ( (mesub >= 1) && (mesub <= 4) ) { - int vert_rate = ((msg[8] & 0x07) << 6) | (msg[9] >> 2); + int vert_rate = getbits(me, 38, 46); if (vert_rate) { --vert_rate; - if (msg[8] & 0x08) - {vert_rate = 0 - vert_rate;} + if (getbit(me, 37)) { + vert_rate = 0 - vert_rate; + } mm->vert_rate = vert_rate * 64; mm->vert_rate_valid = 1; } - mm->vert_rate_source = (msg[8] & 0x10 ? ALTITUDE_GNSS : ALTITUDE_BARO); + mm->vert_rate_source = (getbit(me, 36) ? ALTITUDE_GNSS : ALTITUDE_BARO); } if ((mesub == 1) || (mesub == 2)) { - unsigned ew_raw = ((msg[5] & 0x03) << 8) | msg[6]; - unsigned ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5); + unsigned ew_raw = getbits(me, 15, 24); + unsigned ns_raw = getbits(me, 26, 35); 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); + int ew_vel = (ew_raw - 1) * (getbit(me, 14) ? -1 : 1) * ((mesub == 2) ? 4 : 1); + int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mesub == 2) ? 4 : 1); // Compute velocity and angle from the two speed components mm->speed = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); @@ -819,27 +882,28 @@ static void decodeExtendedSquitter(struct modesMessage *mm) mm->speed_source = SPEED_GROUNDSPEED; } } else if (mesub == 3 || mesub == 4) { - unsigned airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5); + unsigned airspeed = getbits(me, 26, 35); if (airspeed) { --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_source = getbit(me, 25) ? SPEED_TAS : SPEED_IAS; mm->speed_valid = 1; } - if (msg[5] & 0x04) { - mm->heading = ((((msg[5] & 0x03) << 8) | msg[6]) * 45) >> 7; + if (getbit(me, 14)) { + mm->heading = getbits(me, 15, 24); mm->heading_source = HEADING_MAGNETIC; mm->heading_valid = 1; } } - if (msg[10] & 0x7f) { + unsigned raw_delta = getbits(me, 50, 56); + if (raw_delta) { mm->gnss_delta_valid = 1; - mm->gnss_delta = ((msg[10] & 0x80) ? -25 : 25) * ((msg[10] & 0x7f) - 1); + mm->gnss_delta = (raw_delta - 1) * (getbit(me, 49) ? -25 : 25); } break; @@ -849,28 +913,27 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // Ground position int movement; - if (check_imf && (msg[6] & 0x08)) + if (check_imf && getbit(me, 21)) mm->addr |= MODES_NON_ICAO_ADDRESS; 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_lat = getbits(me, 23, 39); + mm->cpr_lon = getbits(me, 40, 56); + mm->cpr_odd = getbit(me, 22); mm->cpr_nucp = (14 - metype); mm->cpr_valid = 1; - movement = ((msg[4] << 4) | (msg[5] >> 4)) & 0x007F; + movement = getbits(me, 6, 12); if ((movement) && (movement < 125)) { mm->speed_valid = 1; mm->speed = decodeMovementField(movement); mm->speed_source = SPEED_GROUNDSPEED; } - if (msg[5] & 0x08) { + if (getbit(me, 13)) { mm->heading_valid = 1; mm->heading_source = HEADING_TRUE; - mm->heading = ((((msg[5] << 4) | (msg[6] >> 4)) & 0x007F) * 45) >> 4; + mm->heading = getbits(me, 14, 20) * 360 / 128; } break; @@ -879,9 +942,9 @@ static void decodeExtendedSquitter(struct modesMessage *mm) 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 altitude (HAE or MSL) - int AC12Field = ((msg[5] << 4) | (msg[6] >> 4)) & 0x0FFF; + int AC12Field = getbits(me, 9, 20); - if (check_imf && (msg[4] & 0x01)) + if (check_imf && getbit(me, 8)) mm->addr |= MODES_NON_ICAO_ADDRESS; if (metype == 0) { @@ -890,8 +953,8 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // Catch some common failure modes and don't mark them as valid // (so they won't be used for positioning) - 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]); + mm->cpr_lat = getbits(me, 23, 39); + mm->cpr_lon = getbits(me, 40, 56); if (AC12Field == 0 && mm->cpr_lon == 0 && (mm->cpr_lat & 0x0fff) == 0 && mm->metype == 15) { // Seen from at least: @@ -904,8 +967,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) } else { // Otherwise, assume it's valid. mm->cpr_valid = 1; - if (mm->msg[6] & 0x04) - mm->cpr_odd = 1; + mm->cpr_odd = getbit(me, 22); if (metype == 18 || metype == 22) mm->cpr_nucp = 0; @@ -930,7 +992,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) case 23: { // Test message if (mesub == 7) { // (see 1090-WP-15-20) - int ID13Field = (((msg[5] << 8) | msg[6]) & 0xFFF1)>>3; + int ID13Field = getbits(me, 9, 21); if (ID13Field) { mm->squawk_valid = 1; mm->squawk = decodeID13Field(ID13Field); @@ -944,25 +1006,30 @@ static void decodeExtendedSquitter(struct modesMessage *mm) case 28: { // Extended Squitter Aircraft Status if (mesub == 1) { // Emergency status squawk field - int ID13Field = (((msg[5] << 8) | msg[6]) & 0x1FFF); + int ID13Field = getbits(me, 12, 24); if (ID13Field) { mm->squawk_valid = 1; mm->squawk = decodeID13Field(ID13Field); } - if (check_imf && (msg[10] & 0x01)) + if (check_imf && getbit(me, 56)) mm->addr |= MODES_NON_ICAO_ADDRESS; } break; } case 29: // Aircraft Trajectory Intent - if (mesub == 1) { // Target state and status, V2 - mm->tss.valid = 1; - mm->tss.sil_type = (me[0] & 0x01) ? SIL_PER_SAMPLE : SIL_PER_HOUR; - mm->tss.altitude_type = (me[1] & 0x80) ? TSS_ALTITUDE_FMS : TSS_ALTITUDE_MCP; + if (check_imf && getbit(me, 51)) + mm->addr |= MODES_NON_ICAO_ADDRESS; - unsigned alt_bits = ((me[1] << 4) | (me[2] >> 4)) & 0x7FF; + if (mesub == 0) { // Target state and status, V1 + // TODO: need RTCA/DO-260A + } else if (mesub == 1) { // Target state and status, V2 + mm->tss.valid = 1; + mm->tss.sil_type = getbit(me, 8) ? SIL_PER_SAMPLE : SIL_PER_HOUR; + mm->tss.altitude_type = getbit(me, 9) ? TSS_ALTITUDE_FMS : TSS_ALTITUDE_MCP; + + unsigned alt_bits = getbits(me, 10, 20); if (alt_bits == 0) { mm->tss.altitude_valid = 0; } else { @@ -970,7 +1037,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) mm->tss.altitude = (alt_bits - 1) * 32; } - unsigned baro_bits = ((me[2] << 5) | (me[3] >> 3)) & 0x1FF; + unsigned baro_bits = getbits(me, 21, 29); if (baro_bits == 0) { mm->tss.baro_valid = 0; } else { @@ -978,26 +1045,25 @@ static void decodeExtendedSquitter(struct modesMessage *mm) mm->tss.baro = 800.0 + (baro_bits - 1) * 0.8; } - mm->tss.heading_valid = (me[3] & 0x04) != 0; + mm->tss.heading_valid = getbit(me, 30); if (mm->tss.heading_valid) { // two's complement -180..+180, which is conveniently // also the same as unsigned 0..360 - unsigned heading_bits = ((me[3] << 7) | (me[4] >> 1)) & 0x1ff; - mm->tss.heading = heading_bits * 180 / 256; + mm->tss.heading = getbits(me, 31, 39) * 180 / 256; } - mm->tss.nac_p = ((me[4] << 3) | (me[5] >> 5)) & 0x0f; - mm->tss.nic_baro = (me[5] & 0x10) ? 1 : 0; - mm->tss.sil = (me[5] >> 2) & 0x03; - mm->tss.mode_valid = (me[5] & 0x02) ? 1 : 0; + mm->tss.nac_p = getbits(me, 40, 43); + mm->tss.nic_baro = getbit(me, 44); + mm->tss.sil = getbits(me, 45, 46); + mm->tss.mode_valid = getbit(me, 47); if (mm->tss.mode_valid) { - mm->tss.mode_autopilot = (me[5] & 0x01) != 0; - mm->tss.mode_vnav = (me[6] & 0x80) != 0; - mm->tss.mode_alt_hold = (me[6] & 0x40) != 0; - mm->tss.mode_approach = (me[6] & 0x10) != 0; + mm->tss.mode_autopilot = getbit(me, 48); + mm->tss.mode_vnav = getbit(me, 49); + mm->tss.mode_alt_hold = getbit(me, 50); + mm->tss.mode_approach = getbit(me, 52); } - mm->tss.acas_operational = (me[6] & 0x08) != 0; + mm->tss.acas_operational = getbit(me, 53); } break; @@ -1005,93 +1071,93 @@ static void decodeExtendedSquitter(struct modesMessage *mm) break; case 31: // Aircraft Operational Status - if (check_imf && (msg[10] & 0x01)) + if (check_imf && getbit(me, 56)) mm->addr |= MODES_NON_ICAO_ADDRESS; if (mm->mesub == 0 || mm->mesub == 1) { mm->opstatus.valid = 1; - mm->opstatus.version = (me[5] >> 5) & 0x07; + mm->opstatus.version = getbits(me, 41, 43); switch (mm->opstatus.version) { case 0: break; case 1: - if ((me[3] & 0xC0) == 0) { - mm->opstatus.om_acas_ra = (me[3] & 0x20) != 0; - mm->opstatus.om_ident = (me[3] & 0x10) != 0; - mm->opstatus.om_atc = (me[3] & 0x08) != 0; + if (getbits(me, 25, 26) == 0) { + mm->opstatus.om_acas_ra = getbit(me, 27); + mm->opstatus.om_ident = getbit(me, 28); + mm->opstatus.om_atc = getbit(me, 29); } - if (mm->mesub == 0 && (me[1] & 0xCC) == 0) { + if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { // airborne - mm->opstatus.cc_acas = (me[1] & 0x20) == 0; - mm->opstatus.cc_cdti = (me[1] & 0x10) != 0; - mm->opstatus.cc_arv = (me[1] & 0x02) != 0; - mm->opstatus.cc_ts = (me[1] & 0x01) != 0; - mm->opstatus.cc_tc = (me[2] >> 6) & 0x03; - } else if (mm->mesub == 1 && (me[1] & 0xCC) == 0) { + mm->opstatus.cc_acas = !getbit(me, 11); + mm->opstatus.cc_cdti = getbit(me, 12); + mm->opstatus.cc_arv = getbit(me, 15); + mm->opstatus.cc_ts = getbit(me, 16); + mm->opstatus.cc_tc = getbits(me, 17, 18); + } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { // surface - mm->opstatus.cc_poa = (me[1] & 0x20) != 0; - mm->opstatus.cc_cdti = (me[1] & 0x10) != 0; - mm->opstatus.cc_b2_low = (me[1] & 0x02) != 0; + mm->opstatus.cc_poa = getbit(me, 11); + mm->opstatus.cc_cdti = getbit(me, 12); + mm->opstatus.cc_b2_low = getbit(me, 15); mm->opstatus.cc_lw_valid = 1; - mm->opstatus.cc_lw = me[2] & 0x0F; + mm->opstatus.cc_lw = getbits(me, 21, 24); } - mm->opstatus.nic_supp_a = (me[5] & 0x10) ? 1 : 0; - mm->opstatus.nac_p = me[5] & 0x0F; - mm->opstatus.sil = (me[6] >> 4) & 0x03; + mm->opstatus.nic_supp_a = getbit(me, 44); + mm->opstatus.nac_p = getbits(me, 45, 48); + mm->opstatus.sil = getbits(me, 51, 52); if (mm->mesub == 0) { - mm->opstatus.nic_baro = (me[6] & 0x08) ? 1 : 0; + mm->opstatus.nic_baro = getbit(me, 53); } else { - mm->opstatus.track_angle = (me[6] & 0x08) ? ANGLE_TRACK : ANGLE_HEADING; + mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; } - mm->opstatus.hrd = (me[6] & 0x04) ? HEADING_MAGNETIC : HEADING_TRUE; + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; break; case 2: default: - if ((me[3] & 0xC0) == 0) { - mm->opstatus.om_acas_ra = (me[3] & 0x20) != 0; - mm->opstatus.om_ident = (me[3] & 0x10) != 0; - mm->opstatus.om_atc = (me[3] & 0x08) != 0; - mm->opstatus.om_saf = (me[3] & 0x04) != 0; - mm->opstatus.om_sda = (me[3] & 0x03); + if (getbits(me, 25, 26) == 0) { + mm->opstatus.om_acas_ra = getbit(me, 27); + mm->opstatus.om_ident = getbit(me, 28); + mm->opstatus.om_atc = getbit(me, 29); + mm->opstatus.om_saf = getbit(me, 30); + mm->opstatus.om_sda = getbits(me, 31, 32); } - if (mm->mesub == 0 && (me[1] & 0xCC) == 0) { + if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { // airborne - mm->opstatus.cc_acas = (me[1] & 0x20) != 0; - mm->opstatus.cc_1090_in = (me[1] & 0x10) != 0; - mm->opstatus.cc_arv = (me[1] & 0x02) != 0; - mm->opstatus.cc_ts = (me[1] & 0x01) != 0; - mm->opstatus.cc_tc = (me[2] >> 6) & 0x03; - mm->opstatus.cc_uat_in = (me[2] & 0x20) != 0; - } else if (mm->mesub == 1 && (me[1] & 0xCC) == 0) { + mm->opstatus.cc_acas = getbit(me, 11); + mm->opstatus.cc_1090_in = getbit(me, 12); + mm->opstatus.cc_arv = getbit(me, 15); + mm->opstatus.cc_ts = getbit(me, 16); + mm->opstatus.cc_tc = getbits(me, 17, 18); + mm->opstatus.cc_uat_in = getbit(me, 19); + } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { // surface - mm->opstatus.cc_poa = (me[1] & 0x20) != 0; - mm->opstatus.cc_1090_in = (me[1] & 0x10) != 0; - mm->opstatus.cc_b2_low = (me[1] & 0x02) != 0; - mm->opstatus.cc_uat_in = (me[1] & 0x01) != 0; - mm->opstatus.cc_nac_v = (me[2] >> 5) & 0x07; - mm->opstatus.cc_nic_supp_c = (me[2] & 0x10) ? 1 : 0; + mm->opstatus.cc_poa = getbit(me, 11); + mm->opstatus.cc_1090_in = getbit(me, 12); + mm->opstatus.cc_b2_low = getbit(me, 15); + mm->opstatus.cc_uat_in = getbit(me, 16); + mm->opstatus.cc_nac_v = getbits(me, 17, 19); + mm->opstatus.cc_nic_supp_c = getbit(me, 20); mm->opstatus.cc_lw_valid = 1; - mm->opstatus.cc_lw = me[2] & 0x0F; - mm->opstatus.cc_antenna_offset = me[3]; + mm->opstatus.cc_lw = getbits(me, 21, 24); + mm->opstatus.cc_antenna_offset = getbits(me, 33, 40); } - mm->opstatus.nic_supp_a = (me[5] & 0x10) ? 1 : 0; - mm->opstatus.nac_p = me[5] & 0x0F; - mm->opstatus.sil = (me[6] >> 4) & 0x03; + mm->opstatus.nic_supp_a = getbit(me, 44); + mm->opstatus.nac_p = getbits(me, 45, 48); + mm->opstatus.sil = getbits(me, 51, 52); if (mm->mesub == 0) { - mm->opstatus.gva = (me[6] >> 6) & 0x03; - mm->opstatus.nic_baro = (me[6] & 0x08) ? 1 : 0; + mm->opstatus.gva = getbits(me, 49, 50); + mm->opstatus.nic_baro = getbit(me, 53); } else { - mm->opstatus.track_angle = (me[6] & 0x08) ? ANGLE_TRACK : ANGLE_HEADING; + mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; } - mm->opstatus.hrd = (me[6] & 0x04) ? HEADING_MAGNETIC : HEADING_TRUE; - mm->opstatus.sil_type = (me[6] & 0x02) ? SIL_PER_SAMPLE : SIL_PER_HOUR; + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; + mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; break; } } @@ -1107,7 +1173,7 @@ static void decodeCommB(struct modesMessage *mm) unsigned char *msg = mm->msg; // This is a bit hairy as we don't know what the requested register was - if (msg[4] == 0x20) { // BDS 2,0 Aircraft Identification + if (getbits(msg, 33, 40) == 0x20) { // BDS 2,0 Aircraft Identification decodeBDS20(mm); } } From f9419f13d5e29d03f91cffcb2e0959cb65af4da0 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 1 Sep 2016 23:13:31 +0100 Subject: [PATCH 07/18] Break up decodeExtendedSquiiter, it was getting pretty large. --- mode_s.c | 734 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 396 insertions(+), 338 deletions(-) diff --git a/mode_s.c b/mode_s.c index afac2d8..3824a3f 100644 --- a/mode_s.c +++ b/mode_s.c @@ -181,7 +181,7 @@ static int decodeAC12Field(int AC12Field, altitude_unit_t *unit) { // // Decode the 7 bit ground movement field PWL exponential style scale // -static int decodeMovementField(int movement) { +static unsigned decodeMovementField(unsigned movement) { int gspeed; // Note : movement codes 0,125,126,127 are all invalid, but they are @@ -753,22 +753,394 @@ static void decodeBDS20(struct modesMessage *mm) 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->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->callsign[8] = '\0'; } -static void decodeExtendedSquitter(struct modesMessage *mm) -{ +static void decodeESIdentAndCategory(struct modesMessage *mm) +{ + // Aircraft Identification and Category unsigned char *me = mm->ME; - int metype = mm->metype = getbits(me, 1, 5); - int mesub = mm->mesub = (metype == 29 ? getbits(me, 6, 7) : getbits(me, 6, 8)); // Extended squitter message subtype - int check_imf = 0; + mm->mesub = getbits(me, 6, 8); + + unsigned chars1 = getbits(me, 9, 32); + unsigned chars2 = getbits(me, 33, 56); + + // A common failure mode seems to be to intermittently send + // all zeros. Catch that here. + if (chars1 != 0 || chars2 != 0) { + mm->callsign_valid = 1; + + 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->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->callsign[8] = '\0'; + } + + mm->category = ((0x0E - mm->metype) << 4) | mm->mesub; + mm->category_valid = 1; +} + +static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) +{ + // Airborne Velocity Message + unsigned char *me = mm->ME; + + mm->mesub = getbits(me, 6, 8); + + if (check_imf && getbit(me, 9)) + mm->addr |= MODES_NON_ICAO_ADDRESS; + + if (mm->mesub < 1 || mm->mesub > 4) + return; + + unsigned vert_rate = getbits(me, 38, 46); + if (vert_rate) { + mm->vert_rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64); + mm->vert_rate_valid = 1; + } + + mm->vert_rate_source = (getbit(me, 36) ? ALTITUDE_GNSS : ALTITUDE_BARO); + + switch (mm->mesub) { + case 1: case 2: + { + unsigned ew_raw = getbits(me, 15, 24); + unsigned ns_raw = getbits(me, 26, 35); + + if (ew_raw && ns_raw) { + int ew_vel = (ew_raw - 1) * (getbit(me, 14) ? -1 : 1) * ((mm->mesub == 2) ? 4 : 1); + int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mm->mesub == 2) ? 4 : 1); + + // Compute velocity and angle from the two speed components + mm->speed = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); + mm->speed_valid = 1; + + 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 (heading < 0) + heading += 360; + mm->heading = (unsigned) heading; + mm->heading_source = HEADING_TRUE; + mm->heading_valid = 1; + } + + mm->speed_source = SPEED_GROUNDSPEED; + } + break; + } + + case 3: case 4: + { + unsigned airspeed = getbits(me, 26, 35); + if (airspeed) { + mm->speed = (airspeed - 1) * (mm->mesub == 4 ? 4 : 1); + mm->speed_source = getbit(me, 25) ? SPEED_TAS : SPEED_IAS; + mm->speed_valid = 1; + } + + if (getbit(me, 14)) { + mm->heading = getbits(me, 15, 24); + mm->heading_source = HEADING_MAGNETIC; + mm->heading_valid = 1; + } + break; + } + } + + unsigned raw_delta = getbits(me, 50, 56); + if (raw_delta) { + mm->gnss_delta_valid = 1; + mm->gnss_delta = (raw_delta - 1) * (getbit(me, 49) ? -25 : 25); + } +} + +static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf) +{ + // Surface position and movement + unsigned char *me = mm->ME; + + if (check_imf && getbit(me, 21)) + mm->addr |= MODES_NON_ICAO_ADDRESS; + + mm->airground = AG_GROUND; // definitely. + mm->cpr_lat = getbits(me, 23, 39); + mm->cpr_lon = getbits(me, 40, 56); + mm->cpr_odd = getbit(me, 22); + mm->cpr_nucp = (14 - mm->metype); + mm->cpr_valid = 1; + + unsigned movement = getbits(me, 6, 12); + if (movement > 0 && movement < 125) { + mm->speed_valid = 1; + mm->speed = decodeMovementField(movement); + mm->speed_source = SPEED_GROUNDSPEED; + } + + if (getbit(me, 13)) { + mm->heading_valid = 1; + mm->heading_source = HEADING_TRUE; + mm->heading = getbits(me, 14, 20) * 360 / 128; + } +} + +static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) +{ + // Airborne position and altitude + unsigned char *me = mm->ME; + + if (check_imf && getbit(me, 8)) + mm->addr |= MODES_NON_ICAO_ADDRESS; + + unsigned AC12Field = getbits(me, 9, 20); + + if (mm->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->cpr_lat = getbits(me, 23, 39); + mm->cpr_lon = getbits(me, 40, 56); + + 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 + // 400648 (BAE ATP) - Atlantic Airlines + // altitude == 0, longitude == 0, type == 15 and zeros in latitude LSB. + // Can alternate with valid reports having type == 14 + Modes.stats_current.cpr_filtered++; + } else { + // Otherwise, assume it's valid. + mm->cpr_valid = 1; + mm->cpr_odd = getbit(me, 22); + + if (mm->metype == 18 || mm->metype == 22) + mm->cpr_nucp = 0; + else if (mm->metype < 18) + mm->cpr_nucp = (18 - mm->metype); + else + mm->cpr_nucp = (29 - mm->metype); + } + } + + if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present + mm->altitude = decodeAC12Field(AC12Field, &mm->altitude_unit); + if (mm->altitude != INVALID_ALTITUDE) { + mm->altitude_valid = 1; + } + + mm->altitude_source = (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) ? ALTITUDE_GNSS : ALTITUDE_BARO; + } +} + +static void decodeESTestMessage(struct modesMessage *mm) +{ + unsigned char *me = mm->ME; + + mm->mesub = getbits(me, 6, 8); + + if (mm->mesub == 7) { // (see 1090-WP-15-20) + int ID13Field = getbits(me, 9, 21); + if (ID13Field) { + mm->squawk_valid = 1; + mm->squawk = decodeID13Field(ID13Field); + } + } +} + +static void decodeESAircraftStatus(struct modesMessage *mm, int check_imf) +{ + // Extended Squitter Aircraft Status + unsigned char *me = mm->ME; + + mm->mesub = getbits(me, 6, 8); + + if (mm->mesub == 1) { // Emergency status squawk field + int ID13Field = getbits(me, 12, 24); + if (ID13Field) { + mm->squawk_valid = 1; + mm->squawk = decodeID13Field(ID13Field); + } + + if (check_imf && getbit(me, 56)) + mm->addr |= MODES_NON_ICAO_ADDRESS; + } +} + +static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) +{ + unsigned char *me = mm->ME; + + mm->mesub = getbits(me, 6, 7); // an unusual message: only 2 bits of subtype + + if (check_imf && getbit(me, 51)) + mm->addr |= MODES_NON_ICAO_ADDRESS; + + if (mm->mesub == 0) { // Target state and status, V1 + // TODO: need RTCA/DO-260A + } else if (mm->mesub == 1) { // Target state and status, V2 + mm->tss.valid = 1; + mm->tss.sil_type = getbit(me, 8) ? SIL_PER_SAMPLE : SIL_PER_HOUR; + mm->tss.altitude_type = getbit(me, 9) ? TSS_ALTITUDE_FMS : TSS_ALTITUDE_MCP; + + unsigned alt_bits = getbits(me, 10, 20); + if (alt_bits == 0) { + mm->tss.altitude_valid = 0; + } else { + mm->tss.altitude_valid = 1; + mm->tss.altitude = (alt_bits - 1) * 32; + } + + unsigned baro_bits = getbits(me, 21, 29); + if (baro_bits == 0) { + mm->tss.baro_valid = 0; + } else { + mm->tss.baro_valid = 1; + mm->tss.baro = 800.0 + (baro_bits - 1) * 0.8; + } + + mm->tss.heading_valid = getbit(me, 30); + if (mm->tss.heading_valid) { + // two's complement -180..+180, which is conveniently + // also the same as unsigned 0..360 + mm->tss.heading = getbits(me, 31, 39) * 180 / 256; + } + + mm->tss.nac_p = getbits(me, 40, 43); + mm->tss.nic_baro = getbit(me, 44); + mm->tss.sil = getbits(me, 45, 46); + mm->tss.mode_valid = getbit(me, 47); + if (mm->tss.mode_valid) { + mm->tss.mode_autopilot = getbit(me, 48); + mm->tss.mode_vnav = getbit(me, 49); + mm->tss.mode_alt_hold = getbit(me, 50); + mm->tss.mode_approach = getbit(me, 52); + } + + mm->tss.acas_operational = getbit(me, 53); + } +} + +static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) +{ + unsigned char *me = mm->ME; + + mm->mesub = getbits(me, 6, 8); + + // Aircraft Operational Status + if (check_imf && getbit(me, 56)) + mm->addr |= MODES_NON_ICAO_ADDRESS; + + if (mm->mesub == 0 || mm->mesub == 1) { + mm->opstatus.valid = 1; + mm->opstatus.version = getbits(me, 41, 43); + + switch (mm->opstatus.version) { + case 0: + break; + + case 1: + if (getbits(me, 25, 26) == 0) { + mm->opstatus.om_acas_ra = getbit(me, 27); + mm->opstatus.om_ident = getbit(me, 28); + mm->opstatus.om_atc = getbit(me, 29); + } + + if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { + // airborne + mm->opstatus.cc_acas = !getbit(me, 11); + mm->opstatus.cc_cdti = getbit(me, 12); + mm->opstatus.cc_arv = getbit(me, 15); + mm->opstatus.cc_ts = getbit(me, 16); + mm->opstatus.cc_tc = getbits(me, 17, 18); + } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { + // surface + mm->opstatus.cc_poa = getbit(me, 11); + mm->opstatus.cc_cdti = getbit(me, 12); + mm->opstatus.cc_b2_low = getbit(me, 15); + mm->opstatus.cc_lw_valid = 1; + mm->opstatus.cc_lw = getbits(me, 21, 24); + } + + mm->opstatus.nic_supp_a = getbit(me, 44); + mm->opstatus.nac_p = getbits(me, 45, 48); + mm->opstatus.sil = getbits(me, 51, 52); + if (mm->mesub == 0) { + mm->opstatus.nic_baro = getbit(me, 53); + } else { + mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; + } + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; + break; + + case 2: + default: + if (getbits(me, 25, 26) == 0) { + mm->opstatus.om_acas_ra = getbit(me, 27); + mm->opstatus.om_ident = getbit(me, 28); + mm->opstatus.om_atc = getbit(me, 29); + mm->opstatus.om_saf = getbit(me, 30); + mm->opstatus.om_sda = getbits(me, 31, 32); + } + + if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { + // airborne + mm->opstatus.cc_acas = getbit(me, 11); + mm->opstatus.cc_1090_in = getbit(me, 12); + mm->opstatus.cc_arv = getbit(me, 15); + mm->opstatus.cc_ts = getbit(me, 16); + mm->opstatus.cc_tc = getbits(me, 17, 18); + mm->opstatus.cc_uat_in = getbit(me, 19); + } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { + // surface + mm->opstatus.cc_poa = getbit(me, 11); + mm->opstatus.cc_1090_in = getbit(me, 12); + mm->opstatus.cc_b2_low = getbit(me, 15); + mm->opstatus.cc_uat_in = getbit(me, 16); + mm->opstatus.cc_nac_v = getbits(me, 17, 19); + mm->opstatus.cc_nic_supp_c = getbit(me, 20); + mm->opstatus.cc_lw_valid = 1; + mm->opstatus.cc_lw = getbits(me, 21, 24); + mm->opstatus.cc_antenna_offset = getbits(me, 33, 40); + } + + mm->opstatus.nic_supp_a = getbit(me, 44); + mm->opstatus.nac_p = getbits(me, 45, 48); + mm->opstatus.sil = getbits(me, 51, 52); + if (mm->mesub == 0) { + mm->opstatus.gva = getbits(me, 49, 50); + mm->opstatus.nic_baro = getbit(me, 53); + } else { + mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; + } + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; + mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; + break; + } + } +} + +static void decodeExtendedSquitter(struct modesMessage *mm) +{ + unsigned char *me = mm->ME; + unsigned metype = mm->metype = getbits(me, 1, 5); + unsigned check_imf = 0; // 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) { @@ -809,358 +1181,44 @@ static void decodeExtendedSquitter(struct modesMessage *mm) } switch (metype) { - case 1: case 2: case 3: case 4: { - // Aircraft Identification and Category - uint32_t chars1, chars2; - - chars1 = getbits(me, 9, 32); - chars2 = getbits(me, 33, 56); - - // A common failure mode seems to be to intermittently send - // all zeros. Catch that here. - if (chars1 != 0 || chars2 != 0) { - mm->callsign_valid = 1; - - 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->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->callsign[8] = '\0'; - } - - mm->category = ((0x0E - metype) << 4) | mesub; - mm->category_valid = 1; + case 1: case 2: case 3: case 4: + decodeESIdentAndCategory(mm); break; - } - - case 19: { // Airborne Velocity Message - if (check_imf && getbit(me, 9)) - mm->addr |= MODES_NON_ICAO_ADDRESS; - - if ( (mesub >= 1) && (mesub <= 4) ) { - int vert_rate = getbits(me, 38, 46); - if (vert_rate) { - --vert_rate; - if (getbit(me, 37)) { - vert_rate = 0 - vert_rate; - } - mm->vert_rate = vert_rate * 64; - mm->vert_rate_valid = 1; - } - - mm->vert_rate_source = (getbit(me, 36) ? ALTITUDE_GNSS : ALTITUDE_BARO); - } - - if ((mesub == 1) || (mesub == 2)) { - unsigned ew_raw = getbits(me, 15, 24); - unsigned ns_raw = getbits(me, 26, 35); - - if (ew_raw && ns_raw) { - int ew_vel = (ew_raw - 1) * (getbit(me, 14) ? -1 : 1) * ((mesub == 2) ? 4 : 1); - int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mesub == 2) ? 4 : 1); - - // Compute velocity and angle from the two speed components - mm->speed = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); - mm->speed_valid = 1; - - 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 (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) { - unsigned airspeed = getbits(me, 26, 35); - if (airspeed) { - --airspeed; - if (mesub == 4) { // If (supersonic) unit is 4 kts - airspeed *= 4; - } - mm->speed = airspeed; - mm->speed_source = getbit(me, 25) ? SPEED_TAS : SPEED_IAS; - mm->speed_valid = 1; - } - - if (getbit(me, 14)) { - mm->heading = getbits(me, 15, 24); - mm->heading_source = HEADING_MAGNETIC; - mm->heading_valid = 1; - } - } - - unsigned raw_delta = getbits(me, 50, 56); - if (raw_delta) { - mm->gnss_delta_valid = 1; - mm->gnss_delta = (raw_delta - 1) * (getbit(me, 49) ? -25 : 25); - } + case 19: + decodeESAirborneVelocity(mm, check_imf); break; - } - - case 5: case 6: case 7: case 8: { - // Ground position - int movement; - - if (check_imf && getbit(me, 21)) - mm->addr |= MODES_NON_ICAO_ADDRESS; - - mm->airground = AG_GROUND; // definitely. - mm->cpr_lat = getbits(me, 23, 39); - mm->cpr_lon = getbits(me, 40, 56); - mm->cpr_odd = getbit(me, 22); - mm->cpr_nucp = (14 - metype); - mm->cpr_valid = 1; - - movement = getbits(me, 6, 12); - if ((movement) && (movement < 125)) { - mm->speed_valid = 1; - mm->speed = decodeMovementField(movement); - mm->speed_source = SPEED_GROUNDSPEED; - } - - if (getbit(me, 13)) { - mm->heading_valid = 1; - mm->heading_source = HEADING_TRUE; - mm->heading = getbits(me, 14, 20) * 360 / 128; - } + case 5: case 6: case 7: case 8: + decodeESSurfacePosition(mm, check_imf); 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 altitude (HAE or MSL) - int AC12Field = getbits(me, 9, 20); - - if (check_imf && getbit(me, 8)) - mm->addr |= MODES_NON_ICAO_ADDRESS; - - 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->cpr_lat = getbits(me, 23, 39); - mm->cpr_lon = getbits(me, 40, 56); - - 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 - // 400648 (BAE ATP) - Atlantic Airlines - // altitude == 0, longitude == 0, type == 15 and zeros in latitude LSB. - // Can alternate with valid reports having type == 14 - Modes.stats_current.cpr_filtered++; - } else { - // Otherwise, assume it's valid. - mm->cpr_valid = 1; - mm->cpr_odd = getbit(me, 22); - - 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 - 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; - } - + case 20: case 21: case 22: // Airborne position, GNSS altitude (HAE or MSL) + decodeESAirbornePosition(mm, check_imf); break; - } - case 23: { // Test message - if (mesub == 7) { // (see 1090-WP-15-20) - int ID13Field = getbits(me, 9, 21); - if (ID13Field) { - mm->squawk_valid = 1; - mm->squawk = decodeID13Field(ID13Field); - } - } + case 23: + decodeESTestMessage(mm); break; - } case 24: // Reserved for Surface System Status break; - case 28: { // Extended Squitter Aircraft Status - if (mesub == 1) { // Emergency status squawk field - int ID13Field = getbits(me, 12, 24); - if (ID13Field) { - mm->squawk_valid = 1; - mm->squawk = decodeID13Field(ID13Field); - } - - if (check_imf && getbit(me, 56)) - mm->addr |= MODES_NON_ICAO_ADDRESS; - } + case 28: + decodeESAircraftStatus(mm, check_imf); break; - } - case 29: // Aircraft Trajectory Intent - if (check_imf && getbit(me, 51)) - mm->addr |= MODES_NON_ICAO_ADDRESS; - - if (mesub == 0) { // Target state and status, V1 - // TODO: need RTCA/DO-260A - } else if (mesub == 1) { // Target state and status, V2 - mm->tss.valid = 1; - mm->tss.sil_type = getbit(me, 8) ? SIL_PER_SAMPLE : SIL_PER_HOUR; - mm->tss.altitude_type = getbit(me, 9) ? TSS_ALTITUDE_FMS : TSS_ALTITUDE_MCP; - - unsigned alt_bits = getbits(me, 10, 20); - if (alt_bits == 0) { - mm->tss.altitude_valid = 0; - } else { - mm->tss.altitude_valid = 1; - mm->tss.altitude = (alt_bits - 1) * 32; - } - - unsigned baro_bits = getbits(me, 21, 29); - if (baro_bits == 0) { - mm->tss.baro_valid = 0; - } else { - mm->tss.baro_valid = 1; - mm->tss.baro = 800.0 + (baro_bits - 1) * 0.8; - } - - mm->tss.heading_valid = getbit(me, 30); - if (mm->tss.heading_valid) { - // two's complement -180..+180, which is conveniently - // also the same as unsigned 0..360 - mm->tss.heading = getbits(me, 31, 39) * 180 / 256; - } - - mm->tss.nac_p = getbits(me, 40, 43); - mm->tss.nic_baro = getbit(me, 44); - mm->tss.sil = getbits(me, 45, 46); - mm->tss.mode_valid = getbit(me, 47); - if (mm->tss.mode_valid) { - mm->tss.mode_autopilot = getbit(me, 48); - mm->tss.mode_vnav = getbit(me, 49); - mm->tss.mode_alt_hold = getbit(me, 50); - mm->tss.mode_approach = getbit(me, 52); - } - - mm->tss.acas_operational = getbit(me, 53); - } + case 29: + decodeESTargetStatus(mm, check_imf); break; case 30: // Aircraft Operational Coordination break; - case 31: // Aircraft Operational Status - if (check_imf && getbit(me, 56)) - mm->addr |= MODES_NON_ICAO_ADDRESS; - - if (mm->mesub == 0 || mm->mesub == 1) { - mm->opstatus.valid = 1; - mm->opstatus.version = getbits(me, 41, 43); - - switch (mm->opstatus.version) { - case 0: - break; - - case 1: - if (getbits(me, 25, 26) == 0) { - mm->opstatus.om_acas_ra = getbit(me, 27); - mm->opstatus.om_ident = getbit(me, 28); - mm->opstatus.om_atc = getbit(me, 29); - } - - if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { - // airborne - mm->opstatus.cc_acas = !getbit(me, 11); - mm->opstatus.cc_cdti = getbit(me, 12); - mm->opstatus.cc_arv = getbit(me, 15); - mm->opstatus.cc_ts = getbit(me, 16); - mm->opstatus.cc_tc = getbits(me, 17, 18); - } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { - // surface - mm->opstatus.cc_poa = getbit(me, 11); - mm->opstatus.cc_cdti = getbit(me, 12); - mm->opstatus.cc_b2_low = getbit(me, 15); - mm->opstatus.cc_lw_valid = 1; - mm->opstatus.cc_lw = getbits(me, 21, 24); - } - - mm->opstatus.nic_supp_a = getbit(me, 44); - mm->opstatus.nac_p = getbits(me, 45, 48); - mm->opstatus.sil = getbits(me, 51, 52); - if (mm->mesub == 0) { - mm->opstatus.nic_baro = getbit(me, 53); - } else { - mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; - } - mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; - break; - - case 2: - default: - if (getbits(me, 25, 26) == 0) { - mm->opstatus.om_acas_ra = getbit(me, 27); - mm->opstatus.om_ident = getbit(me, 28); - mm->opstatus.om_atc = getbit(me, 29); - mm->opstatus.om_saf = getbit(me, 30); - mm->opstatus.om_sda = getbits(me, 31, 32); - } - - if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { - // airborne - mm->opstatus.cc_acas = getbit(me, 11); - mm->opstatus.cc_1090_in = getbit(me, 12); - mm->opstatus.cc_arv = getbit(me, 15); - mm->opstatus.cc_ts = getbit(me, 16); - mm->opstatus.cc_tc = getbits(me, 17, 18); - mm->opstatus.cc_uat_in = getbit(me, 19); - } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { - // surface - mm->opstatus.cc_poa = getbit(me, 11); - mm->opstatus.cc_1090_in = getbit(me, 12); - mm->opstatus.cc_b2_low = getbit(me, 15); - mm->opstatus.cc_uat_in = getbit(me, 16); - mm->opstatus.cc_nac_v = getbits(me, 17, 19); - mm->opstatus.cc_nic_supp_c = getbit(me, 20); - mm->opstatus.cc_lw_valid = 1; - mm->opstatus.cc_lw = getbits(me, 21, 24); - mm->opstatus.cc_antenna_offset = getbits(me, 33, 40); - } - - mm->opstatus.nic_supp_a = getbit(me, 44); - mm->opstatus.nac_p = getbits(me, 45, 48); - mm->opstatus.sil = getbits(me, 51, 52); - if (mm->mesub == 0) { - mm->opstatus.gva = getbits(me, 49, 50); - mm->opstatus.nic_baro = getbit(me, 53); - } else { - mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; - } - mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; - mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; - break; - } - } + case 31: + decodeESOperationalStatus(mm, check_imf); break; default: From 2636353e4ad25106a6c913566297bc90900feaaa Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 2 Sep 2016 18:41:59 +0100 Subject: [PATCH 08/18] Fix aircraft-relative position validity check. --- track.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/track.c b/track.c index 02fd026..7fc8734 100644 --- a/track.c +++ b/track.c @@ -266,7 +266,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now // find reference location double reflat, reflon; - if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID) <= 50000) { // Ok to try aircraft relative first + if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID)) { // Ok to try aircraft relative first reflat = a->lat; reflon = a->lon; if (a->pos_nuc < *nuc) From 1958b23a93d18d33c4e8225a0dbea10e3464235a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 2 Sep 2016 18:45:39 +0100 Subject: [PATCH 09/18] Tweak the on-the-ground conditions a little. --- net_io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index 7dc7fcb..9c4139f 100644 --- a/net_io.c +++ b/net_io.c @@ -1837,7 +1837,8 @@ static void writeFATSV() // don't send mode S very often minAge = 30000; } else if ((airgroundValid && a->airground == AG_GROUND) || - (altValid && a->altitude < 500 && speedValid && a->speed < 100)) { + (altValid && a->altitude < 500 && (!speedValid || a->speed < 200)) || + (speedValid && a->speed < 100 && (!altValid || a->altitude < 1000))) { // we are probably on the ground, increase the update rate minAge = 1000; } else if (!altValid || a->altitude < 10000) { From b827806ce813a2dec67ede3d23fcca37b1285fb6 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 15:58:29 +0100 Subject: [PATCH 10/18] Simplify ident decoding, add some sanity checks to the BDS2,0 case. --- mode_s.c | 65 ++++++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/mode_s.c b/mode_s.c index 3824a3f..94ec7af 100644 --- a/mode_s.c +++ b/mode_s.c @@ -736,30 +736,29 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) // Decode BDS2,0 carried in Comm-B or ES static void decodeBDS20(struct modesMessage *mm) { - uint32_t chars1, chars2; unsigned char *msg = mm->msg; - chars1 = getbits(msg, 41, 64); - chars2 = getbits(msg, 65, 88); - - // A common failure mode seems to be to intermittently send - // all zeros. Catch that here. - if (chars1 == 0 && chars2 == 0) - return; + mm->callsign[0] = ais_charset[getbits(msg, 41, 46)]; + mm->callsign[1] = ais_charset[getbits(msg, 47, 52)]; + mm->callsign[2] = ais_charset[getbits(msg, 53, 58)]; + mm->callsign[3] = ais_charset[getbits(msg, 59, 64)]; + mm->callsign[4] = ais_charset[getbits(msg, 65, 70)]; + mm->callsign[5] = ais_charset[getbits(msg, 71, 76)]; + mm->callsign[6] = ais_charset[getbits(msg, 77, 82)]; + mm->callsign[7] = ais_charset[getbits(msg, 83, 88)]; + mm->callsign[8] = 0; + // Catch possible bad decodings since BDS2,0 is not + // 100% reliable: accept only alphanumeric data mm->callsign_valid = 1; - - 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->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->callsign[8] = '\0'; + for (int i = 0; i < 8; ++i) { + if (! ((mm->callsign[i] >= 'A' && mm->callsign[i] <= 'Z') || + (mm->callsign[i] >= '0' && mm->callsign[i] <= '9') || + mm->callsign[i] == ' ') ) { + mm->callsign_valid = 0; + break; + } + } } static void decodeESIdentAndCategory(struct modesMessage *mm) @@ -769,26 +768,18 @@ static void decodeESIdentAndCategory(struct modesMessage *mm) mm->mesub = getbits(me, 6, 8); - unsigned chars1 = getbits(me, 9, 32); - unsigned chars2 = getbits(me, 33, 56); + mm->callsign[0] = ais_charset[getbits(me, 9, 14)]; + mm->callsign[1] = ais_charset[getbits(me, 15, 20)]; + mm->callsign[2] = ais_charset[getbits(me, 21, 26)]; + mm->callsign[3] = ais_charset[getbits(me, 27, 32)]; + mm->callsign[4] = ais_charset[getbits(me, 33, 38)]; + mm->callsign[5] = ais_charset[getbits(me, 39, 44)]; + mm->callsign[6] = ais_charset[getbits(me, 45, 50)]; + mm->callsign[7] = ais_charset[getbits(me, 51, 56)]; // A common failure mode seems to be to intermittently send // all zeros. Catch that here. - if (chars1 != 0 || chars2 != 0) { - mm->callsign_valid = 1; - - 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->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->callsign[8] = '\0'; - } + mm->callsign_valid = (strcmp(mm->callsign, "@@@@@@@@") != 0); mm->category = ((0x0E - mm->metype) << 4) | mm->mesub; mm->category_valid = 1; From 9e179d46bf954c93ba0b23922d40457e57d01325 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 16:11:31 +0100 Subject: [PATCH 11/18] Don't accept all-zeros messages (e.g. off the network - the demodulator doesn't accept them already) --- mode_s.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mode_s.c b/mode_s.c index 94ec7af..c28fce4 100644 --- a/mode_s.c +++ b/mode_s.c @@ -307,12 +307,12 @@ static inline __attribute__((always_inline)) unsigned getbits(unsigned char *da // -1: message might be valid, but we couldn't validate the CRC against a known ICAO // -2: bad message or unrepairable CRC error +static unsigned char all_zeros[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int scoreModesMessage(unsigned char *msg, int validbits) { int msgtype, msgbits, crc, iid; uint32_t addr; struct errorinfo *ei; - static unsigned char all_zeros[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (validbits < 56) return -2; @@ -434,6 +434,10 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) } msg = mm->msg; + // don't accept all-zeros messages + if (!memcmp(all_zeros, msg, 7)) + return -2; + // Get the message type ASAP as other operations depend on this mm->msgtype = getbits(msg, 1, 5); // Downlink Format mm->msgbits = modesMessageLenByType(mm->msgtype); From 10641303cba3f0887942039ec1d92337b517321e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 16:12:08 +0100 Subject: [PATCH 12/18] Add a couple more ME types. --- mode_s.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mode_s.c b/mode_s.c index c28fce4..7923b0f 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1407,12 +1407,17 @@ static const char *esTypeName(unsigned metype, unsigned mesub) case 29: switch (mesub) { + case 0: + return "Target state and status (V1)"; case 1: - return "Target state and status"; + return "Target state and status (V2)"; default: return "Unknown"; } + case 30: + return "Aircraft Operational Coordination"; + case 31: // Aircraft Operational Status switch (mesub) { case 0: From 2a4aa29f030b910f541301b31de62d3bbae44d4b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 16:37:07 +0100 Subject: [PATCH 13/18] Record the address type details when decoding. --- dump1090.h | 14 +++++++++++ mode_s.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/dump1090.h b/dump1090.h index 0a4cb73..02bb43d 100644 --- a/dump1090.h +++ b/dump1090.h @@ -149,6 +149,19 @@ typedef enum { SOURCE_ADSB, /* data from a ADS-B extended squitter message */ } datasource_t; +/* What sort of address is this and who sent it? */ +typedef enum { + ADDR_ADSB_ICAO, /* ADS-B, ICAO address, transponder sourced */ + ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */ + ADDR_ADSB_OTHER, /* ADS-B, other address format, non-transponder */ + ADDR_TISB_ICAO, /* TIS-B, ICAO address */ + ADDR_TISB_OTHER, /* TIS-B, other address format */ + ADDR_TISB_ANON, /* ADS-R/TIS-B, anonymized address */ + ADDR_ADSR_ICAO, /* ADS-R, ICAO address */ + ADDR_ADSR_OTHER, /* ADS-R, other address format */ + ADDR_UNKNOWN /* unknown address format */ +} addrtype_t; + typedef enum { UNIT_FEET, UNIT_METERS @@ -366,6 +379,7 @@ struct modesMessage { uint32_t crc; // Message CRC int correctedbits; // No. of bits corrected uint32_t addr; // Address Announced + addrtype_t addrtype; // address format / source uint64_t timestampMsg; // Timestamp of the message (12MHz clock) struct timespec sysTimestampMsg; // Timestamp of the message (system time) int remote; // If set this message is from a remote station diff --git a/mode_s.c b/mode_s.c index 7923b0f..d3d5650 100644 --- a/mode_s.c +++ b/mode_s.c @@ -789,6 +789,30 @@ static void decodeESIdentAndCategory(struct modesMessage *mm) mm->category_valid = 1; } +// Handle setting a non-ICAO address +static void setIMF(struct modesMessage *mm) +{ + mm->addr |= MODES_NON_ICAO_ADDRESS; + switch (mm->addrtype) { + case ADDR_ADSB_ICAO: + case ADDR_ADSB_ICAO_NT: + mm->addrtype = ADDR_ADSB_OTHER; + break; + + case ADDR_TISB_ICAO: + mm->addrtype = ADDR_TISB_OTHER; + break; + + case ADDR_ADSR_ICAO: + mm->addrtype = ADDR_ADSR_OTHER; + break; + + default: + // Nothing. + break; + } +} + static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) { // Airborne Velocity Message @@ -797,7 +821,7 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) mm->mesub = getbits(me, 6, 8); if (check_imf && getbit(me, 9)) - mm->addr |= MODES_NON_ICAO_ADDRESS; + setIMF(mm); if (mm->mesub < 1 || mm->mesub > 4) return; @@ -870,7 +894,7 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf) unsigned char *me = mm->ME; if (check_imf && getbit(me, 21)) - mm->addr |= MODES_NON_ICAO_ADDRESS; + setIMF(mm); mm->airground = AG_GROUND; // definitely. mm->cpr_lat = getbits(me, 23, 39); @@ -899,7 +923,7 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) unsigned char *me = mm->ME; if (check_imf && getbit(me, 8)) - mm->addr |= MODES_NON_ICAO_ADDRESS; + setIMF(mm); unsigned AC12Field = getbits(me, 9, 20); @@ -974,7 +998,7 @@ static void decodeESAircraftStatus(struct modesMessage *mm, int check_imf) } if (check_imf && getbit(me, 56)) - mm->addr |= MODES_NON_ICAO_ADDRESS; + setIMF(mm); } } @@ -985,7 +1009,7 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) mm->mesub = getbits(me, 6, 7); // an unusual message: only 2 bits of subtype if (check_imf && getbit(me, 51)) - mm->addr |= MODES_NON_ICAO_ADDRESS; + setIMF(mm); if (mm->mesub == 0) { // Target state and status, V1 // TODO: need RTCA/DO-260A @@ -1040,7 +1064,7 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) // Aircraft Operational Status if (check_imf && getbit(me, 56)) - mm->addr |= MODES_NON_ICAO_ADDRESS; + setIMF(mm); if (mm->mesub == 0 || mm->mesub == 1) { mm->opstatus.valid = 1; @@ -1141,14 +1165,17 @@ static void decodeExtendedSquitter(struct modesMessage *mm) if (mm->msgtype == 18) { switch (mm->CF) { case 0: // ADS-B ES/NT devices that report the ICAO 24-bit address in the AA field + mm->addrtype = ADDR_ADSB_ICAO_NT; break; case 1: // Reserved for ADS-B for ES/NT devices that use other addressing techniques in the AA field + mm->addrtype = ADDR_ADSB_OTHER; mm->addr |= MODES_NON_ICAO_ADDRESS; break; case 2: // Fine TIS-B message (formats are close enough to DF17 for our purposes) mm->source = SOURCE_TISB; + mm->addrtype = ADDR_TISB_ICAO; check_imf = 1; break; @@ -1156,20 +1183,26 @@ static void decodeExtendedSquitter(struct modesMessage *mm) // TODO: decode me. // For now we only look at the IMF bit. mm->source = SOURCE_TISB; - if (getbit(me, 1)) + mm->addrtype = ADDR_TISB_ICAO; + if (getbit(me, 1)) { mm->addr |= MODES_NON_ICAO_ADDRESS; + mm->addrtype = ADDR_TISB_OTHER; + } 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->addrtype = ADDR_TISB_ANON; mm->source = SOURCE_TISB; mm->addr |= MODES_NON_ICAO_ADDRESS; break; case 6: // ADS-B rebroadcast using the same type codes and message formats as defined for DF = 17 ADS-B messages + mm->addrtype = ADDR_ADSR_ICAO; check_imf = 1; break; default: // All others, we don't know the format. + mm->addrtype = ADDR_UNKNOWN; mm->addr |= MODES_NON_ICAO_ADDRESS; // assume non-ICAO return; } @@ -1325,6 +1358,29 @@ static const char *speed_source_to_string(speed_source_t speed) { } } +static const char *addrtype_to_string(addrtype_t type) { + switch (type) { + case ADDR_ADSB_ICAO: + return "ADS-B"; + case ADDR_ADSB_ICAO_NT: + return "ADS-B, non-transponder"; + case ADDR_ADSB_OTHER: + return "ADS-B, other addressing scheme"; + case ADDR_TISB_ICAO: + return "TIS-B"; + case ADDR_TISB_OTHER: + return "TIS-B, other addressing scheme"; + case ADDR_TISB_ANON: + return "TIS-B, anonymized address"; + case ADDR_ADSR_ICAO: + return "ADS-R"; + case ADDR_ADSR_OTHER: + return "ADS-R, other addressing scheme"; + default: + return "unknown addressing scheme"; + } +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -1562,9 +1618,9 @@ void displayModesMessage(struct modesMessage *mm) { printf("\n"); if (mm->addr & MODES_NON_ICAO_ADDRESS) { - printf(" Other Address: %06X\n", mm->addr); + printf(" Other Address: %06X (%s)\n", mm->addr & 0xFFFFFF, addrtype_to_string(mm->addrtype)); } else { - printf(" ICAO Address: %06X\n", mm->addr); + printf(" ICAO Address: %06X (%s)\n", mm->addr, addrtype_to_string(mm->addrtype)); } if (mm->airground != AG_INVALID) { From 8d8443f3dc95dc84b238282a69e45668af98f366 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 16:54:00 +0100 Subject: [PATCH 14/18] Track the "best" addrtype we ever saw for an aircraft. --- dump1090.h | 15 ++++++++++----- track.c | 5 +++++ track.h | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/dump1090.h b/dump1090.h index 02bb43d..c9c7d9c 100644 --- a/dump1090.h +++ b/dump1090.h @@ -149,16 +149,21 @@ typedef enum { SOURCE_ADSB, /* data from a ADS-B extended squitter message */ } datasource_t; -/* What sort of address is this and who sent it? */ +/* What sort of address is this and who sent it? + * (Earlier values are higher priority) + */ typedef enum { ADDR_ADSB_ICAO, /* ADS-B, ICAO address, transponder sourced */ ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */ - ADDR_ADSB_OTHER, /* ADS-B, other address format, non-transponder */ - ADDR_TISB_ICAO, /* TIS-B, ICAO address */ - ADDR_TISB_OTHER, /* TIS-B, other address format */ - ADDR_TISB_ANON, /* ADS-R/TIS-B, anonymized address */ ADDR_ADSR_ICAO, /* ADS-R, ICAO address */ + ADDR_TISB_ICAO, /* TIS-B, ICAO address */ + + ADDR_ADSB_OTHER, /* ADS-B, other address format, non-transponder */ ADDR_ADSR_OTHER, /* ADS-R, other address format */ + ADDR_TISB_OTHER, /* TIS-B, other address format */ + + ADDR_TISB_ANON, /* ADS-R/TIS-B, anonymized address */ + ADDR_UNKNOWN /* unknown address format */ } addrtype_t; diff --git a/track.c b/track.c index 7fc8734..7fff6f7 100644 --- a/track.c +++ b/track.c @@ -66,6 +66,7 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { // Now initialise things that should not be 0/NULL to their defaults a->addr = mm->addr; + a->addrtype = mm->addrtype; for (i = 0; i < 8; ++i) a->signalLevel[i] = 1e-5; a->signalNext = 0; @@ -534,6 +535,10 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->seen = now; a->messages++; + // update addrtype, we only ever go towards "more direct" types + if (mm->addrtype < a->addrtype) + a->addrtype = mm->addrtype; + 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) { diff --git a/track.h b/track.h index 1cab266..574c29f 100644 --- a/track.h +++ b/track.h @@ -69,6 +69,7 @@ typedef struct { /* Structure used to describe the state of one tracked aircraft */ struct aircraft { uint32_t addr; // ICAO address + addrtype_t addrtype; // highest priority address type seen for this aircraft uint64_t seen; // Time (millis) at which the last packet was received long messages; // Number of Mode S messages received From 0ae2c5efc8f600b2503823eb4c97f9112d04a997 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 17:01:07 +0100 Subject: [PATCH 15/18] Emit addrtype in json output. --- net_io.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/net_io.c b/net_io.c index 9c4139f..103230b 100644 --- a/net_io.c +++ b/net_io.c @@ -987,6 +987,29 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s return p; } +static const char *addrtype_short_string(addrtype_t type) { + switch (type) { + case ADDR_ADSB_ICAO: + return "adsb_icao"; + case ADDR_ADSB_ICAO_NT: + return "adsb_icao_nt"; + case ADDR_ADSB_OTHER: + return "adsb_other"; + case ADDR_TISB_ICAO: + return "tisb_icao"; + case ADDR_TISB_OTHER: + return "tisb_other"; + case ADDR_TISB_ANON: + return "tisb_anon"; + case ADDR_ADSR_ICAO: + return "adsr_icao"; + case ADDR_ADSR_OTHER: + return "adsr_other"; + default: + return "unknown"; + } +} + char *generateAircraftJson(const char *url_path, int *len) { uint64_t now = mstime(); struct aircraft *a; @@ -1018,6 +1041,8 @@ 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->addrtype != ADDR_ADSB_ICAO) + p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_short_string(a->addrtype)); if (trackDataValid(&a->squawk_valid)) p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk); if (trackDataValid(&a->callsign_valid)) From 8441cf7d90e73b47e109ff6209fab371cd124847 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 17:02:23 +0100 Subject: [PATCH 16/18] Emit non-icao addresses, address types from faup1090 --- net_io.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/net_io.c b/net_io.c index 103230b..414e3cb 100644 --- a/net_io.c +++ b/net_io.c @@ -1692,8 +1692,19 @@ static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafiel char *end = p + TSV_MAX_PACKET_SIZE; # define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) - p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64 "\thexid\t%06X\t%s\t", mstime() / 1000, mm->addr, datafield); + p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, mstime() / 1000); + if (mm->addr & MODES_NON_ICAO_ADDRESS) { + p += snprintf(p, bufsize(p, end), "otherid\t%06X\t", mm->addr & 0xFFFFFF); + } else { + p += snprintf(p, bufsize(p, end), "hexid\t%06X\t", mm->addr); + } + + if (mm->addrtype != ADDR_ADSB_ICAO) { + p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(mm->addrtype)); + } + + p += snprintf(p, bufsize(p, end), "\t%s\t", datafield); for (size_t i = 0; i < len; ++i) { p += snprintf(p, bufsize(p, end), "%02X", data[i]); } @@ -1715,10 +1726,6 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) return; // not enabled or no active connections } - // skip non-ICAO - if (mm->addr & MODES_NON_ICAO_ADDRESS) - return; - if (a->messages < 2) // basic filter for bad decodes return; @@ -1800,10 +1807,6 @@ static void writeFATSV() int used_tisb = 0; - // skip non-ICAO - if (a->addr & MODES_NON_ICAO_ADDRESS) - continue; - if (a->messages < 2) // basic filter for bad decodes continue; @@ -1883,7 +1886,18 @@ static void writeFATSV() end = p + TSV_MAX_PACKET_SIZE; # 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); + + p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, (uint64_t)(a->seen / 1000)); + + if (a->addr & MODES_NON_ICAO_ADDRESS) { + p += snprintf(p, bufsize(p, end), "otherid\t%06X\t", a->addr & 0xFFFFFF); + } else { + p += snprintf(p, bufsize(p, end), "hexid\t%06X\t", a->addr); + } + + if (a->addrtype != ADDR_ADSB_ICAO) { + p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(a->addrtype)); + } 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); From 4dacb291aba96c2f0cbbe2c90142b38251303ba5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 17:19:36 +0100 Subject: [PATCH 17/18] Fix tabs in faup1090 output. --- net_io.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net_io.c b/net_io.c index 414e3cb..60be682 100644 --- a/net_io.c +++ b/net_io.c @@ -1695,9 +1695,9 @@ static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafiel p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, mstime() / 1000); if (mm->addr & MODES_NON_ICAO_ADDRESS) { - p += snprintf(p, bufsize(p, end), "otherid\t%06X\t", mm->addr & 0xFFFFFF); + p += snprintf(p, bufsize(p, end), "\totherid\t%06X", mm->addr & 0xFFFFFF); } else { - p += snprintf(p, bufsize(p, end), "hexid\t%06X\t", mm->addr); + p += snprintf(p, bufsize(p, end), "\thexid\t%06X", mm->addr); } if (mm->addrtype != ADDR_ADSB_ICAO) { @@ -1890,9 +1890,9 @@ static void writeFATSV() p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, (uint64_t)(a->seen / 1000)); if (a->addr & MODES_NON_ICAO_ADDRESS) { - p += snprintf(p, bufsize(p, end), "otherid\t%06X\t", a->addr & 0xFFFFFF); + p += snprintf(p, bufsize(p, end), "\totherid\t%06X", a->addr & 0xFFFFFF); } else { - p += snprintf(p, bufsize(p, end), "hexid\t%06X\t", a->addr); + p += snprintf(p, bufsize(p, end), "\thexid\t%06X", a->addr); } if (a->addrtype != ADDR_ADSB_ICAO) { From 50a606d23f9aba1628ee315721d3185851c19b01 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 14 Sep 2016 17:19:49 +0100 Subject: [PATCH 18/18] Make it clearer that ADDR_ADSB_ICAO includes Mode S too. --- dump1090.h | 2 +- mode_s.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dump1090.h b/dump1090.h index c9c7d9c..b7eb1ab 100644 --- a/dump1090.h +++ b/dump1090.h @@ -153,7 +153,7 @@ typedef enum { * (Earlier values are higher priority) */ typedef enum { - ADDR_ADSB_ICAO, /* ADS-B, ICAO address, transponder sourced */ + ADDR_ADSB_ICAO, /* Mode S or ADS-B, ICAO address, transponder sourced */ ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */ ADDR_ADSR_ICAO, /* ADS-R, ICAO address */ ADDR_TISB_ICAO, /* TIS-B, ICAO address */ diff --git a/mode_s.c b/mode_s.c index d3d5650..ff51cee 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1361,7 +1361,7 @@ static const char *speed_source_to_string(speed_source_t speed) { static const char *addrtype_to_string(addrtype_t type) { switch (type) { case ADDR_ADSB_ICAO: - return "ADS-B"; + return "Mode S / ADS-B"; case ADDR_ADSB_ICAO_NT: return "ADS-B, non-transponder"; case ADDR_ADSB_OTHER: