WIP big rewrite of message decoding / aircraft tracking.

This commit is contained in:
Oliver Jowett 2016-08-27 14:34:14 +01:00
parent f152bf633e
commit 54ca2c7fb2
9 changed files with 1313 additions and 959 deletions

View file

@ -306,7 +306,6 @@ void demodulate2400(struct mag_buf *mag)
normalize_timespec(&mm.sysTimestampMsg); normalize_timespec(&mm.sysTimestampMsg);
mm.score = bestscore; mm.score = bestscore;
mm.bFlags = mm.correctedbits = 0;
// Decode the received message // Decode the received message
{ {

View file

@ -1019,7 +1019,7 @@ int main(int argc, char **argv) {
} else if (!strcmp(argv[j],"--metric")) { } else if (!strcmp(argv[j],"--metric")) {
Modes.metric = 1; Modes.metric = 1;
} else if (!strcmp(argv[j],"--hae") || !strcmp(argv[j],"--gnss")) { } else if (!strcmp(argv[j],"--hae") || !strcmp(argv[j],"--gnss")) {
Modes.use_hae = 1; Modes.use_gnss = 1;
} else if (!strcmp(argv[j],"--aggressive")) { } else if (!strcmp(argv[j],"--aggressive")) {
#ifdef ALLOW_AGGRESSIVE #ifdef ALLOW_AGGRESSIVE
Modes.nfix_crc = MODES_MAX_BITERRORS; Modes.nfix_crc = MODES_MAX_BITERRORS;

View file

@ -2,7 +2,7 @@
// //
// dump1090.h: main program header // dump1090.h: main program header
// //
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk> // Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
// //
// This file is free software: you may copy, redistribute and/or modify it // 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 // 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_SIZE (MODES_OUT_BUF_SIZE - 256)
#define MODES_OUT_FLUSH_INTERVAL (60000) #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_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) #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_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses
#define MODES_DEBUG_DEMOD (1<<0) #define MODES_DEBUG_DEMOD (1<<0)
@ -310,7 +318,7 @@ struct { // Internal state
int stats_range_histo; // Collect/show a range histogram? int stats_range_histo; // Collect/show a range histogram?
int onlyaddr; // Print only ICAO addresses int onlyaddr; // Print only ICAO addresses
int metric; // Use metric units 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 mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...;
int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 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. 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 double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power
int score; // Scoring from scoreModesMessage, if used int score; // Scoring from scoreModesMessage, if used
// DF 11, DF 17 datasource_t source; // Characterizes the overall message source
int ca; // Responder capabilities
int iid;
// DF 17, DF 18 // Raw data, just extracted directly from the message
int metype; // Extended squitter message type. // The names reflect the field names in Annex 4
int mesub; // Extended squitter message subtype. unsigned IID; // extracted from CRC of DF11s
int heading; // Reported by aircraft, or computed from from EW and NS velocity unsigned AA;
int raw_latitude; // Non decoded latitude. unsigned AC;
int raw_longitude; // Non decoded longitude. unsigned CA;
unsigned nuc_p; // NUCp value implied by message type unsigned CC;
double fLat; // Coordinates obtained from CPR encoded data if/when decoded unsigned CF;
double fLon; // Coordinates obtained from CPR encoded data if/when decoded unsigned DR;
char flight[16]; // 8 chars flight number. unsigned FS;
int ew_velocity; // E/W velocity. unsigned ID;
int ns_velocity; // N/S velocity. unsigned KE;
int vert_rate; // Vertical rate. unsigned ND;
int velocity; // Reported by aircraft, or computed from from EW and NS velocity 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 unsigned category; // A0 - D7 encoded as a single hex byte
int altitude_hae; // altitude reported as GNSS HAE // valid if cpr_valid
int hae_delta; // difference between HAE and baro alt unsigned cpr_lat; // Non decoded latitude.
unsigned cpr_lon; // Non decoded longitude.
unsigned cpr_nucp; // NUCp/NIC value implied by message type
// DF 18 airground_t airground; // air/ground state
int cf; // Control Field
// DF4, DF5, DF20, DF21 // valid if cpr_decoded:
int fs; // Flight status for DF4,5,20,21 double decoded_lat;
int modeA; // 13 bits identity (Squawk). double decoded_lon;
// 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
}; };
// This one needs modesMessage: // This one needs modesMessage:

View file

@ -122,25 +122,28 @@ void interactiveShowData(void) {
char strTt[5] = " "; char strTt[5] = " ";
char strGs[5] = " "; char strGs[5] = " ";
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { if (trackDataValid(&a->squawk_valid)) {
snprintf(strSquawk,5,"%04x", a->modeA);} snprintf(strSquawk,5,"%04x", a->squawk);
}
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) { if (trackDataValid(&a->speed_valid)) {
snprintf (strGs, 5,"%3d", convert_speed(a->speed));} snprintf (strGs, 5,"%3d", convert_speed(a->speed));
}
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { if (trackDataValid(&a->heading_valid)) {
snprintf (strTt, 5,"%03d", a->track);} snprintf (strTt, 5,"%03d", a->heading);
}
if (msgs > 99999) { if (msgs > 99999) {
msgs = 99999;} msgs = 99999;
}
if (Modes.interactive_rtl1090) { // RTL1090 display mode if (Modes.interactive_rtl1090) { // RTL1090 display mode
if (trackDataValid(&a->altitude_valid)) {
if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { snprintf(strFl,6,"F%03d",((a->altitude+50)/100));
snprintf(strFl,6,"F%03d",(a->altitude/100));
} }
printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2.0f\n", 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 } else { // Dump1090 display mode
char strMode[5] = " "; char strMode[5] = " ";
@ -158,22 +161,22 @@ void interactiveShowData(void) {
if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';} if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';} 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(strLat, 8,"%7.03f", a->lat);
snprintf(strLon, 9,"%8.03f", a->lon); 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"); snprintf(strFl, 7," grnd");
} else if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) { } else if (Modes.use_gnss && trackDataValid(&a->altitude_gnss_valid)) {
snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_hae)); snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_gnss));
} else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { } else if (trackDataValid(&a->altitude_valid)) {
snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude)); 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", 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), (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); strLat, strLon, 10 * log10(signalAverage), msgs, (now - a->seen)/1000.0);
} }
count++; count++;

View file

@ -89,18 +89,21 @@ void decodeModeAMessage(struct modesMessage *mm, int ModeA)
mm->addr = (ModeA & 0x0000FF7F) | MODES_NON_ICAO_ADDRESS; mm->addr = (ModeA & 0x0000FF7F) | MODES_NON_ICAO_ADDRESS;
// Set the Identity field to ModeA // Set the Identity field to ModeA
mm->modeA = ModeA & 0x7777; mm->squawk = ModeA & 0x7777;
mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID; mm->squawk_valid = 1;
// Flag ident in flight status // 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 // Decode an altitude if this looks like a possible mode C
if (!mm->fs) { if (!mm->spi) {
int modeC = ModeAToModeC(ModeA); int modeC = ModeAToModeC(ModeA);
if (modeC >= -12) { if (modeC != INVALID_ALTITUDE) {
mm->altitude = modeC * 100; mm->altitude = modeC * 100;
mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID; mm->altitude_unit = UNIT_FEET;
mm->altitude_source = ALTITUDE_BARO;
mm->altitude_valid = 1;
} }
} }

1027
mode_s.c

File diff suppressed because it is too large Load diff

453
net_io.c
View file

@ -2,7 +2,7 @@
// //
// net_io.c: network handling. // net_io.c: network handling.
// //
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk> // Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
// //
// This file is free software: you may copy, redistribute and/or modify it // 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 // 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 // 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) { static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
char *p; char *p;
@ -515,38 +514,48 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
// //
// Decide on the basic SBS Message Type // Decide on the basic SBS Message Type
if ((mm->msgtype == 4) || (mm->msgtype == 20)) { switch (mm->msgtype) {
case 4:
case 20:
msgType = 5; msgType = 5;
} else if ((mm->msgtype == 5) || (mm->msgtype == 21)) { break;
break;
case 5:
case 21:
msgType = 6; msgType = 6;
} else if ((mm->msgtype == 0) || (mm->msgtype == 16)) { break;
case 0:
case 16:
msgType = 7; msgType = 7;
} else if (mm->msgtype == 11) { break;
case 11:
msgType = 8; msgType = 8;
} else if ((mm->msgtype != 17) && (mm->msgtype != 18)) { break;
return;
} else if ((mm->metype >= 1) && (mm->metype <= 4)) { case 17:
msgType = 1; case 18:
} else if ((mm->metype >= 5) && (mm->metype <= 8)) { if (mm->metype >= 1 && mm->metype <= 4) {
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) msgType = 1;
{msgType = 2;} } else if (mm->metype >= 5 && mm->metype <= 8) {
else msgType = 2;
{msgType = 7;} } else if (mm->metype >= 9 && mm->metype <= 18) {
} else if ((mm->metype >= 9) && (mm->metype <= 18)) { msgType = 3;
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) } else if (mm->metype == 19) {
{msgType = 3;} msgType = 4;
else } else {
{msgType = 7;} return;
} else if (mm->metype != 19) { }
return; break;
} else if ((mm->mesub == 1) || (mm->mesub == 2)) {
msgType = 4; default:
} else {
return; return;
} }
// Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff // 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 // Find current system time
clock_gettime(CLOCK_REALTIME, &now); 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)); 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) // Field 11 is the callsign (if we have it)
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);} if (mm->callsign_valid) {p += sprintf(p, ",%s", mm->callsign);}
else {p += sprintf(p, ",");} else {p += sprintf(p, ",");}
// Field 12 is the altitude (if we have it) - force to zero if we're on the ground // Field 12 is the altitude (if we have it)
if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) { if (mm->altitude_valid) {
p += sprintf(p, ",0"); if (Modes.use_gnss) {
} else if (Modes.use_hae && (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) { if (mm->altitude_source == ALTITUDE_GNSS) {
p += sprintf(p, ",%dH", mm->altitude_hae); p += sprintf(p, ",%dH", mm->altitude);
} else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { } else if (trackDataValid(&a->gnss_delta_valid)) {
if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) { p += sprintf(p, ",%dH", mm->altitude + a->gnss_delta);
p += sprintf(p, ",%dH", mm->altitude + a->hae_delta); } else {
p += sprintf(p, ",%d", mm->altitude);
}
} else { } 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 { } else {
p += sprintf(p, ","); p += sprintf(p, ",");
} }
// Field 13 is the ground Speed (if we have it) // Field 13 is the ground Speed (if we have it)
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED) {
p += sprintf(p, ",%d", mm->velocity); p += sprintf(p, ",%d", mm->speed);
} else { } else {
p += sprintf(p, ","); p += sprintf(p, ",");
} }
// Field 14 is the ground Heading (if we have it) // 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); p += sprintf(p, ",%d", mm->heading);
} else { } else {
p += sprintf(p, ","); p += sprintf(p, ",");
} }
// Fields 15 and 16 are the Lat/Lon (if we have it) // 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);} if (mm->cpr_decoded) {
else {p += sprintf(p, ",,");} 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) // Field 17 is the VerticalRate (if we have it)
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);} if (mm->vert_rate_valid) {
else {p += sprintf(p, ",");} p += sprintf(p, ",%d", mm->vert_rate);
} else {
p += sprintf(p, ",");
}
// Field 18 is the Squawk (if we have it) // Field 18 is the Squawk (if we have it)
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);} if (mm->squawk_valid) {
else {p += sprintf(p, ",");} p += sprintf(p, ",%04x", mm->squawk);
} else {
p += sprintf(p, ",");
}
// Field 19 is the Squawk Changing Alert flag (if we have it) // Field 19 is the Squawk Changing Alert flag (if we have it)
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { if (mm->alert_valid) {
if ((mm->fs >= 2) && (mm->fs <= 4)) { if (mm->alert) {
p += sprintf(p, ",-1"); p += sprintf(p, ",-1");
} else { } else {
p += sprintf(p, ",0"); 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) // Field 20 is the Squawk Emergency flag (if we have it)
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { if (mm->squawk_valid) {
if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) { if ((mm->squawk == 0x7500) || (mm->squawk == 0x7600) || (mm->squawk == 0x7700)) {
p += sprintf(p, ",-1"); p += sprintf(p, ",-1");
} else { } else {
p += sprintf(p, ",0"); 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) // Field 21 is the Squawk Ident flag (if we have it)
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { if (mm->spi_valid) {
if ((mm->fs >= 4) && (mm->fs <= 5)) { if (mm->spi) {
p += sprintf(p, ",-1"); p += sprintf(p, ",-1");
} else { } else {
p += sprintf(p, ",0"); 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) // Field 22 is the OnTheGround flag (if we have it)
if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) { switch (mm->airground) {
if (mm->bFlags & MODES_ACFLAGS_AOG) { case AG_GROUND:
p += sprintf(p, ",-1"); p += sprintf(p, ",-1");
} else { break;
p += sprintf(p, ",0"); case AG_AIRBORNE:
} p += sprintf(p, ",0");
} else { break;
default:
p += sprintf(p, ","); p += sprintf(p, ",");
break;
} }
p += sprintf(p, "\r\n"); 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) { 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) { if (!is_mlat && mm->correctedbits < 2) {
// Don't ever forward 2-bit-corrected messages via SBS output. // Don't ever forward 2-bit-corrected messages via SBS output.
@ -928,24 +956,24 @@ static const char *jsonEscapeString(const char *str) {
return buf; 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, "["); p += snprintf(p, end-p, "[");
if (flags & MODES_ACFLAGS_SQUAWK_VALID) if (a->squawk_valid.source == source)
p += snprintf(p, end-p, "\"squawk\","); p += snprintf(p, end-p, "\"squawk\",");
if (flags & MODES_ACFLAGS_CALLSIGN_VALID) if (a->callsign_valid.source == source)
p += snprintf(p, end-p, "\"callsign\","); 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\","); 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\","); p += snprintf(p, end-p, "\"altitude\",");
if (flags & MODES_ACFLAGS_HEADING_VALID) if (a->heading_valid.source == source)
p += snprintf(p, end-p, "\"track\","); p += snprintf(p, end-p, "\"track\",");
if (flags & MODES_ACFLAGS_SPEED_VALID) if (a->speed_valid.source == source)
p += snprintf(p, end-p, "\"speed\","); 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\","); 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\","); p += snprintf(p, end-p, "\"category\",");
if (p[-1] != '[') if (p[-1] != '[')
--p; --p;
@ -984,32 +1012,29 @@ char *generateAircraftJson(const char *url_path, int *len) {
*p++ = ','; *p++ = ',';
p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); 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) if (trackDataValid(&a->squawk_valid))
p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->modeA); p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk);
if (a->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) if (trackDataValid(&a->callsign_valid))
p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->flight)); p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign));
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) 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->seenLatLon)/1000.0); 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 ((a->bFlags & MODES_ACFLAGS_AOG_VALID) && (a->bFlags & MODES_ACFLAGS_AOG)) if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND)
p += snprintf(p, end-p, ",\"altitude\":\"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); 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); p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate);
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) if (trackDataValid(&a->heading_valid))
p += snprintf(p, end-p, ",\"track\":%d", a->track); p += snprintf(p, end-p, ",\"track\":%d", a->heading);
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) if (trackDataValid(&a->speed_valid))
p += snprintf(p, end-p, ",\"speed\":%d", a->speed); 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); p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category);
if (a->mlatFlags) {
p += snprintf(p, end-p, ",\"mlat\":"); p += snprintf(p, end-p, ",\"mlat\":");
p = append_flags(p, end, a->mlatFlags); p = append_flags(p, end, a, SOURCE_MLAT);
} p += snprintf(p, end-p, ",\"tisb\":");
if (a->tisbFlags) { p = append_flags(p, end, a, SOURCE_TISB);
p += snprintf(p, end-p, ",\"tisb\":");
p = append_flags(p, end, a->tisbFlags);
}
p += snprintf(p, end-p, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}", p += snprintf(p, end-p, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}",
a->messages, (now - a->seen)/1000.0, a->messages, (now - a->seen)/1000.0,
@ -1471,11 +1496,12 @@ static int handleHTTPRequest(struct client *c, char *p) {
// Send header and content. // Send header and content.
#ifndef _WIN32 #ifndef _WIN32
if ( (write(c->fd, hdr, hdrlen) != hdrlen) if ( (write(c->fd, hdr, hdrlen) != hdrlen)
|| (write(c->fd, content, clen) != clen) ) { || (write(c->fd, content, clen) != clen) )
#else #else
if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen) if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen)
|| (send(c->fd, content, clen, 0) != clen) ) { || (send(c->fd, content, clen, 0) != clen) )
#endif #endif
{
free(content); free(content);
return 1; return 1;
} }
@ -1536,10 +1562,11 @@ static void modesReadFromClient(struct client *c) {
} }
#ifndef _WIN32 #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 #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 #endif
{
return; return;
} }
@ -1645,29 +1672,22 @@ static void writeFATSV()
for (a = Modes.aircrafts; a; a = a->next) { for (a = Modes.aircrafts; a; a = a->next) {
int altValid = 0; int altValid = 0;
int alt = 0; int altGNSSValid = 0;
uint64_t altAge = 999999; int positionValid = 0;
int groundValid = 0;
int ground = 0;
int latlonValid = 0;
uint64_t latlonAge = 999999;
int speedValid = 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 minAge;
uint64_t trackAge = 999999;
uint64_t emittedAge;
int useful = 0; int useful = 0;
int changed = 0; int changed = 0;
char *p, *end; char *p, *end;
int flags;
int used_tisb = 0; int used_tisb = 0;
// skip non-ICAO // skip non-ICAO
@ -1682,82 +1702,70 @@ static void writeFATSV()
continue; 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 // If we are definitely on the ground, suppress any unreliable altitude info.
flags = a->bFlags & ~a->mlatFlags; // 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
if (flags & MODES_ACFLAGS_ALTITUDE_VALID) { // might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000".
alt = a->altitude; if (airgroundValid && a->airground == AG_GROUND && a->altitude_valid.source < SOURCE_MODE_S_CHECKED)
altAge = now - a->seenAltitude; altValid = 0;
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 it hasn't changed altitude, heading, or speed much, // if it hasn't changed altitude, heading, or speed much,
// don't update so often // don't update so often
changed = 0; 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; changed = 1;
} }
if (speedValid && abs(a->speed - a->fatsv_emitted_speed) >= 25) { if (speedValid && abs(a->speed - a->fatsv_emitted_speed) >= 25) {
changed = 1; 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; 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 // Below 10000 feet, emit up to every 5s when changing, 10s otherwise
if (changed && emittedAge < 5000) minAge = (changed ? 5000 : 10000);
continue;
if (!changed && emittedAge < 10000)
continue;
} else { } else {
// Above 10000 feet, emit up to every 10s when changing, 30s otherwise // Above 10000 feet, emit up to every 10s when changing, 30s otherwise
if (changed && emittedAge < 10000) minAge = (changed ? 10000 : 30000);
continue;
if (!changed && emittedAge < 30000)
continue;
} }
if ((now - a->fatsv_last_emitted) < minAge)
continue;
p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE);
if (!p) if (!p)
return; return;
@ -1766,47 +1774,101 @@ static void writeFATSV()
# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) # 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%ld\thexid\t%06X", (long)(a->seen / 1000), a->addr);
if (*a->flight != '\0') { 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->flight); 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) { 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->modeA); 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 // only emit alt, speed, latlon, track if they have been received since the last time
// and are not stale // and are not stale
if (altValid && altAge < emittedAge) { if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\talt\t%d", alt); p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude);
useful = 1; a->fatsv_emitted_altitude = a->altitude;
} if (a->altitude_valid.source == SOURCE_TISB) {
used_tisb = 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");
} }
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); 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; useful = 1;
} }
if (trackValid && trackAge < emittedAge) { if (headingValid && a->heading_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\theading\t%d", a->track); 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; useful = 1;
} }
if (used_tisb) { if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\ttisb\t1"); 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 // if we didn't get at least an alt or a speed or a latlon or
@ -1816,6 +1878,10 @@ static void writeFATSV()
continue; continue;
} }
if (used_tisb) {
p += snprintf(p, bufsize(p,end), "\ttisb\t1");
}
p += snprintf(p, bufsize(p,end), "\n"); p += snprintf(p, bufsize(p,end), "\n");
if (p <= end) if (p <= end)
@ -1825,9 +1891,6 @@ static void writeFATSV()
# undef bufsize # undef bufsize
a->fatsv_last_emitted = now; 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(); uint64_t now = mstime();
int need_flush = 0; int need_flush = 0;
// Accept new connetions // Accept new connections
modesAcceptClients(); modesAcceptClients();
// Read from clients // Read from clients

407
track.c
View file

@ -2,7 +2,7 @@
// //
// track.c: aircraft state tracking // track.c: aircraft state tracking
// //
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk> // Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
// //
// This file is free software: you may copy, redistribute and/or modify it // 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 // 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. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dump1090.h" #include "dump1090.h"
#include <inttypes.h>
/* #define DEBUG_CPR_CHECKS */ /* #define DEBUG_CPR_CHECKS */
@ -74,7 +75,7 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
// time this ModeA/C is received again in the future // time this ModeA/C is received again in the future
if (mm->msgtype == 32) { if (mm->msgtype == 32) {
a->modeACflags = MODEAC_MSG_FLAG; a->modeACflags = MODEAC_MSG_FLAG;
if (!(mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID)) { if (!mm->altitude_valid) {
a->modeACflags |= MODEAC_MSG_MODEA_ONLY; a->modeACflags |= MODEAC_MSG_MODEA_ONLY;
} }
} }
@ -103,6 +104,51 @@ struct aircraft *trackFindAircraft(uint32_t addr) {
return (NULL); 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 // 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 // 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. // 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; uint64_t elapsed;
double distance; double distance;
@ -157,17 +203,17 @@ static int speed_check(struct aircraft *a, struct modesMessage *mm, double lat,
int speed; int speed;
int inrange; int inrange;
if (!(a->bFlags & MODES_ACFLAGS_LATLON_VALID)) if (!trackDataValid(&a->position_valid))
return 1; // no reference, assume OK 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)) if (trackDataValid(&a->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)
speed = a->speed; 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 else
speed = surface ? 100 : 600; // guess 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) static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double *lat, double *lon, unsigned *nuc)
{ {
int result; int result;
int fflag = (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) != 0; int fflag = mm->cpr_odd;
int surface = (mm->bFlags & MODES_ACFLAGS_AOG) != 0; 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) { if (surface) {
// surface global CPR // surface global CPR
// find reference location // find reference location
double reflat, reflon; 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; reflat = a->lat;
reflon = a->lon; reflon = a->lon;
if (a->pos_nuc < *nuc) 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, result = decodeCPRsurface(reflat, reflon,
a->even_cprlat, a->even_cprlon, a->cpr_even_lat, a->cpr_even_lon,
a->odd_cprlat, a->odd_cprlon, a->cpr_odd_lat, a->cpr_odd_lon,
fflag, fflag,
lat, lon); lat, lon);
} else { } else {
// airborne global CPR // airborne global CPR
result = decodeCPRairborne(a->even_cprlat, a->even_cprlon, result = decodeCPRairborne(a->cpr_even_lat, a->cpr_even_lon,
a->odd_cprlat, a->odd_cprlon, a->cpr_odd_lat, a->cpr_odd_lon,
fflag, fflag,
lat, lon); lat, lon);
} }
if (result < 0) { if (result < 0) {
#ifdef DEBUG_CPR_CHECKS #ifdef DEBUG_CPR_CHECKS
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) { fprintf(stderr, "CPR: decode failure for %06X (%d).\n", a->addr, result);
fprintf(stderr, "CPR: decode failure from MLAT (%06X) (%d).\n", a->addr, result); fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n",
fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n", a->cpr_even_lat, a->cpr_even_lon,
a->even_cprlat, a->even_cprlon, a->cpr_odd_lat, a->cpr_odd_lon,
a->odd_cprlat, a->odd_cprlon, fflag ? "odd" : "even");
fflag ? "odd" : "even");
}
#endif #endif
return result; 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 // for mlat results, skip the speed check
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) if (mm->source == SOURCE_MLAT)
return result; return result;
// check speed limit // 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++; Modes.stats_current.cpr_global_speed_checks++;
return -2; return -2;
} }
@ -290,12 +334,12 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
double reflat, reflon; double reflat, reflon;
double range_limit = 0; double range_limit = 0;
int result; int result;
int fflag = (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) != 0; int fflag = mm->cpr_odd;
int surface = (mm->bFlags & MODES_ACFLAGS_AOG) != 0; 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; reflat = a->lat;
reflon = a->lon; reflon = a->lon;
@ -331,12 +375,13 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
} }
result = decodeCPRrelative(reflat, reflon, result = decodeCPRrelative(reflat, reflon,
mm->raw_latitude, mm->cpr_lat,
mm->raw_longitude, mm->cpr_lon,
fflag, surface, fflag, surface,
lat, lon); lat, lon);
if (result < 0) if (result < 0) {
return result; return result;
}
// check range limit // check range limit
if (range_limit > 0) { if (range_limit > 0) {
@ -348,7 +393,10 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
} }
// check speed limit // 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++; Modes.stats_current.cpr_local_speed_checks++;
return -1; return -1;
} }
@ -356,73 +404,62 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
return 0; 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) static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t now)
{ {
int location_result = -1; int location_result = -1;
int max_elapsed; uint64_t max_elapsed;
double new_lat = 0, new_lon = 0; double new_lat = 0, new_lon = 0;
unsigned new_nuc = 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; ++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 // Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
if (mm->speed_valid && mm->speed <= 25)
if ((mm->bFlags & MODES_ACFLAGS_SPEED_VALID) && mm->velocity <= 25)
max_elapsed = 50000; max_elapsed = 50000;
else else
max_elapsed = 25000; max_elapsed = 25000;
} else { } else {
++Modes.stats_current.cpr_airborne;
// Airborne: 10 seconds // Airborne: 10 seconds
max_elapsed = 10000; 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 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); location_result = doGlobalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc);
if (location_result == -2) { if (location_result == -2) {
#ifdef DEBUG_CPR_CHECKS #ifdef DEBUG_CPR_CHECKS
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) { fprintf(stderr, "global CPR failure (invalid) for (%06X).\n", a->addr);
fprintf(stderr, "CPR failure from MLAT (%06X).\n", a->addr);
}
#endif #endif
// Global CPR failed because the position produced implausible results. // 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. // 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 // Also disable aircraft-relative positions until we have a new good position (but don't discard the
// recorded position itself) // recorded position itself)
Modes.stats_current.cpr_global_bad++; 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; return;
} else if (location_result == -1) { } else if (location_result == -1) {
#ifdef DEBUG_CPR_CHECKS #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); fprintf(stderr, "CPR skipped from MLAT (%06X).\n", a->addr);
} }
#endif #endif
@ -431,6 +468,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t
Modes.stats_current.cpr_global_skipped++; Modes.stats_current.cpr_global_skipped++;
} else { } else {
Modes.stats_current.cpr_global_ok++; 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) { if (location_result == -1) {
location_result = doLocalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc); 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++; Modes.stats_current.cpr_local_skipped++;
} else { } else {
Modes.stats_current.cpr_local_ok++; Modes.stats_current.cpr_local_ok++;
if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) mm->cpr_relative = 1;
Modes.stats_current.cpr_local_aircraft_relative++;
else if (mm->cpr_odd) {
Modes.stats_current.cpr_local_receiver_relative++; a->position_valid = a->cpr_odd_valid;
mm->bFlags |= MODES_ACFLAGS_REL_CPR_USED; } else {
a->position_valid = a->cpr_even_valid;
}
} }
} }
if (location_result == 0) { if (location_result == 0) {
// If we sucessfully decoded, back copy the results to mm so that we can print them in list output // 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->cpr_decoded = 1;
mm->fLat = new_lat; mm->decoded_lat = new_lat;
mm->fLon = new_lon; mm->decoded_lon = new_lon;
// Update aircraft state // Update aircraft state
a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK);
a->lat = new_lat; a->lat = new_lat;
a->lon = new_lon; a->lon = new_lon;
a->pos_nuc = new_nuc; a->pos_nuc = new_nuc;
a->seenLatLon = a->seen;
update_range_histogram(new_lat, new_lon); update_range_histogram(new_lat, new_lon);
} }
@ -493,126 +531,100 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
a->seen = now; a->seen = now;
a->messages++; a->messages++;
// if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) {
if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) { unsigned modeC = (a->altitude + 49) / 100;
a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG); if (modeC != a->altitude_modeC) {
} a->modeCcount = 0; //....zero the hit count
// 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
a->modeACflags &= ~MODEAC_MSG_MODEC_HIT; 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->squawk_valid && accept_data(&a->squawk_valid, mm->source, now)) {
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID) { if (mm->squawk != a->squawk) {
a->altitude_hae = mm->altitude_hae; a->modeAcount = 0; //....zero the hit count
// 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
a->modeACflags &= ~MODEAC_MSG_MODEA_HIT; 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->altitude_valid && mm->altitude_source == ALTITUDE_GNSS && accept_data(&a->altitude_gnss_valid, mm->source, now)) {
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) { a->altitude_gnss = mm->altitude;
a->track = mm->heading;
a->seenTrack = now;
} }
// If a (new) SPEED has been received, copy it to the aircraft structure if (mm->gnss_delta_valid && accept_data(&a->gnss_delta_valid, mm->source, now)) {
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) { a->gnss_delta = mm->gnss_delta;
a->speed = mm->velocity;
a->seenSpeed = now;
} }
// If a (new) Vertical Descent rate has been received, copy it to the aircraft structure if (mm->heading_valid && mm->heading_source == HEADING_TRUE && accept_data(&a->heading_valid, mm->source, now)) {
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) { 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 = 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->category_valid && accept_data(&a->category_valid, mm->source, now)) {
if (mm->bFlags & MODES_ACFLAGS_CATEGORY_VALID) {
a->category = mm->category; a->category = mm->category;
} }
// Update the aircrafts a->bFlags to reflect the newly received mm->bFlags; if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source, now)) {
a->bFlags |= mm->bFlags; a->airground = mm->airground;
// If we have a baro altitude and a HAE delta from baro, calculate the HAE altitude
if ((a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) {
a->altitude_hae = a->altitude + a->hae_delta;
a->bFlags |= MODES_ACFLAGS_ALTITUDE_HAE_VALID;
} }
// Update mlat flags. The mlat flags indicate which bits in bFlags if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source, now)) {
// were last set based on a mlat-derived message. memcpy(a->callsign, mm->callsign, sizeof(a->callsign));
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) }
a->mlatFlags = (a->mlatFlags & a->bFlags) | mm->bFlags;
else
a->mlatFlags = (a->mlatFlags & a->bFlags) & ~mm->bFlags;
// Same for TIS-B // CPR, even
if (mm->bFlags & MODES_ACFLAGS_FROM_TISB) if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) {
a->tisbFlags = (a->tisbFlags & a->bFlags) | mm->bFlags; a->cpr_even_airground = mm->airground;
else a->cpr_even_lat = mm->cpr_lat;
a->tisbFlags = (a->tisbFlags & a->bFlags) & ~mm->bFlags; 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) { if (mm->msgtype == 32) {
int flags = a->modeACflags; 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 ((b->modeACflags & MODEAC_MSG_FLAG) == 0) { // skip any fudged ICAO records
// If both (a) and (b) have valid squawks... // 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 // ...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->modeAcount = a->messages;
b->modeACflags |= MODEAC_MSG_MODEA_HIT; b->modeACflags |= MODEAC_MSG_MODEA_HIT;
a->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 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 // ... 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 if ( (a->altitude_modeC == b->altitude_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->altitude_modeC == b->altitude_modeC + 1) // or this Mode-C - 100 ft
|| (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft || (a->altitude_modeC + 1 == b->altitude_modeC ) ) { // or this Mode-C + 100 ft
b->modeCcount = a->messages; b->modeCcount = a->messages;
b->modeACflags |= MODEAC_MSG_MODEC_HIT; b->modeACflags |= MODEAC_MSG_MODEC_HIT;
a->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; prev->next = a->next; free(a); a = prev->next;
} }
} else { } else {
if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && (now - a->seenLatLon) > TRACK_AIRCRAFT_POSITION_TTL) {
/* Position is too old and no longer valid */ #define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0)
a->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); 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; prev = a; a = a->next;
} }
} }

148
track.h
View file

@ -2,7 +2,7 @@
// //
// track.h: aircraft state tracking prototypes // track.h: aircraft state tracking prototypes
// //
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk> // Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
// //
// This file is free software: you may copy, redistribute and/or modify it // 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 // under the terms of the GNU General Public License as published by the
@ -59,63 +59,131 @@
/* Maximum validity of an aircraft position */ /* Maximum validity of an aircraft position */
#define TRACK_AIRCRAFT_POSITION_TTL 60000 #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 */ /* Structure used to describe the state of one tracked aircraft */
struct aircraft { struct aircraft {
uint32_t addr; // ICAO address 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 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 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 modeAcount; // Mode A Squawk hit Count
long modeCcount; // Mode C Altitude hit Count long modeCcount; // Mode C Altitude hit Count
int modeACflags; // Flags for mode A/C recognition int modeACflags; // Flags for mode A/C recognition
int fatsv_emitted_altitude; // last FA emitted altitude int fatsv_emitted_altitude; // last FA emitted altitude
int fatsv_emitted_track; // last FA emitted track int fatsv_emitted_altitude_gnss; // -"- GNSS altitude
int fatsv_emitted_speed; // last FA emitted speed int fatsv_emitted_heading; // -"- true track
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted 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 fatsv_last_emitted; // time (millis) aircraft was last FA emitted
uint64_t odd_cprtime;
int odd_cprlat;
int odd_cprlon;
unsigned odd_cprnuc;
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 aircraft *next; // Next aircraft in our linked list
struct modesMessage first_message; // A copy of the first message we received for this aircraft. 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. /* Update aircraft state from data in the provided mesage.
* Return the tracked aircraft. * Return the tracked aircraft.