WIP big rewrite of message decoding / aircraft tracking.
This commit is contained in:
parent
f152bf633e
commit
54ca2c7fb2
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
180
dump1090.h
180
dump1090.h
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// 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
|
||||
// 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:
|
||||
|
|
|
@ -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++;
|
||||
|
|
15
mode_ac.c
15
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
421
net_io.c
421
net_io.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// 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
|
||||
// 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)) {
|
||||
break;
|
||||
|
||||
case 17:
|
||||
case 18:
|
||||
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)) {
|
||||
} 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);}
|
||||
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 {
|
||||
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) {
|
||||
switch (mm->airground) {
|
||||
case AG_GROUND:
|
||||
p += sprintf(p, ",-1");
|
||||
} else {
|
||||
break;
|
||||
case AG_AIRBORNE:
|
||||
p += sprintf(p, ",0");
|
||||
}
|
||||
} else {
|
||||
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 = append_flags(p, end, a, SOURCE_MLAT);
|
||||
p += snprintf(p, end-p, ",\"tisb\":");
|
||||
p = append_flags(p, end, a->tisbFlags);
|
||||
}
|
||||
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);
|
||||
if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) {
|
||||
p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude);
|
||||
a->fatsv_emitted_altitude = a->altitude;
|
||||
if (a->altitude_valid.source == SOURCE_TISB) {
|
||||
used_tisb = 1;
|
||||
}
|
||||
useful = 1;
|
||||
}
|
||||
|
||||
if (speedValid && speedAge < emittedAge) {
|
||||
if (altGNSSValid && a->altitude_gnss_valid.updated > a->fatsv_last_emitted) {
|
||||
p += snprintf(p, bufsize(p,end), "\talt_gnss\t%d", a->altitude_gnss);
|
||||
a->fatsv_emitted_altitude_gnss = a->altitude_gnss;
|
||||
if (a->altitude_gnss_valid.source == SOURCE_TISB) {
|
||||
used_tisb = 1;
|
||||
}
|
||||
useful = 1;
|
||||
}
|
||||
|
||||
if (speedValid && a->speed_valid.updated > a->fatsv_last_emitted) {
|
||||
p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed);
|
||||
a->fatsv_emitted_speed = a->speed;
|
||||
if (a->speed_valid.source == SOURCE_TISB) {
|
||||
used_tisb = 1;
|
||||
}
|
||||
useful = 1;
|
||||
}
|
||||
|
||||
if (groundValid) {
|
||||
if (ground) {
|
||||
p += snprintf(p, bufsize(p,end), "\tairGround\tG");
|
||||
} else {
|
||||
p += snprintf(p, bufsize(p,end), "\tairGround\tA");
|
||||
if (speedIASValid && a->speed_ias_valid.updated > a->fatsv_last_emitted) {
|
||||
p += snprintf(p, bufsize(p,end), "\tspeed_ias\t%d", a->speed_ias);
|
||||
a->fatsv_emitted_speed_ias = a->speed_ias;
|
||||
if (a->speed_ias_valid.source == SOURCE_TISB) {
|
||||
used_tisb = 1;
|
||||
}
|
||||
useful = 1;
|
||||
}
|
||||
|
||||
if (latlonValid && latlonAge < emittedAge) {
|
||||
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
|
||||
|
|
397
track.c
397
track.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// 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
|
||||
// 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 <inttypes.h>
|
||||
|
||||
/* #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, "CPR: decode failure for %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,
|
||||
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
|
||||
{
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
|
|
146
track.h
146
track.h
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// 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
|
||||
// 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
|
||||
int fatsv_emitted_altitude_gnss; // -"- GNSS altitude
|
||||
int fatsv_emitted_heading; // -"- true track
|
||||
int fatsv_emitted_heading_magnetic; // -"- magnetic heading
|
||||
int fatsv_emitted_speed; // -"- groundspeed
|
||||
int fatsv_emitted_speed_ias; // -"- IAS
|
||||
int fatsv_emitted_speed_tas; // -"- TAS
|
||||
airground_t fatsv_emitted_airground; // -"- air/ground state
|
||||
|
||||
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted
|
||||
|
||||
// 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 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.
|
||||
|
|
Loading…
Reference in a new issue