Merge remote-tracking branch 'upstream/master' into dev

This commit is contained in:
Oliver Jowett 2016-09-14 17:35:25 +01:00
commit b260e1341e
9 changed files with 2063 additions and 1100 deletions

View file

@ -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
{

View file

@ -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;

View file

@ -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,70 @@ 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;
/* What sort of address is this and who sent it?
* (Earlier values are higher priority)
*/
typedef enum {
ADDR_ADSB_ICAO, /* Mode S or ADS-B, ICAO address, transponder sourced */
ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */
ADDR_ADSR_ICAO, /* ADS-R, ICAO address */
ADDR_TISB_ICAO, /* TIS-B, ICAO address */
ADDR_ADSB_OTHER, /* ADS-B, other address format, non-transponder */
ADDR_ADSR_OTHER, /* ADS-R, other address format */
ADDR_TISB_OTHER, /* TIS-B, other address format */
ADDR_TISB_ANON, /* ADS-R/TIS-B, anonymized address */
ADDR_UNKNOWN /* unknown address format */
} addrtype_t;
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;
typedef enum {
SIL_PER_SAMPLE, SIL_PER_HOUR
} sil_type_t;
#define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses
#define MODES_DEBUG_DEMOD (1<<0)
@ -310,7 +340,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.
@ -354,48 +384,154 @@ struct modesMessage {
uint32_t crc; // Message CRC
int correctedbits; // No. of bits corrected
uint32_t addr; // Address Announced
addrtype_t addrtype; // address format / source
uint64_t timestampMsg; // Timestamp of the message (12MHz clock)
struct timespec sysTimestampMsg; // Timestamp of the message (system time)
int remote; // If set this message is from a remote station
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).
// valid if cpr_decoded:
double decoded_lat;
double decoded_lon;
// DF20, DF21
int bds; // BDS value implied if overlay control was used
// Operational Status
struct {
unsigned valid : 1;
unsigned version : 3;
// Fields used by multiple message types.
int altitude;
int unit;
int bFlags; // Flags related to fields in this structure
unsigned om_acas_ra : 1;
unsigned om_ident : 1;
unsigned om_atc : 1;
unsigned om_saf : 1;
unsigned om_sda : 2;
unsigned cc_acas : 1;
unsigned cc_cdti : 1;
unsigned cc_1090_in : 1;
unsigned cc_arv : 1;
unsigned cc_ts : 1;
unsigned cc_tc : 2;
unsigned cc_uat_in : 1;
unsigned cc_poa : 1;
unsigned cc_b2_low : 1;
unsigned cc_nac_v : 3;
unsigned cc_nic_supp_c : 1;
unsigned cc_lw_valid : 1;
unsigned nic_supp_a : 1;
unsigned nac_p : 4;
unsigned gva : 2;
unsigned sil : 2;
unsigned nic_baro : 1;
sil_type_t sil_type;
enum { ANGLE_HEADING, ANGLE_TRACK } track_angle;
heading_source_t hrd;
unsigned cc_lw;
unsigned cc_antenna_offset;
} opstatus;
// Target State & Status (ADS-B V2 only)
struct {
unsigned valid : 1;
unsigned altitude_valid : 1;
unsigned baro_valid : 1;
unsigned heading_valid : 1;
unsigned mode_valid : 1;
unsigned mode_autopilot : 1;
unsigned mode_vnav : 1;
unsigned mode_alt_hold : 1;
unsigned mode_approach : 1;
unsigned acas_operational : 1;
unsigned nac_p : 4;
unsigned nic_baro : 1;
unsigned sil : 2;
sil_type_t sil_type;
enum { TSS_ALTITUDE_MCP, TSS_ALTITUDE_FMS } altitude_type;
unsigned altitude;
float baro;
unsigned heading;
} tss;
};
// This one needs modesMessage:

View file

@ -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++;

View file

@ -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;
}
}

1679
mode_s.c

File diff suppressed because it is too large Load diff

603
net_io.c
View file

@ -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
@ -77,6 +77,8 @@ static void send_raw_heartbeat(struct net_service *service);
static void send_beast_heartbeat(struct net_service *service);
static void send_sbs_heartbeat(struct net_service *service);
static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a);
//
//=========================================================================
//
@ -493,7 +495,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 +516,48 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
//
// Decide on the basic SBS Message Type
if ((mm->msgtype == 4) || (mm->msgtype == 20)) {
switch (mm->msgtype) {
case 4:
case 20:
msgType = 5;
} else if ((mm->msgtype == 5) || (mm->msgtype == 21)) {
break;
break;
case 5:
case 21:
msgType = 6;
} else if ((mm->msgtype == 0) || (mm->msgtype == 16)) {
break;
case 0:
case 16:
msgType = 7;
} else if (mm->msgtype == 11) {
break;
case 11:
msgType = 8;
} else if ((mm->msgtype != 17) && (mm->msgtype != 18)) {
return;
} else if ((mm->metype >= 1) && (mm->metype <= 4)) {
msgType = 1;
} else if ((mm->metype >= 5) && (mm->metype <= 8)) {
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
{msgType = 2;}
else
{msgType = 7;}
} else if ((mm->metype >= 9) && (mm->metype <= 18)) {
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
{msgType = 3;}
else
{msgType = 7;}
} else if (mm->metype != 19) {
return;
} else if ((mm->mesub == 1) || (mm->mesub == 2)) {
msgType = 4;
} else {
break;
case 17:
case 18:
if (mm->metype >= 1 && mm->metype <= 4) {
msgType = 1;
} else if (mm->metype >= 5 && mm->metype <= 8) {
msgType = 2;
} else if (mm->metype >= 9 && mm->metype <= 18) {
msgType = 3;
} else if (mm->metype == 19) {
msgType = 4;
} else {
return;
}
break;
default:
return;
}
// Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff
p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr);
p += sprintf(p, "MSG,%d,1,1,%06X,1,", msgType, mm->addr);
// Find current system time
clock_gettime(CLOCK_REALTIME, &now);
@ -564,53 +575,70 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
p += sprintf(p, "%02d:%02d:%02d.%03u", stTime_now.tm_hour, stTime_now.tm_min, stTime_now.tm_sec, (unsigned) (now.tv_nsec / 1000000U));
// Field 11 is the callsign (if we have it)
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);}
else {p += sprintf(p, ",");}
if (mm->callsign_valid) {p += sprintf(p, ",%s", mm->callsign);}
else {p += sprintf(p, ",");}
// Field 12 is the altitude (if we have it) - force to zero if we're on the ground
if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) {
p += sprintf(p, ",0");
} else if (Modes.use_hae && (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) {
p += sprintf(p, ",%dH", mm->altitude_hae);
} else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) {
p += sprintf(p, ",%dH", mm->altitude + a->hae_delta);
// Field 12 is the altitude (if we have it)
if (mm->altitude_valid) {
if (Modes.use_gnss) {
if (mm->altitude_source == ALTITUDE_GNSS) {
p += sprintf(p, ",%dH", mm->altitude);
} else if (trackDataValid(&a->gnss_delta_valid)) {
p += sprintf(p, ",%dH", mm->altitude + a->gnss_delta);
} else {
p += sprintf(p, ",%d", mm->altitude);
}
} else {
p += sprintf(p, ",%d", mm->altitude);
if (mm->altitude_source == ALTITUDE_BARO) {
p += sprintf(p, ",%d", mm->altitude);
} else if (trackDataValid(&a->gnss_delta_valid)) {
p += sprintf(p, ",%d", mm->altitude - a->gnss_delta);
} else {
p += sprintf(p, ",,");
}
}
} else {
p += sprintf(p, ",");
}
// Field 13 is the ground Speed (if we have it)
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
p += sprintf(p, ",%d", mm->velocity);
if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED) {
p += sprintf(p, ",%d", mm->speed);
} else {
p += sprintf(p, ",");
}
// Field 14 is the ground Heading (if we have it)
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
if (mm->heading_valid && mm->heading_source == HEADING_TRUE) {
p += sprintf(p, ",%d", mm->heading);
} else {
p += sprintf(p, ",");
}
// Fields 15 and 16 are the Lat/Lon (if we have it)
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);}
else {p += sprintf(p, ",,");}
if (mm->cpr_decoded) {
p += sprintf(p, ",%1.5f,%1.5f", mm->decoded_lat, mm->decoded_lon);
} else {
p += sprintf(p, ",,");
}
// Field 17 is the VerticalRate (if we have it)
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);}
else {p += sprintf(p, ",");}
if (mm->vert_rate_valid) {
p += sprintf(p, ",%d", mm->vert_rate);
} else {
p += sprintf(p, ",");
}
// Field 18 is the Squawk (if we have it)
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);}
else {p += sprintf(p, ",");}
if (mm->squawk_valid) {
p += sprintf(p, ",%04x", mm->squawk);
} else {
p += sprintf(p, ",");
}
// Field 19 is the Squawk Changing Alert flag (if we have it)
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
if ((mm->fs >= 2) && (mm->fs <= 4)) {
if (mm->alert_valid) {
if (mm->alert) {
p += sprintf(p, ",-1");
} else {
p += sprintf(p, ",0");
@ -620,8 +648,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 +659,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 +670,16 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
}
// Field 22 is the OnTheGround flag (if we have it)
if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) {
if (mm->bFlags & MODES_ACFLAGS_AOG) {
p += sprintf(p, ",-1");
} else {
p += sprintf(p, ",0");
}
} else {
switch (mm->airground) {
case AG_GROUND:
p += sprintf(p, ",-1");
break;
case AG_AIRBORNE:
p += sprintf(p, ",0");
break;
default:
p += sprintf(p, ",");
break;
}
p += sprintf(p, "\r\n");
@ -678,7 +708,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.
@ -697,6 +727,10 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) {
// Forward mlat messages via beast output only if --forward-mlat is set
modesSendBeastOutput(mm);
}
if (!is_mlat) {
writeFATSVEvent(mm, a);
}
}
//
//=========================================================================
@ -928,24 +962,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;
@ -953,6 +987,29 @@ static char *append_flags(char *p, char *end, int flags)
return p;
}
static const char *addrtype_short_string(addrtype_t type) {
switch (type) {
case ADDR_ADSB_ICAO:
return "adsb_icao";
case ADDR_ADSB_ICAO_NT:
return "adsb_icao_nt";
case ADDR_ADSB_OTHER:
return "adsb_other";
case ADDR_TISB_ICAO:
return "tisb_icao";
case ADDR_TISB_OTHER:
return "tisb_other";
case ADDR_TISB_ANON:
return "tisb_anon";
case ADDR_ADSR_ICAO:
return "adsr_icao";
case ADDR_ADSR_OTHER:
return "adsr_other";
default:
return "unknown";
}
}
char *generateAircraftJson(const char *url_path, int *len) {
uint64_t now = mstime();
struct aircraft *a;
@ -984,32 +1041,31 @@ 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 (a->addrtype != ADDR_ADSB_ICAO)
p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_short_string(a->addrtype));
if (trackDataValid(&a->squawk_valid))
p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk);
if (trackDataValid(&a->callsign_valid))
p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign));
if (trackDataValid(&a->position_valid))
p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->position_valid.updated)/1000.0);
if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND)
p += snprintf(p, end-p, ",\"altitude\":\"ground\"");
else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID)
else if (trackDataValid(&a->altitude_valid))
p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude);
if (a->bFlags & MODES_ACFLAGS_VERTRATE_VALID)
if (trackDataValid(&a->vert_rate_valid))
p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate);
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID)
p += snprintf(p, end-p, ",\"track\":%d", a->track);
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID)
if (trackDataValid(&a->heading_valid))
p += snprintf(p, end-p, ",\"track\":%d", a->heading);
if (trackDataValid(&a->speed_valid))
p += snprintf(p, end-p, ",\"speed\":%d", a->speed);
if (a->bFlags & MODES_ACFLAGS_CATEGORY_VALID)
if (trackDataValid(&a->category_valid))
p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category);
if (a->mlatFlags) {
p += snprintf(p, end-p, ",\"mlat\":");
p = append_flags(p, end, a->mlatFlags);
}
if (a->tisbFlags) {
p += snprintf(p, end-p, ",\"tisb\":");
p = append_flags(p, end, a->tisbFlags);
}
p += snprintf(p, end-p, ",\"mlat\":");
p = append_flags(p, end, a, SOURCE_MLAT);
p += snprintf(p, end-p, ",\"tisb\":");
p = append_flags(p, end, a, SOURCE_TISB);
p += snprintf(p, end-p, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}",
a->messages, (now - a->seen)/1000.0,
@ -1471,11 +1527,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 +1593,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;
}
@ -1625,6 +1683,92 @@ static void modesReadFromClient(struct client *c) {
#define TSV_MAX_PACKET_SIZE 180
static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafield, unsigned char *data, size_t len)
{
char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE);
if (!p)
return;
char *end = p + TSV_MAX_PACKET_SIZE;
# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p)))
p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, mstime() / 1000);
if (mm->addr & MODES_NON_ICAO_ADDRESS) {
p += snprintf(p, bufsize(p, end), "\totherid\t%06X", mm->addr & 0xFFFFFF);
} else {
p += snprintf(p, bufsize(p, end), "\thexid\t%06X", mm->addr);
}
if (mm->addrtype != ADDR_ADSB_ICAO) {
p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(mm->addrtype));
}
p += snprintf(p, bufsize(p, end), "\t%s\t", datafield);
for (size_t i = 0; i < len; ++i) {
p += snprintf(p, bufsize(p, end), "%02X", data[i]);
}
p += snprintf(p, bufsize(p, end), "\n");
if (p <= end)
completeWrite(&Modes.fatsv_out, p);
else
fprintf(stderr, "fatsv: output too large (max %d, overran by %d)\n", TSV_MAX_PACKET_SIZE, (int) (p - end));
# undef bufsize
}
static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a)
{
// Write event records for a couple of message types.
if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) {
return; // not enabled or no active connections
}
if (a->messages < 2) // basic filter for bad decodes
return;
switch (mm->msgtype) {
case 20:
case 21:
if (mm->correctedbits > 0)
break; // only messages we trust a little more
// DF 20/21: Comm-B: emit if they've changed since we last sent them
//
// BDS 1,0: data link capability report
// BDS 3,0: ACAS RA report
if (mm->MB[0] == 0x10 && memcmp(mm->MB, a->fatsv_emitted_bds_10, 7) != 0) {
memcpy(a->fatsv_emitted_bds_10, mm->MB, 7);
writeFATSVEventMessage(mm, "datalink_caps", mm->MB, 7);
}
else if (mm->MB[0] == 0x30 && memcmp(mm->MB, a->fatsv_emitted_bds_30, 7) != 0) {
memcpy(a->fatsv_emitted_bds_30, mm->MB, 7);
writeFATSVEventMessage(mm, "commb_acas_ra", mm->MB, 7);
}
break;
case 17:
// DF 17: extended squitter
// type 28 subtype 2: ACAS RA report
// first byte has the type/subtype, remaining bytes match the BDS 3,0 format
if (mm->metype == 28 && mm->mesub == 2 && memcmp(&mm->ME[1], &a->fatsv_emitted_bds_30[1], 6) != 0) {
memcpy(a->fatsv_emitted_bds_30, &mm->ME[1], 6);
writeFATSVEventMessage(mm, "es_acas_ra", mm->ME, 7);
} else if (mm->metype == 31 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_status, 7) != 0) {
memcpy(a->fatsv_emitted_es_status, mm->ME, 7);
writeFATSVEventMessage(mm, "es_op_status", mm->ME, 7);
} else if (mm->metype == 29 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_target, 7) != 0) {
memcpy(a->fatsv_emitted_es_target, mm->ME, 7);
writeFATSVEventMessage(mm, "es_target", mm->ME, 7);
}
break;
}
}
static void writeFATSV()
{
struct aircraft *a;
@ -1645,35 +1789,24 @@ 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
if (a->addr & MODES_NON_ICAO_ADDRESS)
continue;
if (a->messages < 2) // basic filter for bad decodes
continue;
@ -1682,131 +1815,200 @@ 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 < 200)) ||
(speedValid && a->speed < 100 && (!altValid || a->altitude < 1000))) {
// we are probably on the ground, increase the update rate
minAge = 1000;
} else if (!altValid || a->altitude < 10000) {
// 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;
end = p + TSV_MAX_PACKET_SIZE;
# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p)))
p += snprintf(p, bufsize(p,end), "clock\t%ld\thexid\t%06X", (long)(a->seen / 1000), a->addr);
if (*a->flight != '\0') {
p += snprintf(p, bufsize(p,end), "\tident\t%s", a->flight);
p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, (uint64_t)(a->seen / 1000));
if (a->addr & MODES_NON_ICAO_ADDRESS) {
p += snprintf(p, bufsize(p, end), "\totherid\t%06X", a->addr & 0xFFFFFF);
} else {
p += snprintf(p, bufsize(p, end), "\thexid\t%06X", a->addr);
}
if (flags & MODES_ACFLAGS_SQUAWK_VALID) {
p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->modeA);
if (a->addrtype != ADDR_ADSB_ICAO) {
p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(a->addrtype));
}
if (trackDataValidEx(&a->callsign_valid, now, 120000, SOURCE_MODE_S_CHECKED)) { // we accept quite old idents as they shouldn't change often
p += snprintf(p, bufsize(p,end), "\tident\t%s", a->callsign);
switch (a->callsign_valid.source) {
case SOURCE_MODE_S:
p += snprintf(p, bufsize(p,end), "\tiSource\tmodes");
break;
case SOURCE_ADSB:
p += snprintf(p, bufsize(p,end), "\tiSource\tadsb");
break;
case SOURCE_TISB:
p += snprintf(p, bufsize(p,end), "\tiSource\ttisb");
break;
default:
p += snprintf(p, bufsize(p,end), "\tiSource\tunknown");
break;
}
if (a->callsign_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
}
if (trackDataValidEx(&a->squawk_valid, now, 120000, SOURCE_MODE_S)) { // we accept quite old squawks as they shouldn't change often
p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->squawk);
if (a->squawk_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
}
// only emit alt, speed, latlon, track if they have been received since the last time
// and are not stale
if (altValid && altAge < emittedAge) {
p += snprintf(p, bufsize(p,end), "\talt\t%d", alt);
useful = 1;
}
if (speedValid && speedAge < emittedAge) {
p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed);
useful = 1;
}
if (groundValid) {
if (ground) {
p += snprintf(p, bufsize(p,end), "\tairGround\tG");
} else {
p += snprintf(p, bufsize(p,end), "\tairGround\tA");
if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude);
a->fatsv_emitted_altitude = a->altitude;
if (a->altitude_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (latlonValid && latlonAge < emittedAge) {
if (altGNSSValid && a->altitude_gnss_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\talt_gnss\t%d", a->altitude_gnss);
a->fatsv_emitted_altitude_gnss = a->altitude_gnss;
if (a->altitude_gnss_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (speedValid && a->speed_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed);
a->fatsv_emitted_speed = a->speed;
if (a->speed_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (speedIASValid && a->speed_ias_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tspeed_ias\t%d", a->speed_ias);
a->fatsv_emitted_speed_ias = a->speed_ias;
if (a->speed_ias_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (speedTASValid && a->speed_tas_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tspeed_tas\t%d", a->speed_tas);
a->fatsv_emitted_speed_tas = a->speed_tas;
if (a->speed_tas_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (positionValid && a->position_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon);
if (a->position_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (trackValid && trackAge < emittedAge) {
p += snprintf(p, bufsize(p,end), "\theading\t%d", a->track);
if (headingValid && a->heading_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\theading\t%d", a->heading);
a->fatsv_emitted_heading = a->heading;
if (a->heading_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (used_tisb) {
p += snprintf(p, bufsize(p,end), "\ttisb\t1");
if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\theading_magnetic\t%d", a->heading);
a->fatsv_emitted_heading_magnetic = a->heading_magnetic;
if (a->heading_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE)) {
p += snprintf(p, bufsize(p,end), "\tairGround\t%s", a->airground == AG_GROUND ? "G+" : "A+");
a->fatsv_emitted_airground = a->airground;
if (a->airground_valid.source == SOURCE_TISB) {
used_tisb = 1;
}
useful = 1;
}
// if we didn't get at least an alt or a speed or a latlon or
@ -1816,6 +2018,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 +2031,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 +2043,7 @@ void modesNetPeriodicWork(void) {
uint64_t now = mstime();
int need_flush = 0;
// Accept new connetions
// Accept new connections
modesAcceptClients();
// Read from clients

413
track.c
View file

@ -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 */
@ -65,16 +66,20 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
// Now initialise things that should not be 0/NULL to their defaults
a->addr = mm->addr;
a->addrtype = mm->addrtype;
for (i = 0; i < 8; ++i)
a->signalLevel[i] = 1e-5;
a->signalNext = 0;
// start off with the "last emitted" ACAS RA being blank (just the BDS 3,0 code)
a->fatsv_emitted_bds_30[0] = 0x30;
// mm->msgtype 32 is used to represent Mode A/C. These values can never change, so
// set them once here during initialisation, and don't bother to set them every
// time this ModeA/C is received again in the future
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 +108,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 +199,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 +207,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 +257,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)) { // Ok to try aircraft relative first
reflat = a->lat;
reflon = a->lon;
if (a->pos_nuc < *nuc)
@ -231,27 +281,25 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now
}
result = decodeCPRsurface(reflat, reflon,
a->even_cprlat, a->even_cprlon,
a->odd_cprlat, a->odd_cprlon,
a->cpr_even_lat, a->cpr_even_lon,
a->cpr_odd_lat, a->cpr_odd_lon,
fflag,
lat, lon);
} else {
// airborne global CPR
result = decodeCPRairborne(a->even_cprlat, a->even_cprlon,
a->odd_cprlat, a->odd_cprlon,
result = decodeCPRairborne(a->cpr_even_lat, a->cpr_even_lon,
a->cpr_odd_lat, a->cpr_odd_lon,
fflag,
lat, lon);
}
if (result < 0) {
#ifdef DEBUG_CPR_CHECKS
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) {
fprintf(stderr, "CPR: decode failure from MLAT (%06X) (%d).\n", a->addr, result);
fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n",
a->even_cprlat, a->even_cprlon,
a->odd_cprlat, a->odd_cprlon,
fflag ? "odd" : "even");
}
fprintf(stderr, "CPR: decode failure for %06X (%d).\n", a->addr, result);
fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n",
a->cpr_even_lat, a->cpr_even_lon,
a->cpr_odd_lat, a->cpr_odd_lon,
fflag ? "odd" : "even");
#endif
return result;
}
@ -271,11 +319,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 +338,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 +379,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 +397,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 +408,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 +472,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 +480,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 +535,104 @@ 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);
}
// update addrtype, we only ever go towards "more direct" types
if (mm->addrtype < a->addrtype)
a->addrtype = mm->addrtype;
// If we've got a new cprlat or cprlon
if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) {
updatePosition(a, mm, now);
}
// If a (new) CALLSIGN has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {
memcpy(a->flight, mm->flight, sizeof(a->flight));
}
// If a (new) ALTITUDE has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
if ( (a->modeCcount) // if we've a modeCcount already
&& (a->altitude != mm->altitude ) ) // and Altitude has changed
// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet
// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet
{
a->modeCcount = 0; //....zero the hit count
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) {
unsigned modeC = (a->altitude + 49) / 100;
if (modeC != a->altitude_modeC) {
a->modeCcount = 0; //....zero the hit count
a->modeACflags &= ~MODEAC_MSG_MODEC_HIT;
}
// If we received an altitude in a (non-mlat) DF17/18 squitter recently, ignore
// DF0/4/16/20 altitudes as single-bit errors can attribute them to the wrong
// aircraft
if ((a->bFlags & ~a->mlatFlags & MODES_ACFLAGS_ALTITUDE_VALID) &&
(now - a->seenAltitude) < 15000 &&
(a->bFlags & ~a->mlatFlags & MODES_ACFLAGS_LATLON_VALID) &&
(now - a->seenLatLon) < 15000 &&
mm->msgtype != 17 &&
mm->msgtype != 18) {
Modes.stats_current.suppressed_altitude_messages++;
} else {
a->altitude = mm->altitude;
a->modeC = (mm->altitude + 49) / 100;
a->seenAltitude = now;
// reporting of HAE and baro altitudes is mutually exclusive
// so if we see a baro altitude, assume the HAE altitude is invalid
// we will recalculate it from baro + HAE delta below, where possible
a->bFlags &= ~MODES_ACFLAGS_ALTITUDE_HAE_VALID;
}
a->altitude = mm->altitude;
a->altitude_modeC = modeC;
}
// If a (new) HAE altitude has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID) {
a->altitude_hae = mm->altitude_hae;
// reporting of HAE and baro altitudes is mutually exclusive
// if you have both, you're meant to report baro and a HAE delta,
// so if we see explicit HAE then assume the delta is invalid too
a->bFlags &= ~(MODES_ACFLAGS_ALTITUDE_VALID | MODES_ACFLAGS_HAE_DELTA_VALID);
}
// If a (new) HAE/barometric difference has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID) {
a->hae_delta = mm->hae_delta;
// reporting of HAE and baro altitudes is mutually exclusive
// if you have both, you're meant to report baro and a HAE delta,
// so if we see a HAE delta then assume the HAE altitude is invalid
a->bFlags &= ~MODES_ACFLAGS_ALTITUDE_HAE_VALID;
}
// If a (new) SQUAWK has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
if (a->modeA != mm->modeA) {
a->modeAcount = 0; // Squawk has changed, so zero the hit count
if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source, now)) {
if (mm->squawk != a->squawk) {
a->modeAcount = 0; //....zero the hit count
a->modeACflags &= ~MODEAC_MSG_MODEA_HIT;
}
a->modeA = mm->modeA;
a->squawk = mm->squawk;
}
// If a (new) HEADING has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
a->track = mm->heading;
a->seenTrack = now;
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GNSS && accept_data(&a->altitude_gnss_valid, mm->source, now)) {
a->altitude_gnss = mm->altitude;
}
// If a (new) SPEED has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
a->speed = mm->velocity;
a->seenSpeed = now;
if (mm->gnss_delta_valid && accept_data(&a->gnss_delta_valid, mm->source, now)) {
a->gnss_delta = mm->gnss_delta;
}
// If a (new) Vertical Descent rate has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {
if (mm->heading_valid && mm->heading_source == HEADING_TRUE && accept_data(&a->heading_valid, mm->source, now)) {
a->heading = mm->heading;
}
if (mm->heading_valid && mm->heading_source == HEADING_MAGNETIC && accept_data(&a->heading_magnetic_valid, mm->source, now)) {
a->heading_magnetic = mm->heading;
}
if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED && accept_data(&a->speed_valid, mm->source, now)) {
a->speed = mm->speed;
}
if (mm->speed_valid && mm->speed_source == SPEED_IAS && accept_data(&a->speed_ias_valid, mm->source, now)) {
a->speed_ias = mm->speed;
}
if (mm->speed_valid && mm->speed_source == SPEED_TAS && accept_data(&a->speed_tas_valid, mm->source, now)) {
a->speed_tas = mm->speed;
}
if (mm->vert_rate_valid && accept_data(&a->vert_rate_valid, mm->source, now)) {
a->vert_rate = mm->vert_rate;
a->vert_rate_source = mm->vert_rate_source;
}
// If a (new) category has been received, copy it to the aircraft structure
if (mm->bFlags & MODES_ACFLAGS_CATEGORY_VALID) {
if (mm->category_valid && accept_data(&a->category_valid, mm->source, now)) {
a->category = mm->category;
}
// Update the aircrafts a->bFlags to reflect the newly received mm->bFlags;
a->bFlags |= mm->bFlags;
// If we have a baro altitude and a HAE delta from baro, calculate the HAE altitude
if ((a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) {
a->altitude_hae = a->altitude + a->hae_delta;
a->bFlags |= MODES_ACFLAGS_ALTITUDE_HAE_VALID;
if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source, now)) {
a->airground = mm->airground;
}
// Update mlat flags. The mlat flags indicate which bits in bFlags
// were last set based on a mlat-derived message.
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT)
a->mlatFlags = (a->mlatFlags & a->bFlags) | mm->bFlags;
else
a->mlatFlags = (a->mlatFlags & a->bFlags) & ~mm->bFlags;
if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source, now)) {
memcpy(a->callsign, mm->callsign, sizeof(a->callsign));
}
// Same for TIS-B
if (mm->bFlags & MODES_ACFLAGS_FROM_TISB)
a->tisbFlags = (a->tisbFlags & a->bFlags) | mm->bFlags;
else
a->tisbFlags = (a->tisbFlags & a->bFlags) & ~mm->bFlags;
// CPR, even
if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) {
a->cpr_even_airground = mm->airground;
a->cpr_even_lat = mm->cpr_lat;
a->cpr_even_lon = mm->cpr_lon;
a->cpr_even_nuc = mm->cpr_nucp;
}
// CPR, odd
if (mm->cpr_valid && mm->cpr_odd && accept_data(&a->cpr_odd_valid, mm->source, now)) {
a->cpr_odd_airground = mm->airground;
a->cpr_odd_lat = mm->cpr_lat;
a->cpr_odd_lon = mm->cpr_lon;
a->cpr_odd_nuc = mm->cpr_nucp;
}
// Now handle derived data
// derive GNSS if we have baro + delta
if (compare_validity(&a->altitude_valid, &a->altitude_gnss_valid, now) > 0 &&
compare_validity(&a->gnss_delta_valid, &a->altitude_gnss_valid, now) > 0) {
// Baro and delta are both more recent than GNSS, derive GNSS from baro + delta
a->altitude_gnss = a->altitude + a->gnss_delta;
combine_validity(&a->altitude_gnss_valid, &a->altitude_valid, &a->gnss_delta_valid);
}
// If we've got a new cprlat or cprlon
if (mm->cpr_valid) {
updatePosition(a, mm, now);
}
if (mm->msgtype == 32) {
int flags = a->modeACflags;
@ -670,9 +690,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 +704,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 +768,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;
}
}

153
track.h
View file

@ -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,136 @@
/* 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.
addrtype_t addrtype; // highest priority address type seen for this aircraft
uint64_t seen; // Time (millis) at which the last packet was received
uint64_t seenLatLon; // Time (millis) at which lat, lon was measured
uint64_t seenAltitude; // Time (millis) at which altitude was measured
uint64_t seenSpeed; // Time (millis) at which speed was measured
uint64_t seenTrack; // Time (millis) at which track was measured
int mlatFlags; // Data derived from mlat messages
int tisbFlags; // Data derived from TIS-B messages
long messages; // Number of Mode S messages received
int modeA; // Squawk
int modeC; // Altitude
double signalLevel[8]; // Last 8 Signal Amplitudes
int signalNext; // next index of signalLevel to use
data_validity callsign_valid;
char callsign[9]; // Flight number
data_validity altitude_valid;
int altitude; // Altitude (Baro)
unsigned altitude_modeC; // (as a Mode C value)
data_validity altitude_gnss_valid;
int altitude_gnss; // Altitude (GNSS)
data_validity gnss_delta_valid;
int gnss_delta; // Difference between GNSS and Baro altitudes
data_validity speed_valid;
unsigned speed;
data_validity speed_ias_valid;
unsigned speed_ias;
data_validity speed_tas_valid;
unsigned speed_tas;
data_validity heading_valid;
unsigned heading; // Heading (OK it's really the track)
data_validity heading_magnetic_valid;
unsigned heading_magnetic; // Heading
data_validity vert_rate_valid;
int vert_rate; // Vertical rate
altitude_source_t vert_rate_source;
data_validity squawk_valid;
unsigned squawk; // Squawk
data_validity category_valid;
unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte
data_validity airground_valid;
airground_t airground; // air/ground status
data_validity cpr_odd_valid; // Last seen even CPR message
airground_t cpr_odd_airground;
unsigned cpr_odd_lat;
unsigned cpr_odd_lon;
unsigned cpr_odd_nuc;
data_validity cpr_even_valid; // Last seen odd CPR message
airground_t cpr_even_airground;
unsigned cpr_even_lat;
unsigned cpr_even_lon;
unsigned cpr_even_nuc;
data_validity position_valid;
double lat, lon; // Coordinated obtained from CPR encoded data
unsigned pos_nuc; // NUCp of last computed position
long modeAcount; // Mode A Squawk hit Count
long modeCcount; // Mode C Altitude hit Count
int modeACflags; // Flags for mode A/C recognition
int fatsv_emitted_altitude; // last FA emitted altitude
int fatsv_emitted_track; // last FA emitted track
int fatsv_emitted_speed; // last FA emitted speed
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted
int fatsv_emitted_altitude; // last FA emitted altitude
int fatsv_emitted_altitude_gnss; // -"- GNSS altitude
int fatsv_emitted_heading; // -"- true track
int fatsv_emitted_heading_magnetic; // -"- magnetic heading
int fatsv_emitted_speed; // -"- groundspeed
int fatsv_emitted_speed_ias; // -"- IAS
int fatsv_emitted_speed_tas; // -"- TAS
airground_t fatsv_emitted_airground; // -"- air/ground state
unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message
unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message
unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message
unsigned char fatsv_emitted_es_target[7]; // -"- ES target status message
// Encoded latitude and longitude as extracted by odd and even CPR encoded messages
uint64_t odd_cprtime;
int odd_cprlat;
int odd_cprlon;
unsigned odd_cprnuc;
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted
uint64_t even_cprtime;
int even_cprlat;
int even_cprlon;
unsigned even_cprnuc;
double lat, lon; // Coordinated obtained from CPR encoded data
unsigned pos_nuc; // NUCp of last computed position
unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte
int bFlags; // Flags related to valid fields in this structure
struct aircraft *next; // Next aircraft in our linked list
struct modesMessage first_message; // A copy of the first message we received for this aircraft.
};
/* is this bit of data valid? */
static inline int trackDataValid(const data_validity *v)
{
return (v->source != SOURCE_INVALID);
}
/* .. with these constraints? */
static inline int trackDataValidEx(const data_validity *v,
uint64_t now,
uint64_t maxAge,
datasource_t minSource)
{
if (v->source == SOURCE_INVALID)
return 0;
if (v->source < minSource)
return 0;
if (v->updated < now && (now - v->updated) > maxAge)
return 0;
return 1;
}
/* what's the age of this data? */
static inline uint64_t trackDataAge(const data_validity *v,
uint64_t now)
{
if (v->source == SOURCE_INVALID)
return ~(uint64_t)0;
if (v->updated >= now)
return 0;
return (now - v->updated);
}
/* Update aircraft state from data in the provided mesage.
* Return the tracked aircraft.