Merge remote-tracking branch 'upstream/master' into dev
This commit is contained in:
commit
b260e1341e
|
@ -306,7 +306,6 @@ void demodulate2400(struct mag_buf *mag)
|
||||||
normalize_timespec(&mm.sysTimestampMsg);
|
normalize_timespec(&mm.sysTimestampMsg);
|
||||||
|
|
||||||
mm.score = bestscore;
|
mm.score = bestscore;
|
||||||
mm.bFlags = mm.correctedbits = 0;
|
|
||||||
|
|
||||||
// Decode the received message
|
// Decode the received message
|
||||||
{
|
{
|
||||||
|
|
|
@ -1019,7 +1019,7 @@ int main(int argc, char **argv) {
|
||||||
} else if (!strcmp(argv[j],"--metric")) {
|
} else if (!strcmp(argv[j],"--metric")) {
|
||||||
Modes.metric = 1;
|
Modes.metric = 1;
|
||||||
} else if (!strcmp(argv[j],"--hae") || !strcmp(argv[j],"--gnss")) {
|
} else if (!strcmp(argv[j],"--hae") || !strcmp(argv[j],"--gnss")) {
|
||||||
Modes.use_hae = 1;
|
Modes.use_gnss = 1;
|
||||||
} else if (!strcmp(argv[j],"--aggressive")) {
|
} else if (!strcmp(argv[j],"--aggressive")) {
|
||||||
#ifdef ALLOW_AGGRESSIVE
|
#ifdef ALLOW_AGGRESSIVE
|
||||||
Modes.nfix_crc = MODES_MAX_BITERRORS;
|
Modes.nfix_crc = MODES_MAX_BITERRORS;
|
||||||
|
|
260
dump1090.h
260
dump1090.h
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// dump1090.h: main program header
|
// dump1090.h: main program header
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk>
|
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
|
||||||
//
|
//
|
||||||
// This file is free software: you may copy, redistribute and/or modify it
|
// This file is free software: you may copy, redistribute and/or modify it
|
||||||
// under the terms of the GNU General Public License as published by the
|
// under the terms of the GNU General Public License as published by the
|
||||||
|
@ -135,40 +135,70 @@ typedef struct rtlsdr_dev rtlsdr_dev_t;
|
||||||
#define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256)
|
#define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256)
|
||||||
#define MODES_OUT_FLUSH_INTERVAL (60000)
|
#define MODES_OUT_FLUSH_INTERVAL (60000)
|
||||||
|
|
||||||
#define MODES_UNIT_FEET 0
|
|
||||||
#define MODES_UNIT_METERS 1
|
|
||||||
|
|
||||||
#define MODES_USER_LATLON_VALID (1<<0)
|
#define MODES_USER_LATLON_VALID (1<<0)
|
||||||
|
|
||||||
#define MODES_ACFLAGS_LATLON_VALID (1<<0) // Aircraft Lat/Lon is decoded
|
|
||||||
#define MODES_ACFLAGS_ALTITUDE_VALID (1<<1) // Aircraft altitude is known
|
|
||||||
#define MODES_ACFLAGS_HEADING_VALID (1<<2) // Aircraft heading is known
|
|
||||||
#define MODES_ACFLAGS_SPEED_VALID (1<<3) // Aircraft speed is known
|
|
||||||
#define MODES_ACFLAGS_VERTRATE_VALID (1<<4) // Aircraft vertical rate is known
|
|
||||||
#define MODES_ACFLAGS_SQUAWK_VALID (1<<5) // Aircraft Mode A Squawk is known
|
|
||||||
#define MODES_ACFLAGS_CALLSIGN_VALID (1<<6) // Aircraft Callsign Identity
|
|
||||||
#define MODES_ACFLAGS_EWSPEED_VALID (1<<7) // Aircraft East West Speed is known
|
|
||||||
#define MODES_ACFLAGS_NSSPEED_VALID (1<<8) // Aircraft North South Speed is known
|
|
||||||
#define MODES_ACFLAGS_AOG (1<<9) // Aircraft is On the Ground
|
|
||||||
#define MODES_ACFLAGS_LLEVEN_VALID (1<<10) // Aircraft Even Lot/Lon is known
|
|
||||||
#define MODES_ACFLAGS_LLODD_VALID (1<<11) // Aircraft Odd Lot/Lon is known
|
|
||||||
#define MODES_ACFLAGS_AOG_VALID (1<<12) // MODES_ACFLAGS_AOG is valid
|
|
||||||
#define MODES_ACFLAGS_FS_VALID (1<<13) // Aircraft Flight Status is known
|
|
||||||
#define MODES_ACFLAGS_NSEWSPD_VALID (1<<14) // Aircraft EW and NS Speed is known
|
|
||||||
#define MODES_ACFLAGS_LATLON_REL_OK (1<<15) // Indicates it's OK to do a relative CPR
|
|
||||||
#define MODES_ACFLAGS_REL_CPR_USED (1<<16) // Lat/lon derived from relative CPR
|
|
||||||
#define MODES_ACFLAGS_CATEGORY_VALID (1<<17) // Aircraft category is known
|
|
||||||
#define MODES_ACFLAGS_FROM_MLAT (1<<18) // Data was derived from multilateration
|
|
||||||
#define MODES_ACFLAGS_ALTITUDE_HAE_VALID (1<<19) // altitude_hae is valid
|
|
||||||
#define MODES_ACFLAGS_HAE_DELTA_VALID (1<<20) // hae_delta is valid
|
|
||||||
#define MODES_ACFLAGS_FROM_TISB (1<<21) // Data was derived from TIS-B messages
|
|
||||||
|
|
||||||
#define MODES_ACFLAGS_LLEITHER_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID)
|
|
||||||
#define MODES_ACFLAGS_LLBOTH_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID)
|
|
||||||
#define MODES_ACFLAGS_AOG_GROUND (MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG)
|
|
||||||
|
|
||||||
#define INVALID_ALTITUDE (-9999)
|
#define INVALID_ALTITUDE (-9999)
|
||||||
|
|
||||||
|
/* Where did a bit of data arrive from? In order of increasing priority */
|
||||||
|
typedef enum {
|
||||||
|
SOURCE_INVALID, /* data is not valid */
|
||||||
|
SOURCE_MLAT, /* derived from mlat */
|
||||||
|
SOURCE_MODE_S, /* data from a Mode S message, no full CRC */
|
||||||
|
SOURCE_MODE_S_CHECKED, /* data from a Mode S message with full CRC */
|
||||||
|
SOURCE_TISB, /* data from a TIS-B extended squitter message */
|
||||||
|
SOURCE_ADSB, /* data from a ADS-B extended squitter message */
|
||||||
|
} datasource_t;
|
||||||
|
|
||||||
|
/* 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_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses
|
||||||
|
|
||||||
#define MODES_DEBUG_DEMOD (1<<0)
|
#define MODES_DEBUG_DEMOD (1<<0)
|
||||||
|
@ -310,7 +340,7 @@ struct { // Internal state
|
||||||
int stats_range_histo; // Collect/show a range histogram?
|
int stats_range_histo; // Collect/show a range histogram?
|
||||||
int onlyaddr; // Print only ICAO addresses
|
int onlyaddr; // Print only ICAO addresses
|
||||||
int metric; // Use metric units
|
int metric; // Use metric units
|
||||||
int use_hae; // Use HAE altitudes with H suffix when available
|
int use_gnss; // Use GNSS altitudes with H suffix ("HAE", though it isn't always) when available
|
||||||
int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...;
|
int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...;
|
||||||
int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090
|
int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090
|
||||||
char *json_dir; // Path to json base directory, or NULL not to write json.
|
char *json_dir; // Path to json base directory, or NULL not to write json.
|
||||||
|
@ -354,48 +384,154 @@ struct modesMessage {
|
||||||
uint32_t crc; // Message CRC
|
uint32_t crc; // Message CRC
|
||||||
int correctedbits; // No. of bits corrected
|
int correctedbits; // No. of bits corrected
|
||||||
uint32_t addr; // Address Announced
|
uint32_t addr; // Address Announced
|
||||||
|
addrtype_t addrtype; // address format / source
|
||||||
uint64_t timestampMsg; // Timestamp of the message (12MHz clock)
|
uint64_t timestampMsg; // Timestamp of the message (12MHz clock)
|
||||||
struct timespec sysTimestampMsg; // Timestamp of the message (system time)
|
struct timespec sysTimestampMsg; // Timestamp of the message (system time)
|
||||||
int remote; // If set this message is from a remote station
|
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
|
double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power
|
||||||
int score; // Scoring from scoreModesMessage, if used
|
int score; // Scoring from scoreModesMessage, if used
|
||||||
|
|
||||||
// DF 11, DF 17
|
datasource_t source; // Characterizes the overall message source
|
||||||
int ca; // Responder capabilities
|
|
||||||
int iid;
|
|
||||||
|
|
||||||
// DF 17, DF 18
|
// Raw data, just extracted directly from the message
|
||||||
int metype; // Extended squitter message type.
|
// The names reflect the field names in Annex 4
|
||||||
int mesub; // Extended squitter message subtype.
|
unsigned IID; // extracted from CRC of DF11s
|
||||||
int heading; // Reported by aircraft, or computed from from EW and NS velocity
|
unsigned AA;
|
||||||
int raw_latitude; // Non decoded latitude.
|
unsigned AC;
|
||||||
int raw_longitude; // Non decoded longitude.
|
unsigned CA;
|
||||||
unsigned nuc_p; // NUCp value implied by message type
|
unsigned CC;
|
||||||
double fLat; // Coordinates obtained from CPR encoded data if/when decoded
|
unsigned CF;
|
||||||
double fLon; // Coordinates obtained from CPR encoded data if/when decoded
|
unsigned DR;
|
||||||
char flight[16]; // 8 chars flight number.
|
unsigned FS;
|
||||||
int ew_velocity; // E/W velocity.
|
unsigned ID;
|
||||||
int ns_velocity; // N/S velocity.
|
unsigned KE;
|
||||||
int vert_rate; // Vertical rate.
|
unsigned ND;
|
||||||
int velocity; // Reported by aircraft, or computed from from EW and NS velocity
|
unsigned RI;
|
||||||
|
unsigned SL;
|
||||||
|
unsigned UM;
|
||||||
|
unsigned VS;
|
||||||
|
unsigned char MB[7];
|
||||||
|
unsigned char MD[10];
|
||||||
|
unsigned char ME[7];
|
||||||
|
unsigned char MV[7];
|
||||||
|
|
||||||
|
// Decoded data
|
||||||
|
unsigned altitude_valid : 1;
|
||||||
|
unsigned heading_valid : 1;
|
||||||
|
unsigned speed_valid : 1;
|
||||||
|
unsigned vert_rate_valid : 1;
|
||||||
|
unsigned squawk_valid : 1;
|
||||||
|
unsigned callsign_valid : 1;
|
||||||
|
unsigned ew_velocity_valid : 1;
|
||||||
|
unsigned ns_velocity_valid : 1;
|
||||||
|
unsigned cpr_valid : 1;
|
||||||
|
unsigned cpr_odd : 1;
|
||||||
|
unsigned cpr_decoded : 1;
|
||||||
|
unsigned cpr_relative : 1;
|
||||||
|
unsigned category_valid : 1;
|
||||||
|
unsigned gnss_delta_valid : 1;
|
||||||
|
unsigned from_mlat : 1;
|
||||||
|
unsigned from_tisb : 1;
|
||||||
|
unsigned spi_valid : 1;
|
||||||
|
unsigned spi : 1;
|
||||||
|
unsigned alert_valid : 1;
|
||||||
|
unsigned alert : 1;
|
||||||
|
|
||||||
|
unsigned metype; // DF17/18 ME type
|
||||||
|
unsigned mesub; // DF17/18 ME subtype
|
||||||
|
|
||||||
|
// valid if altitude_valid:
|
||||||
|
int altitude; // Altitude in either feet or meters
|
||||||
|
altitude_unit_t altitude_unit; // the unit used for altitude
|
||||||
|
altitude_source_t altitude_source; // whether the altitude is a barometric altude or a GNSS height
|
||||||
|
// valid if gnss_delta_valid:
|
||||||
|
int gnss_delta; // difference between GNSS and baro alt
|
||||||
|
// valid if heading_valid:
|
||||||
|
unsigned heading; // Reported by aircraft, or computed from from EW and NS velocity
|
||||||
|
heading_source_t heading_source; // what "heading" is measuring (true or magnetic heading)
|
||||||
|
// valid if speed_valid:
|
||||||
|
unsigned speed; // in kts, reported by aircraft, or computed from from EW and NS velocity
|
||||||
|
speed_source_t speed_source; // what "speed" is measuring (groundspeed / IAS / TAS)
|
||||||
|
// valid if vert_rate_valid:
|
||||||
|
int vert_rate; // vertical rate in feet/minute
|
||||||
|
altitude_source_t vert_rate_source; // the altitude source used for vert_rate
|
||||||
|
// valid if squawk_valid:
|
||||||
|
unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits
|
||||||
|
// valid if callsign_valid
|
||||||
|
char callsign[9]; // 8 chars flight number
|
||||||
|
// valid if category_valid
|
||||||
unsigned category; // A0 - D7 encoded as a single hex byte
|
unsigned category; // A0 - D7 encoded as a single hex byte
|
||||||
int altitude_hae; // altitude reported as GNSS HAE
|
// valid if cpr_valid
|
||||||
int hae_delta; // difference between HAE and baro alt
|
unsigned cpr_lat; // Non decoded latitude.
|
||||||
|
unsigned cpr_lon; // Non decoded longitude.
|
||||||
|
unsigned cpr_nucp; // NUCp/NIC value implied by message type
|
||||||
|
|
||||||
// DF 18
|
airground_t airground; // air/ground state
|
||||||
int cf; // Control Field
|
|
||||||
|
|
||||||
// DF4, DF5, DF20, DF21
|
// valid if cpr_decoded:
|
||||||
int fs; // Flight status for DF4,5,20,21
|
double decoded_lat;
|
||||||
int modeA; // 13 bits identity (Squawk).
|
double decoded_lon;
|
||||||
|
|
||||||
// DF20, DF21
|
// Operational Status
|
||||||
int bds; // BDS value implied if overlay control was used
|
struct {
|
||||||
|
unsigned valid : 1;
|
||||||
|
unsigned version : 3;
|
||||||
|
|
||||||
// Fields used by multiple message types.
|
unsigned om_acas_ra : 1;
|
||||||
int altitude;
|
unsigned om_ident : 1;
|
||||||
int unit;
|
unsigned om_atc : 1;
|
||||||
int bFlags; // Flags related to fields in this structure
|
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:
|
// This one needs modesMessage:
|
||||||
|
|
|
@ -122,25 +122,28 @@ void interactiveShowData(void) {
|
||||||
char strTt[5] = " ";
|
char strTt[5] = " ";
|
||||||
char strGs[5] = " ";
|
char strGs[5] = " ";
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
if (trackDataValid(&a->squawk_valid)) {
|
||||||
snprintf(strSquawk,5,"%04x", a->modeA);}
|
snprintf(strSquawk,5,"%04x", a->squawk);
|
||||||
|
}
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
if (trackDataValid(&a->speed_valid)) {
|
||||||
snprintf (strGs, 5,"%3d", convert_speed(a->speed));}
|
snprintf (strGs, 5,"%3d", convert_speed(a->speed));
|
||||||
|
}
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
if (trackDataValid(&a->heading_valid)) {
|
||||||
snprintf (strTt, 5,"%03d", a->track);}
|
snprintf (strTt, 5,"%03d", a->heading);
|
||||||
|
}
|
||||||
|
|
||||||
if (msgs > 99999) {
|
if (msgs > 99999) {
|
||||||
msgs = 99999;}
|
msgs = 99999;
|
||||||
|
}
|
||||||
|
|
||||||
if (Modes.interactive_rtl1090) { // RTL1090 display mode
|
if (Modes.interactive_rtl1090) { // RTL1090 display mode
|
||||||
|
if (trackDataValid(&a->altitude_valid)) {
|
||||||
if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
snprintf(strFl,6,"F%03d",((a->altitude+50)/100));
|
||||||
snprintf(strFl,6,"F%03d",(a->altitude/100));
|
|
||||||
}
|
}
|
||||||
printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2.0f\n",
|
printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2.0f\n",
|
||||||
a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (now - a->seen)/1000.0);
|
a->addr, a->callsign, strFl, strGs, strTt, strSquawk, msgs, (now - a->seen)/1000.0);
|
||||||
|
|
||||||
} else { // Dump1090 display mode
|
} else { // Dump1090 display mode
|
||||||
char strMode[5] = " ";
|
char strMode[5] = " ";
|
||||||
|
@ -158,22 +161,22 @@ void interactiveShowData(void) {
|
||||||
if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
|
if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
|
||||||
if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';}
|
if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';}
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
|
if (trackDataValid(&a->position_valid)) {
|
||||||
snprintf(strLat, 8,"%7.03f", a->lat);
|
snprintf(strLat, 8,"%7.03f", a->lat);
|
||||||
snprintf(strLon, 9,"%8.03f", a->lon);
|
snprintf(strLon, 9,"%8.03f", a->lon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_AOG) {
|
if (trackDataValid(&a->airground_valid) && a->airground == AG_GROUND) {
|
||||||
snprintf(strFl, 7," grnd");
|
snprintf(strFl, 7," grnd");
|
||||||
} else if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) {
|
} else if (Modes.use_gnss && trackDataValid(&a->altitude_gnss_valid)) {
|
||||||
snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_hae));
|
snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_gnss));
|
||||||
} else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
} else if (trackDataValid(&a->altitude_valid)) {
|
||||||
snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude));
|
snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude));
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s%06X %-4s %-4s %-8s %6s %3s %3s %7s %8s %5.1f %5d %2.0f\n",
|
printf("%s%06X %-4s %-4s %-8s %6s %3s %3s %7s %8s %5.1f %5d %2.0f\n",
|
||||||
(a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : " ", (a->addr & 0xffffff),
|
(a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : " ", (a->addr & 0xffffff),
|
||||||
strMode, strSquawk, a->flight, strFl, strGs, strTt,
|
strMode, strSquawk, a->callsign, strFl, strGs, strTt,
|
||||||
strLat, strLon, 10 * log10(signalAverage), msgs, (now - a->seen)/1000.0);
|
strLat, strLon, 10 * log10(signalAverage), msgs, (now - a->seen)/1000.0);
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
|
|
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;
|
mm->addr = (ModeA & 0x0000FF7F) | MODES_NON_ICAO_ADDRESS;
|
||||||
|
|
||||||
// Set the Identity field to ModeA
|
// Set the Identity field to ModeA
|
||||||
mm->modeA = ModeA & 0x7777;
|
mm->squawk = ModeA & 0x7777;
|
||||||
mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
|
mm->squawk_valid = 1;
|
||||||
|
|
||||||
// Flag ident in flight status
|
// Flag ident in flight status
|
||||||
mm->fs = ModeA & 0x0080;
|
mm->spi = (ModeA & 0x0080) ? 1 : 0;
|
||||||
|
mm->spi_valid = 1;
|
||||||
|
|
||||||
// Decode an altitude if this looks like a possible mode C
|
// Decode an altitude if this looks like a possible mode C
|
||||||
if (!mm->fs) {
|
if (!mm->spi) {
|
||||||
int modeC = ModeAToModeC(ModeA);
|
int modeC = ModeAToModeC(ModeA);
|
||||||
if (modeC >= -12) {
|
if (modeC != INVALID_ALTITUDE) {
|
||||||
mm->altitude = modeC * 100;
|
mm->altitude = modeC * 100;
|
||||||
mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID;
|
mm->altitude_unit = UNIT_FEET;
|
||||||
|
mm->altitude_source = ALTITUDE_BARO;
|
||||||
|
mm->altitude_valid = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
571
net_io.c
571
net_io.c
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// net_io.c: network handling.
|
// net_io.c: network handling.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk>
|
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
|
||||||
//
|
//
|
||||||
// This file is free software: you may copy, redistribute and/or modify it
|
// This file is free software: you may copy, redistribute and/or modify it
|
||||||
// under the terms of the GNU General Public License as published by the
|
// under the terms of the GNU General Public License as published by the
|
||||||
|
@ -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_beast_heartbeat(struct net_service *service);
|
||||||
static void send_sbs_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
|
// Write SBS output to TCP clients
|
||||||
// The message structure mm->bFlags tells us what has been updated by this message
|
|
||||||
//
|
//
|
||||||
static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
char *p;
|
char *p;
|
||||||
|
@ -515,38 +516,48 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
//
|
//
|
||||||
|
|
||||||
// Decide on the basic SBS Message Type
|
// Decide on the basic SBS Message Type
|
||||||
if ((mm->msgtype == 4) || (mm->msgtype == 20)) {
|
switch (mm->msgtype) {
|
||||||
|
case 4:
|
||||||
|
case 20:
|
||||||
msgType = 5;
|
msgType = 5;
|
||||||
} else if ((mm->msgtype == 5) || (mm->msgtype == 21)) {
|
break;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
case 21:
|
||||||
msgType = 6;
|
msgType = 6;
|
||||||
} else if ((mm->msgtype == 0) || (mm->msgtype == 16)) {
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
case 16:
|
||||||
msgType = 7;
|
msgType = 7;
|
||||||
} else if (mm->msgtype == 11) {
|
break;
|
||||||
|
|
||||||
|
case 11:
|
||||||
msgType = 8;
|
msgType = 8;
|
||||||
} else if ((mm->msgtype != 17) && (mm->msgtype != 18)) {
|
break;
|
||||||
return;
|
|
||||||
} else if ((mm->metype >= 1) && (mm->metype <= 4)) {
|
case 17:
|
||||||
|
case 18:
|
||||||
|
if (mm->metype >= 1 && mm->metype <= 4) {
|
||||||
msgType = 1;
|
msgType = 1;
|
||||||
} else if ((mm->metype >= 5) && (mm->metype <= 8)) {
|
} else if (mm->metype >= 5 && mm->metype <= 8) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
|
msgType = 2;
|
||||||
{msgType = 2;}
|
} else if (mm->metype >= 9 && mm->metype <= 18) {
|
||||||
else
|
msgType = 3;
|
||||||
{msgType = 7;}
|
} else if (mm->metype == 19) {
|
||||||
} 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;
|
msgType = 4;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff
|
// Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff
|
||||||
p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr);
|
p += sprintf(p, "MSG,%d,1,1,%06X,1,", msgType, mm->addr);
|
||||||
|
|
||||||
// Find current system time
|
// Find current system time
|
||||||
clock_gettime(CLOCK_REALTIME, &now);
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
@ -564,53 +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));
|
p += sprintf(p, "%02d:%02d:%02d.%03u", stTime_now.tm_hour, stTime_now.tm_min, stTime_now.tm_sec, (unsigned) (now.tv_nsec / 1000000U));
|
||||||
|
|
||||||
// Field 11 is the callsign (if we have it)
|
// Field 11 is the callsign (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);}
|
if (mm->callsign_valid) {p += sprintf(p, ",%s", mm->callsign);}
|
||||||
else {p += sprintf(p, ",");}
|
else {p += sprintf(p, ",");}
|
||||||
|
|
||||||
// Field 12 is the altitude (if we have it) - force to zero if we're on the ground
|
// Field 12 is the altitude (if we have it)
|
||||||
if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) {
|
if (mm->altitude_valid) {
|
||||||
p += sprintf(p, ",0");
|
if (Modes.use_gnss) {
|
||||||
} else if (Modes.use_hae && (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID)) {
|
if (mm->altitude_source == ALTITUDE_GNSS) {
|
||||||
p += sprintf(p, ",%dH", mm->altitude_hae);
|
p += sprintf(p, ",%dH", mm->altitude);
|
||||||
} else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
} else if (trackDataValid(&a->gnss_delta_valid)) {
|
||||||
if (Modes.use_hae && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) {
|
p += sprintf(p, ",%dH", mm->altitude + a->gnss_delta);
|
||||||
p += sprintf(p, ",%dH", mm->altitude + a->hae_delta);
|
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",%d", mm->altitude);
|
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 {
|
} else {
|
||||||
p += sprintf(p, ",");
|
p += sprintf(p, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field 13 is the ground Speed (if we have it)
|
// Field 13 is the ground Speed (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED) {
|
||||||
p += sprintf(p, ",%d", mm->velocity);
|
p += sprintf(p, ",%d", mm->speed);
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",");
|
p += sprintf(p, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field 14 is the ground Heading (if we have it)
|
// Field 14 is the ground Heading (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
if (mm->heading_valid && mm->heading_source == HEADING_TRUE) {
|
||||||
p += sprintf(p, ",%d", mm->heading);
|
p += sprintf(p, ",%d", mm->heading);
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",");
|
p += sprintf(p, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields 15 and 16 are the Lat/Lon (if we have it)
|
// Fields 15 and 16 are the Lat/Lon (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);}
|
if (mm->cpr_decoded) {
|
||||||
else {p += sprintf(p, ",,");}
|
p += sprintf(p, ",%1.5f,%1.5f", mm->decoded_lat, mm->decoded_lon);
|
||||||
|
} else {
|
||||||
|
p += sprintf(p, ",,");
|
||||||
|
}
|
||||||
|
|
||||||
// Field 17 is the VerticalRate (if we have it)
|
// Field 17 is the VerticalRate (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);}
|
if (mm->vert_rate_valid) {
|
||||||
else {p += sprintf(p, ",");}
|
p += sprintf(p, ",%d", mm->vert_rate);
|
||||||
|
} else {
|
||||||
|
p += sprintf(p, ",");
|
||||||
|
}
|
||||||
|
|
||||||
// Field 18 is the Squawk (if we have it)
|
// Field 18 is the Squawk (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);}
|
if (mm->squawk_valid) {
|
||||||
else {p += sprintf(p, ",");}
|
p += sprintf(p, ",%04x", mm->squawk);
|
||||||
|
} else {
|
||||||
|
p += sprintf(p, ",");
|
||||||
|
}
|
||||||
|
|
||||||
// Field 19 is the Squawk Changing Alert flag (if we have it)
|
// Field 19 is the Squawk Changing Alert flag (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
|
if (mm->alert_valid) {
|
||||||
if ((mm->fs >= 2) && (mm->fs <= 4)) {
|
if (mm->alert) {
|
||||||
p += sprintf(p, ",-1");
|
p += sprintf(p, ",-1");
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",0");
|
p += sprintf(p, ",0");
|
||||||
|
@ -620,8 +648,8 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field 20 is the Squawk Emergency flag (if we have it)
|
// Field 20 is the Squawk Emergency flag (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
if (mm->squawk_valid) {
|
||||||
if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) {
|
if ((mm->squawk == 0x7500) || (mm->squawk == 0x7600) || (mm->squawk == 0x7700)) {
|
||||||
p += sprintf(p, ",-1");
|
p += sprintf(p, ",-1");
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",0");
|
p += sprintf(p, ",0");
|
||||||
|
@ -631,8 +659,8 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field 21 is the Squawk Ident flag (if we have it)
|
// Field 21 is the Squawk Ident flag (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
|
if (mm->spi_valid) {
|
||||||
if ((mm->fs >= 4) && (mm->fs <= 5)) {
|
if (mm->spi) {
|
||||||
p += sprintf(p, ",-1");
|
p += sprintf(p, ",-1");
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",0");
|
p += sprintf(p, ",0");
|
||||||
|
@ -642,14 +670,16 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field 22 is the OnTheGround flag (if we have it)
|
// Field 22 is the OnTheGround flag (if we have it)
|
||||||
if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) {
|
switch (mm->airground) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_AOG) {
|
case AG_GROUND:
|
||||||
p += sprintf(p, ",-1");
|
p += sprintf(p, ",-1");
|
||||||
} else {
|
break;
|
||||||
|
case AG_AIRBORNE:
|
||||||
p += sprintf(p, ",0");
|
p += sprintf(p, ",0");
|
||||||
}
|
break;
|
||||||
} else {
|
default:
|
||||||
p += sprintf(p, ",");
|
p += sprintf(p, ",");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
p += sprintf(p, "\r\n");
|
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) {
|
void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
int is_mlat = ((mm->bFlags & MODES_ACFLAGS_FROM_MLAT) != 0);
|
int is_mlat = (mm->source == SOURCE_MLAT);
|
||||||
|
|
||||||
if (!is_mlat && mm->correctedbits < 2) {
|
if (!is_mlat && mm->correctedbits < 2) {
|
||||||
// Don't ever forward 2-bit-corrected messages via SBS output.
|
// Don't ever forward 2-bit-corrected messages via SBS output.
|
||||||
|
@ -697,6 +727,10 @@ void modesQueueOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
// Forward mlat messages via beast output only if --forward-mlat is set
|
// Forward mlat messages via beast output only if --forward-mlat is set
|
||||||
modesSendBeastOutput(mm);
|
modesSendBeastOutput(mm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_mlat) {
|
||||||
|
writeFATSVEvent(mm, a);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
@ -928,24 +962,24 @@ static const char *jsonEscapeString(const char *str) {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *append_flags(char *p, char *end, int flags)
|
static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t source)
|
||||||
{
|
{
|
||||||
p += snprintf(p, end-p, "[");
|
p += snprintf(p, end-p, "[");
|
||||||
if (flags & MODES_ACFLAGS_SQUAWK_VALID)
|
if (a->squawk_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"squawk\",");
|
p += snprintf(p, end-p, "\"squawk\",");
|
||||||
if (flags & MODES_ACFLAGS_CALLSIGN_VALID)
|
if (a->callsign_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"callsign\",");
|
p += snprintf(p, end-p, "\"callsign\",");
|
||||||
if (flags & MODES_ACFLAGS_LATLON_VALID)
|
if (a->position_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"lat\",\"lon\",");
|
p += snprintf(p, end-p, "\"lat\",\"lon\",");
|
||||||
if (flags & MODES_ACFLAGS_ALTITUDE_VALID)
|
if (a->altitude_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"altitude\",");
|
p += snprintf(p, end-p, "\"altitude\",");
|
||||||
if (flags & MODES_ACFLAGS_HEADING_VALID)
|
if (a->heading_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"track\",");
|
p += snprintf(p, end-p, "\"track\",");
|
||||||
if (flags & MODES_ACFLAGS_SPEED_VALID)
|
if (a->speed_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"speed\",");
|
p += snprintf(p, end-p, "\"speed\",");
|
||||||
if (flags & MODES_ACFLAGS_VERTRATE_VALID)
|
if (a->vert_rate_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"vert_rate\",");
|
p += snprintf(p, end-p, "\"vert_rate\",");
|
||||||
if (flags & MODES_ACFLAGS_CATEGORY_VALID)
|
if (a->category_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"category\",");
|
p += snprintf(p, end-p, "\"category\",");
|
||||||
if (p[-1] != '[')
|
if (p[-1] != '[')
|
||||||
--p;
|
--p;
|
||||||
|
@ -953,6 +987,29 @@ static char *append_flags(char *p, char *end, int flags)
|
||||||
return p;
|
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) {
|
char *generateAircraftJson(const char *url_path, int *len) {
|
||||||
uint64_t now = mstime();
|
uint64_t now = mstime();
|
||||||
struct aircraft *a;
|
struct aircraft *a;
|
||||||
|
@ -984,32 +1041,31 @@ char *generateAircraftJson(const char *url_path, int *len) {
|
||||||
*p++ = ',';
|
*p++ = ',';
|
||||||
|
|
||||||
p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF);
|
p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF);
|
||||||
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID)
|
if (a->addrtype != ADDR_ADSB_ICAO)
|
||||||
p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->modeA);
|
p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_short_string(a->addrtype));
|
||||||
if (a->bFlags & MODES_ACFLAGS_CALLSIGN_VALID)
|
if (trackDataValid(&a->squawk_valid))
|
||||||
p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->flight));
|
p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk);
|
||||||
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID)
|
if (trackDataValid(&a->callsign_valid))
|
||||||
p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->seenLatLon)/1000.0);
|
p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign));
|
||||||
if ((a->bFlags & MODES_ACFLAGS_AOG_VALID) && (a->bFlags & MODES_ACFLAGS_AOG))
|
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\"");
|
p += snprintf(p, end-p, ",\"altitude\":\"ground\"");
|
||||||
else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID)
|
else if (trackDataValid(&a->altitude_valid))
|
||||||
p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude);
|
p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude);
|
||||||
if (a->bFlags & MODES_ACFLAGS_VERTRATE_VALID)
|
if (trackDataValid(&a->vert_rate_valid))
|
||||||
p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate);
|
p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate);
|
||||||
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID)
|
if (trackDataValid(&a->heading_valid))
|
||||||
p += snprintf(p, end-p, ",\"track\":%d", a->track);
|
p += snprintf(p, end-p, ",\"track\":%d", a->heading);
|
||||||
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID)
|
if (trackDataValid(&a->speed_valid))
|
||||||
p += snprintf(p, end-p, ",\"speed\":%d", a->speed);
|
p += snprintf(p, end-p, ",\"speed\":%d", a->speed);
|
||||||
if (a->bFlags & MODES_ACFLAGS_CATEGORY_VALID)
|
if (trackDataValid(&a->category_valid))
|
||||||
p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category);
|
p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category);
|
||||||
if (a->mlatFlags) {
|
|
||||||
p += snprintf(p, end-p, ",\"mlat\":");
|
p += snprintf(p, end-p, ",\"mlat\":");
|
||||||
p = append_flags(p, end, a->mlatFlags);
|
p = append_flags(p, end, a, SOURCE_MLAT);
|
||||||
}
|
|
||||||
if (a->tisbFlags) {
|
|
||||||
p += snprintf(p, end-p, ",\"tisb\":");
|
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}",
|
p += snprintf(p, end-p, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}",
|
||||||
a->messages, (now - a->seen)/1000.0,
|
a->messages, (now - a->seen)/1000.0,
|
||||||
|
@ -1471,11 +1527,12 @@ static int handleHTTPRequest(struct client *c, char *p) {
|
||||||
// Send header and content.
|
// Send header and content.
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if ( (write(c->fd, hdr, hdrlen) != hdrlen)
|
if ( (write(c->fd, hdr, hdrlen) != hdrlen)
|
||||||
|| (write(c->fd, content, clen) != clen) ) {
|
|| (write(c->fd, content, clen) != clen) )
|
||||||
#else
|
#else
|
||||||
if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen)
|
if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen)
|
||||||
|| (send(c->fd, content, clen, 0) != clen) ) {
|
|| (send(c->fd, content, clen, 0) != clen) )
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
free(content);
|
free(content);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1536,10 +1593,11 @@ static void modesReadFromClient(struct client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if (nread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { // No data available (not really an error)
|
if (nread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) // No data available (not really an error)
|
||||||
#else
|
#else
|
||||||
if (nread < 0 && errno == EWOULDBLOCK) { // No data available (not really an error)
|
if (nread < 0 && errno == EWOULDBLOCK) // No data available (not really an error)
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1625,6 +1683,92 @@ static void modesReadFromClient(struct client *c) {
|
||||||
|
|
||||||
#define TSV_MAX_PACKET_SIZE 180
|
#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()
|
static void writeFATSV()
|
||||||
{
|
{
|
||||||
struct aircraft *a;
|
struct aircraft *a;
|
||||||
|
@ -1645,35 +1789,24 @@ static void writeFATSV()
|
||||||
|
|
||||||
for (a = Modes.aircrafts; a; a = a->next) {
|
for (a = Modes.aircrafts; a; a = a->next) {
|
||||||
int altValid = 0;
|
int altValid = 0;
|
||||||
int alt = 0;
|
int altGNSSValid = 0;
|
||||||
uint64_t altAge = 999999;
|
int positionValid = 0;
|
||||||
|
|
||||||
int groundValid = 0;
|
|
||||||
int ground = 0;
|
|
||||||
|
|
||||||
int latlonValid = 0;
|
|
||||||
uint64_t latlonAge = 999999;
|
|
||||||
|
|
||||||
int speedValid = 0;
|
int speedValid = 0;
|
||||||
uint64_t speedAge = 999999;
|
int speedIASValid = 0;
|
||||||
|
int speedTASValid = 0;
|
||||||
|
int headingValid = 0;
|
||||||
|
int headingMagValid = 0;
|
||||||
|
int airgroundValid = 0;
|
||||||
|
|
||||||
int trackValid = 0;
|
uint64_t minAge;
|
||||||
uint64_t trackAge = 999999;
|
|
||||||
|
|
||||||
uint64_t emittedAge;
|
|
||||||
|
|
||||||
int useful = 0;
|
int useful = 0;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
|
|
||||||
char *p, *end;
|
char *p, *end;
|
||||||
|
|
||||||
int flags;
|
|
||||||
int used_tisb = 0;
|
int used_tisb = 0;
|
||||||
|
|
||||||
// skip non-ICAO
|
|
||||||
if (a->addr & MODES_NON_ICAO_ADDRESS)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (a->messages < 2) // basic filter for bad decodes
|
if (a->messages < 2) // basic filter for bad decodes
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1682,131 +1815,200 @@ static void writeFATSV()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
emittedAge = (now - a->fatsv_last_emitted);
|
altValid = trackDataValidEx(&a->altitude_valid, now, 15000, SOURCE_MODE_S); // for non-ADS-B transponders, DF0/4/16/20 are the only sources of altitude data
|
||||||
|
altGNSSValid = trackDataValidEx(&a->altitude_gnss_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
airgroundValid = trackDataValidEx(&a->airground_valid, now, 15000, SOURCE_MODE_S_CHECKED); // for non-ADS-B transponders, only trust DF11 CA field
|
||||||
|
positionValid = trackDataValidEx(&a->position_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
headingValid = trackDataValidEx(&a->heading_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
headingMagValid = trackDataValidEx(&a->heading_magnetic_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
speedValid = trackDataValidEx(&a->speed_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
speedIASValid = trackDataValidEx(&a->speed_ias_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
speedTASValid = trackDataValidEx(&a->speed_tas_valid, now, 15000, SOURCE_MODE_S_CHECKED);
|
||||||
|
|
||||||
// ignore all mlat-derived data
|
// If we are definitely on the ground, suppress any unreliable altitude info.
|
||||||
flags = a->bFlags & ~a->mlatFlags;
|
// When on the ground, ADS-B transponders don't emit an ADS-B message that includes
|
||||||
|
// altitude, so a corrupted Mode S altitude response from some other in-the-air AC
|
||||||
if (flags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
// might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000".
|
||||||
alt = a->altitude;
|
if (airgroundValid && a->airground == AG_GROUND && a->altitude_valid.source < SOURCE_MODE_S_CHECKED)
|
||||||
altAge = now - a->seenAltitude;
|
altValid = 0;
|
||||||
altValid = (altAge <= 30000);
|
|
||||||
used_tisb |= (a->tisbFlags & MODES_ACFLAGS_ALTITUDE_VALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & MODES_ACFLAGS_AOG_VALID) {
|
|
||||||
groundValid = 1;
|
|
||||||
|
|
||||||
if (flags & MODES_ACFLAGS_AOG) {
|
|
||||||
// force zero altitude on ground
|
|
||||||
alt = 0;
|
|
||||||
altValid = 1;
|
|
||||||
altAge = 0;
|
|
||||||
ground = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
used_tisb |= (a->tisbFlags & MODES_ACFLAGS_AOG_VALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & MODES_ACFLAGS_LATLON_VALID) {
|
|
||||||
latlonAge = now - a->seenLatLon;
|
|
||||||
latlonValid = (latlonAge <= 30000);
|
|
||||||
used_tisb |= (a->tisbFlags & MODES_ACFLAGS_LATLON_VALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & MODES_ACFLAGS_HEADING_VALID) {
|
|
||||||
trackAge = now - a->seenTrack;
|
|
||||||
trackValid = (trackAge <= 30000);
|
|
||||||
used_tisb |= (a->tisbFlags & MODES_ACFLAGS_HEADING_VALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & MODES_ACFLAGS_SPEED_VALID) {
|
|
||||||
speedAge = now - a->seenSpeed;
|
|
||||||
speedValid = (speedAge <= 30000);
|
|
||||||
used_tisb |= (a->tisbFlags & MODES_ACFLAGS_SPEED_VALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't send mode S very often
|
|
||||||
if (!latlonValid && emittedAge < 30000) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it hasn't changed altitude, heading, or speed much,
|
// if it hasn't changed altitude, heading, or speed much,
|
||||||
// don't update so often
|
// don't update so often
|
||||||
changed = 0;
|
changed = 0;
|
||||||
if (trackValid && abs(a->track - a->fatsv_emitted_track) >= 2) {
|
if (altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) {
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
if (altGNSSValid && abs(a->altitude_gnss - a->fatsv_emitted_altitude_gnss) >= 50) {
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
if (headingValid && abs(a->heading - a->fatsv_emitted_heading) >= 2) {
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
if (headingMagValid && abs(a->heading_magnetic - a->fatsv_emitted_heading_magnetic) >= 2) {
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
if (speedValid && abs(a->speed - a->fatsv_emitted_speed) >= 25) {
|
if (speedValid && abs(a->speed - a->fatsv_emitted_speed) >= 25) {
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
if (altValid && abs(alt - a->fatsv_emitted_altitude) >= 50) {
|
if (speedIASValid && abs(a->speed_ias - a->fatsv_emitted_speed_ias) >= 25) {
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
if (speedTASValid && abs(a->speed_tas - a->fatsv_emitted_speed_tas) >= 25) {
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!altValid || alt < 10000) {
|
if (airgroundValid && ((a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) ||
|
||||||
|
(a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE))) {
|
||||||
|
// Air-ground transition, handle it immediately.
|
||||||
|
minAge = 0;
|
||||||
|
} else if (!positionValid) {
|
||||||
|
// don't send mode S very often
|
||||||
|
minAge = 30000;
|
||||||
|
} else if ((airgroundValid && a->airground == AG_GROUND) ||
|
||||||
|
(altValid && a->altitude < 500 && (!speedValid || a->speed < 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
|
// Below 10000 feet, emit up to every 5s when changing, 10s otherwise
|
||||||
if (changed && emittedAge < 5000)
|
minAge = (changed ? 5000 : 10000);
|
||||||
continue;
|
|
||||||
if (!changed && emittedAge < 10000)
|
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
// Above 10000 feet, emit up to every 10s when changing, 30s otherwise
|
// Above 10000 feet, emit up to every 10s when changing, 30s otherwise
|
||||||
if (changed && emittedAge < 10000)
|
minAge = (changed ? 10000 : 30000);
|
||||||
continue;
|
|
||||||
if (!changed && emittedAge < 30000)
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((now - a->fatsv_last_emitted) < minAge)
|
||||||
|
continue;
|
||||||
|
|
||||||
p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE);
|
p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE);
|
||||||
if (!p)
|
if (!p)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
end = p + TSV_MAX_PACKET_SIZE;
|
end = p + TSV_MAX_PACKET_SIZE;
|
||||||
# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p)))
|
# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p)))
|
||||||
p += snprintf(p, bufsize(p,end), "clock\t%ld\thexid\t%06X", (long)(a->seen / 1000), a->addr);
|
|
||||||
|
|
||||||
if (*a->flight != '\0') {
|
p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, (uint64_t)(a->seen / 1000));
|
||||||
p += snprintf(p, bufsize(p,end), "\tident\t%s", a->flight);
|
|
||||||
|
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) {
|
if (a->addrtype != ADDR_ADSB_ICAO) {
|
||||||
p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->modeA);
|
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
|
// only emit alt, speed, latlon, track if they have been received since the last time
|
||||||
// and are not stale
|
// and are not stale
|
||||||
|
|
||||||
if (altValid && altAge < emittedAge) {
|
if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) {
|
||||||
p += snprintf(p, bufsize(p,end), "\talt\t%d", alt);
|
p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude);
|
||||||
|
a->fatsv_emitted_altitude = a->altitude;
|
||||||
|
if (a->altitude_valid.source == SOURCE_TISB) {
|
||||||
|
used_tisb = 1;
|
||||||
|
}
|
||||||
useful = 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);
|
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;
|
useful = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groundValid) {
|
if (speedIASValid && a->speed_ias_valid.updated > a->fatsv_last_emitted) {
|
||||||
if (ground) {
|
p += snprintf(p, bufsize(p,end), "\tspeed_ias\t%d", a->speed_ias);
|
||||||
p += snprintf(p, bufsize(p,end), "\tairGround\tG");
|
a->fatsv_emitted_speed_ias = a->speed_ias;
|
||||||
} else {
|
if (a->speed_ias_valid.source == SOURCE_TISB) {
|
||||||
p += snprintf(p, bufsize(p,end), "\tairGround\tA");
|
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);
|
p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon);
|
||||||
|
if (a->position_valid.source == SOURCE_TISB) {
|
||||||
|
used_tisb = 1;
|
||||||
|
}
|
||||||
useful = 1;
|
useful = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackValid && trackAge < emittedAge) {
|
if (headingValid && a->heading_valid.updated > a->fatsv_last_emitted) {
|
||||||
p += snprintf(p, bufsize(p,end), "\theading\t%d", a->track);
|
p += snprintf(p, bufsize(p,end), "\theading\t%d", a->heading);
|
||||||
|
a->fatsv_emitted_heading = a->heading;
|
||||||
|
if (a->heading_valid.source == SOURCE_TISB) {
|
||||||
|
used_tisb = 1;
|
||||||
|
}
|
||||||
useful = 1;
|
useful = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (used_tisb) {
|
if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) {
|
||||||
p += snprintf(p, bufsize(p,end), "\ttisb\t1");
|
p += snprintf(p, bufsize(p,end), "\theading_magnetic\t%d", a->heading);
|
||||||
|
a->fatsv_emitted_heading_magnetic = a->heading_magnetic;
|
||||||
|
if (a->heading_valid.source == SOURCE_TISB) {
|
||||||
|
used_tisb = 1;
|
||||||
|
}
|
||||||
|
useful = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE)) {
|
||||||
|
p += snprintf(p, bufsize(p,end), "\tairGround\t%s", a->airground == AG_GROUND ? "G+" : "A+");
|
||||||
|
a->fatsv_emitted_airground = a->airground;
|
||||||
|
if (a->airground_valid.source == SOURCE_TISB) {
|
||||||
|
used_tisb = 1;
|
||||||
|
}
|
||||||
|
useful = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we didn't get at least an alt or a speed or a latlon or
|
// if we didn't get at least an alt or a speed or a latlon or
|
||||||
|
@ -1816,6 +2018,10 @@ static void writeFATSV()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (used_tisb) {
|
||||||
|
p += snprintf(p, bufsize(p,end), "\ttisb\t1");
|
||||||
|
}
|
||||||
|
|
||||||
p += snprintf(p, bufsize(p,end), "\n");
|
p += snprintf(p, bufsize(p,end), "\n");
|
||||||
|
|
||||||
if (p <= end)
|
if (p <= end)
|
||||||
|
@ -1825,9 +2031,6 @@ static void writeFATSV()
|
||||||
# undef bufsize
|
# undef bufsize
|
||||||
|
|
||||||
a->fatsv_last_emitted = now;
|
a->fatsv_last_emitted = now;
|
||||||
a->fatsv_emitted_altitude = alt;
|
|
||||||
a->fatsv_emitted_track = a->track;
|
|
||||||
a->fatsv_emitted_speed = a->speed;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1840,7 +2043,7 @@ void modesNetPeriodicWork(void) {
|
||||||
uint64_t now = mstime();
|
uint64_t now = mstime();
|
||||||
int need_flush = 0;
|
int need_flush = 0;
|
||||||
|
|
||||||
// Accept new connetions
|
// Accept new connections
|
||||||
modesAcceptClients();
|
modesAcceptClients();
|
||||||
|
|
||||||
// Read from clients
|
// Read from clients
|
||||||
|
|
403
track.c
403
track.c
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// track.c: aircraft state tracking
|
// track.c: aircraft state tracking
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk>
|
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
|
||||||
//
|
//
|
||||||
// This file is free software: you may copy, redistribute and/or modify it
|
// This file is free software: you may copy, redistribute and/or modify it
|
||||||
// under the terms of the GNU General Public License as published by the
|
// under the terms of the GNU General Public License as published by the
|
||||||
|
@ -48,6 +48,7 @@
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "dump1090.h"
|
#include "dump1090.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
/* #define DEBUG_CPR_CHECKS */
|
/* #define DEBUG_CPR_CHECKS */
|
||||||
|
|
||||||
|
@ -65,16 +66,20 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
|
||||||
|
|
||||||
// Now initialise things that should not be 0/NULL to their defaults
|
// Now initialise things that should not be 0/NULL to their defaults
|
||||||
a->addr = mm->addr;
|
a->addr = mm->addr;
|
||||||
|
a->addrtype = mm->addrtype;
|
||||||
for (i = 0; i < 8; ++i)
|
for (i = 0; i < 8; ++i)
|
||||||
a->signalLevel[i] = 1e-5;
|
a->signalLevel[i] = 1e-5;
|
||||||
a->signalNext = 0;
|
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
|
// 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
|
// set them once here during initialisation, and don't bother to set them every
|
||||||
// time this ModeA/C is received again in the future
|
// time this ModeA/C is received again in the future
|
||||||
if (mm->msgtype == 32) {
|
if (mm->msgtype == 32) {
|
||||||
a->modeACflags = MODEAC_MSG_FLAG;
|
a->modeACflags = MODEAC_MSG_FLAG;
|
||||||
if (!(mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID)) {
|
if (!mm->altitude_valid) {
|
||||||
a->modeACflags |= MODEAC_MSG_MODEA_ONLY;
|
a->modeACflags |= MODEAC_MSG_MODEA_ONLY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +108,51 @@ struct aircraft *trackFindAircraft(uint32_t addr) {
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should we accept some new data from the given source?
|
||||||
|
// If so, update the validity and return 1
|
||||||
|
static int accept_data(data_validity *d, datasource_t source, uint64_t now)
|
||||||
|
{
|
||||||
|
if (source < d->source && now < d->stale)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
d->source = source;
|
||||||
|
d->updated = now;
|
||||||
|
d->stale = now + 60000;
|
||||||
|
d->expires = now + 70000;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given two datasources, produce a third datasource for data combined from them.
|
||||||
|
static void combine_validity(data_validity *to, const data_validity *from1, const data_validity *from2) {
|
||||||
|
if (from1->source == SOURCE_INVALID) {
|
||||||
|
*to = *from2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from2->source == SOURCE_INVALID) {
|
||||||
|
*to = *from1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
to->source = (from1->source < from2->source) ? from1->source : from2->source; // the worse of the two input sources
|
||||||
|
to->updated = (from1->updated > from2->updated) ? from1->updated : from2->updated; // the *later* of the two update times
|
||||||
|
to->stale = (from1->stale < from2->stale) ? from1->stale : from2->stale; // the earlier of the two stale times
|
||||||
|
to->expires = (from1->expires < from2->expires) ? from1->expires : from2->expires; // the earlier of the two expiry times
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_validity(const data_validity *lhs, const data_validity *rhs, uint64_t now) {
|
||||||
|
if (now < lhs->stale && lhs->source > rhs->source)
|
||||||
|
return 1;
|
||||||
|
else if (now < rhs->stale && lhs->source < rhs->source)
|
||||||
|
return -1;
|
||||||
|
else if (lhs->updated > rhs->updated)
|
||||||
|
return 1;
|
||||||
|
else if (lhs->updated < rhs->updated)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// CPR position updating
|
// CPR position updating
|
||||||
//
|
//
|
||||||
|
@ -149,7 +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
|
// return true if it's OK for the aircraft to have travelled from its last known position
|
||||||
// to a new position at (lat,lon,surface) at a time of now.
|
// to a new position at (lat,lon,surface) at a time of now.
|
||||||
static int speed_check(struct aircraft *a, struct modesMessage *mm, double lat, double lon, uint64_t now, int surface)
|
static int speed_check(struct aircraft *a, double lat, double lon, uint64_t now, int surface)
|
||||||
{
|
{
|
||||||
uint64_t elapsed;
|
uint64_t elapsed;
|
||||||
double distance;
|
double distance;
|
||||||
|
@ -157,17 +207,17 @@ static int speed_check(struct aircraft *a, struct modesMessage *mm, double lat,
|
||||||
int speed;
|
int speed;
|
||||||
int inrange;
|
int inrange;
|
||||||
|
|
||||||
if (!(a->bFlags & MODES_ACFLAGS_LATLON_VALID))
|
if (!trackDataValid(&a->position_valid))
|
||||||
return 1; // no reference, assume OK
|
return 1; // no reference, assume OK
|
||||||
|
|
||||||
elapsed = now - a->seenLatLon;
|
elapsed = trackDataAge(&a->position_valid, now);
|
||||||
|
|
||||||
if ((mm->bFlags & MODES_ACFLAGS_SPEED_VALID) && (a->bFlags & MODES_ACFLAGS_SPEED_VALID))
|
if (trackDataValid(&a->speed_valid))
|
||||||
speed = (mm->velocity + a->speed) / 2;
|
|
||||||
else if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID)
|
|
||||||
speed = mm->velocity;
|
|
||||||
else if ((a->bFlags & MODES_ACFLAGS_SPEED_VALID) && (now - a->seenSpeed) < 30000)
|
|
||||||
speed = a->speed;
|
speed = a->speed;
|
||||||
|
else if (trackDataValid(&a->speed_ias_valid))
|
||||||
|
speed = a->speed_ias * 4 / 3;
|
||||||
|
else if (trackDataValid(&a->speed_tas_valid))
|
||||||
|
speed = a->speed_tas * 4 / 3;
|
||||||
else
|
else
|
||||||
speed = surface ? 100 : 600; // guess
|
speed = surface ? 100 : 600; // guess
|
||||||
|
|
||||||
|
@ -207,17 +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)
|
static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double *lat, double *lon, unsigned *nuc)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
int fflag = (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) != 0;
|
int fflag = mm->cpr_odd;
|
||||||
int surface = (mm->bFlags & MODES_ACFLAGS_AOG) != 0;
|
int surface = (mm->airground == AG_GROUND);
|
||||||
|
|
||||||
*nuc = (a->even_cprnuc < a->odd_cprnuc ? a->even_cprnuc : a->odd_cprnuc); // worst of the two positions
|
*nuc = (a->cpr_even_nuc < a->cpr_odd_nuc ? a->cpr_even_nuc : a->cpr_odd_nuc); // worst of the two positions
|
||||||
|
|
||||||
if (surface) {
|
if (surface) {
|
||||||
// surface global CPR
|
// surface global CPR
|
||||||
// find reference location
|
// find reference location
|
||||||
double reflat, reflon;
|
double reflat, reflon;
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first
|
if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID)) { // Ok to try aircraft relative first
|
||||||
reflat = a->lat;
|
reflat = a->lat;
|
||||||
reflon = a->lon;
|
reflon = a->lon;
|
||||||
if (a->pos_nuc < *nuc)
|
if (a->pos_nuc < *nuc)
|
||||||
|
@ -231,27 +281,25 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now
|
||||||
}
|
}
|
||||||
|
|
||||||
result = decodeCPRsurface(reflat, reflon,
|
result = decodeCPRsurface(reflat, reflon,
|
||||||
a->even_cprlat, a->even_cprlon,
|
a->cpr_even_lat, a->cpr_even_lon,
|
||||||
a->odd_cprlat, a->odd_cprlon,
|
a->cpr_odd_lat, a->cpr_odd_lon,
|
||||||
fflag,
|
fflag,
|
||||||
lat, lon);
|
lat, lon);
|
||||||
} else {
|
} else {
|
||||||
// airborne global CPR
|
// airborne global CPR
|
||||||
result = decodeCPRairborne(a->even_cprlat, a->even_cprlon,
|
result = decodeCPRairborne(a->cpr_even_lat, a->cpr_even_lon,
|
||||||
a->odd_cprlat, a->odd_cprlon,
|
a->cpr_odd_lat, a->cpr_odd_lon,
|
||||||
fflag,
|
fflag,
|
||||||
lat, lon);
|
lat, lon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
#ifdef DEBUG_CPR_CHECKS
|
#ifdef DEBUG_CPR_CHECKS
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) {
|
fprintf(stderr, "CPR: decode failure for %06X (%d).\n", a->addr, result);
|
||||||
fprintf(stderr, "CPR: decode failure from MLAT (%06X) (%d).\n", a->addr, result);
|
|
||||||
fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n",
|
fprintf(stderr, " even: %d %d odd: %d %d fflag: %s\n",
|
||||||
a->even_cprlat, a->even_cprlon,
|
a->cpr_even_lat, a->cpr_even_lon,
|
||||||
a->odd_cprlat, a->odd_cprlon,
|
a->cpr_odd_lat, a->cpr_odd_lon,
|
||||||
fflag ? "odd" : "even");
|
fflag ? "odd" : "even");
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return result;
|
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
|
// for mlat results, skip the speed check
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT)
|
if (mm->source == SOURCE_MLAT)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// check speed limit
|
// check speed limit
|
||||||
if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && a->pos_nuc >= *nuc && !speed_check(a, mm, *lat, *lon, now, surface)) {
|
if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, now, surface)) {
|
||||||
Modes.stats_current.cpr_global_speed_checks++;
|
Modes.stats_current.cpr_global_speed_checks++;
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
@ -290,12 +338,12 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
|
||||||
double reflat, reflon;
|
double reflat, reflon;
|
||||||
double range_limit = 0;
|
double range_limit = 0;
|
||||||
int result;
|
int result;
|
||||||
int fflag = (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) != 0;
|
int fflag = mm->cpr_odd;
|
||||||
int surface = (mm->bFlags & MODES_ACFLAGS_AOG) != 0;
|
int surface = (mm->airground == AG_GROUND);
|
||||||
|
|
||||||
*nuc = mm->nuc_p;
|
*nuc = mm->cpr_nucp;
|
||||||
|
|
||||||
if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) {
|
if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID)) {
|
||||||
reflat = a->lat;
|
reflat = a->lat;
|
||||||
reflon = a->lon;
|
reflon = a->lon;
|
||||||
|
|
||||||
|
@ -331,12 +379,13 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
|
||||||
}
|
}
|
||||||
|
|
||||||
result = decodeCPRrelative(reflat, reflon,
|
result = decodeCPRrelative(reflat, reflon,
|
||||||
mm->raw_latitude,
|
mm->cpr_lat,
|
||||||
mm->raw_longitude,
|
mm->cpr_lon,
|
||||||
fflag, surface,
|
fflag, surface,
|
||||||
lat, lon);
|
lat, lon);
|
||||||
if (result < 0)
|
if (result < 0) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// check range limit
|
// check range limit
|
||||||
if (range_limit > 0) {
|
if (range_limit > 0) {
|
||||||
|
@ -348,7 +397,10 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check speed limit
|
// check speed limit
|
||||||
if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && a->pos_nuc >= *nuc && !speed_check(a, mm, *lat, *lon, now, surface)) {
|
if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, now, surface)) {
|
||||||
|
#ifdef DEBUG_CPR_CHECKS
|
||||||
|
fprintf(stderr, "Speed check for %06X with local decoding failed\n", a->addr);
|
||||||
|
#endif
|
||||||
Modes.stats_current.cpr_local_speed_checks++;
|
Modes.stats_current.cpr_local_speed_checks++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -356,73 +408,62 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t time_between(uint64_t t1, uint64_t t2)
|
||||||
|
{
|
||||||
|
if (t1 >= t2)
|
||||||
|
return t1 - t2;
|
||||||
|
else
|
||||||
|
return t2 - t1;
|
||||||
|
}
|
||||||
|
|
||||||
static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t now)
|
static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t now)
|
||||||
{
|
{
|
||||||
int location_result = -1;
|
int location_result = -1;
|
||||||
int max_elapsed;
|
uint64_t max_elapsed;
|
||||||
double new_lat = 0, new_lon = 0;
|
double new_lat = 0, new_lon = 0;
|
||||||
unsigned new_nuc = 0;
|
unsigned new_nuc = 0;
|
||||||
|
int surface;
|
||||||
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_AOG)
|
surface = (mm->airground == AG_GROUND);
|
||||||
|
|
||||||
|
if (surface) {
|
||||||
++Modes.stats_current.cpr_surface;
|
++Modes.stats_current.cpr_surface;
|
||||||
else
|
|
||||||
++Modes.stats_current.cpr_airborne;
|
|
||||||
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_AOG) {
|
|
||||||
// Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
|
// Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
|
||||||
|
if (mm->speed_valid && mm->speed <= 25)
|
||||||
if ((mm->bFlags & MODES_ACFLAGS_SPEED_VALID) && mm->velocity <= 25)
|
|
||||||
max_elapsed = 50000;
|
max_elapsed = 50000;
|
||||||
else
|
else
|
||||||
max_elapsed = 25000;
|
max_elapsed = 25000;
|
||||||
} else {
|
} else {
|
||||||
|
++Modes.stats_current.cpr_airborne;
|
||||||
|
|
||||||
// Airborne: 10 seconds
|
// Airborne: 10 seconds
|
||||||
max_elapsed = 10000;
|
max_elapsed = 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) {
|
|
||||||
a->odd_cprnuc = mm->nuc_p;
|
|
||||||
a->odd_cprlat = mm->raw_latitude;
|
|
||||||
a->odd_cprlon = mm->raw_longitude;
|
|
||||||
a->odd_cprtime = now;
|
|
||||||
} else {
|
|
||||||
a->even_cprnuc = mm->nuc_p;
|
|
||||||
a->even_cprlat = mm->raw_latitude;
|
|
||||||
a->even_cprlon = mm->raw_longitude;
|
|
||||||
a->even_cprtime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have enough recent data, try global CPR
|
// If we have enough recent data, try global CPR
|
||||||
if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= max_elapsed) {
|
if (trackDataValid(&a->cpr_odd_valid) && trackDataValid(&a->cpr_even_valid) &&
|
||||||
|
a->cpr_odd_valid.source == a->cpr_even_valid.source &&
|
||||||
|
a->cpr_odd_airground == a->cpr_even_airground &&
|
||||||
|
time_between(a->cpr_odd_valid.updated, a->cpr_even_valid.updated) <= max_elapsed) {
|
||||||
|
|
||||||
location_result = doGlobalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc);
|
location_result = doGlobalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc);
|
||||||
|
|
||||||
if (location_result == -2) {
|
if (location_result == -2) {
|
||||||
#ifdef DEBUG_CPR_CHECKS
|
#ifdef DEBUG_CPR_CHECKS
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) {
|
fprintf(stderr, "global CPR failure (invalid) for (%06X).\n", a->addr);
|
||||||
fprintf(stderr, "CPR failure from MLAT (%06X).\n", a->addr);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
// Global CPR failed because the position produced implausible results.
|
// Global CPR failed because the position produced implausible results.
|
||||||
// This is bad data. Discard both odd and even messages and wait for a fresh pair.
|
// This is bad data. Discard both odd and even messages and wait for a fresh pair.
|
||||||
// Also disable aircraft-relative positions until we have a new good position (but don't discard the
|
// Also disable aircraft-relative positions until we have a new good position (but don't discard the
|
||||||
// recorded position itself)
|
// recorded position itself)
|
||||||
Modes.stats_current.cpr_global_bad++;
|
Modes.stats_current.cpr_global_bad++;
|
||||||
a->bFlags &= ~(MODES_ACFLAGS_LATLON_REL_OK | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID);
|
a->cpr_odd_valid.source = a->cpr_even_valid.source = a->position_valid.source = SOURCE_INVALID;
|
||||||
|
|
||||||
// Also discard the current message's data as it is suspect - we don't want
|
|
||||||
// to update any of the aircraft state from this.
|
|
||||||
mm->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LLODD_VALID | MODES_ACFLAGS_LLEVEN_VALID |
|
|
||||||
MODES_ACFLAGS_ALTITUDE_VALID |
|
|
||||||
MODES_ACFLAGS_SPEED_VALID |
|
|
||||||
MODES_ACFLAGS_HEADING_VALID |
|
|
||||||
MODES_ACFLAGS_NSEWSPD_VALID |
|
|
||||||
MODES_ACFLAGS_VERTRATE_VALID |
|
|
||||||
MODES_ACFLAGS_AOG_VALID |
|
|
||||||
MODES_ACFLAGS_AOG);
|
|
||||||
return;
|
return;
|
||||||
} else if (location_result == -1) {
|
} else if (location_result == -1) {
|
||||||
#ifdef DEBUG_CPR_CHECKS
|
#ifdef DEBUG_CPR_CHECKS
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT) {
|
if (mm->source == SOURCE_MLAT) {
|
||||||
fprintf(stderr, "CPR skipped from MLAT (%06X).\n", a->addr);
|
fprintf(stderr, "CPR skipped from MLAT (%06X).\n", a->addr);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -431,6 +472,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t
|
||||||
Modes.stats_current.cpr_global_skipped++;
|
Modes.stats_current.cpr_global_skipped++;
|
||||||
} else {
|
} else {
|
||||||
Modes.stats_current.cpr_global_ok++;
|
Modes.stats_current.cpr_global_ok++;
|
||||||
|
combine_validity(&a->position_valid, &a->cpr_even_valid, &a->cpr_odd_valid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,30 +480,30 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t
|
||||||
if (location_result == -1) {
|
if (location_result == -1) {
|
||||||
location_result = doLocalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc);
|
location_result = doLocalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc);
|
||||||
|
|
||||||
if (location_result == -1) {
|
if (location_result < 0) {
|
||||||
Modes.stats_current.cpr_local_skipped++;
|
Modes.stats_current.cpr_local_skipped++;
|
||||||
} else {
|
} else {
|
||||||
Modes.stats_current.cpr_local_ok++;
|
Modes.stats_current.cpr_local_ok++;
|
||||||
if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK)
|
mm->cpr_relative = 1;
|
||||||
Modes.stats_current.cpr_local_aircraft_relative++;
|
|
||||||
else
|
if (mm->cpr_odd) {
|
||||||
Modes.stats_current.cpr_local_receiver_relative++;
|
a->position_valid = a->cpr_odd_valid;
|
||||||
mm->bFlags |= MODES_ACFLAGS_REL_CPR_USED;
|
} else {
|
||||||
|
a->position_valid = a->cpr_even_valid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location_result == 0) {
|
if (location_result == 0) {
|
||||||
// If we sucessfully decoded, back copy the results to mm so that we can print them in list output
|
// If we sucessfully decoded, back copy the results to mm so that we can print them in list output
|
||||||
mm->bFlags |= MODES_ACFLAGS_LATLON_VALID;
|
mm->cpr_decoded = 1;
|
||||||
mm->fLat = new_lat;
|
mm->decoded_lat = new_lat;
|
||||||
mm->fLon = new_lon;
|
mm->decoded_lon = new_lon;
|
||||||
|
|
||||||
// Update aircraft state
|
// Update aircraft state
|
||||||
a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK);
|
|
||||||
a->lat = new_lat;
|
a->lat = new_lat;
|
||||||
a->lon = new_lon;
|
a->lon = new_lon;
|
||||||
a->pos_nuc = new_nuc;
|
a->pos_nuc = new_nuc;
|
||||||
a->seenLatLon = a->seen;
|
|
||||||
|
|
||||||
update_range_histogram(new_lat, new_lon);
|
update_range_histogram(new_lat, new_lon);
|
||||||
}
|
}
|
||||||
|
@ -493,126 +535,104 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
a->seen = now;
|
a->seen = now;
|
||||||
a->messages++;
|
a->messages++;
|
||||||
|
|
||||||
// if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags
|
// update addrtype, we only ever go towards "more direct" types
|
||||||
if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) {
|
if (mm->addrtype < a->addrtype)
|
||||||
a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG);
|
a->addrtype = mm->addrtype;
|
||||||
}
|
|
||||||
|
|
||||||
// If we've got a new cprlat or cprlon
|
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) {
|
unsigned modeC = (a->altitude + 49) / 100;
|
||||||
updatePosition(a, mm, now);
|
if (modeC != a->altitude_modeC) {
|
||||||
}
|
|
||||||
|
|
||||||
// If a (new) CALLSIGN has been received, copy it to the aircraft structure
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {
|
|
||||||
memcpy(a->flight, mm->flight, sizeof(a->flight));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a (new) ALTITUDE has been received, copy it to the aircraft structure
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
|
||||||
if ( (a->modeCcount) // if we've a modeCcount already
|
|
||||||
&& (a->altitude != mm->altitude ) ) // and Altitude has changed
|
|
||||||
// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet
|
|
||||||
// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet
|
|
||||||
{
|
|
||||||
a->modeCcount = 0; //....zero the hit count
|
a->modeCcount = 0; //....zero the hit count
|
||||||
a->modeACflags &= ~MODEAC_MSG_MODEC_HIT;
|
a->modeACflags &= ~MODEAC_MSG_MODEC_HIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we received an altitude in a (non-mlat) DF17/18 squitter recently, ignore
|
|
||||||
// DF0/4/16/20 altitudes as single-bit errors can attribute them to the wrong
|
|
||||||
// aircraft
|
|
||||||
if ((a->bFlags & ~a->mlatFlags & MODES_ACFLAGS_ALTITUDE_VALID) &&
|
|
||||||
(now - a->seenAltitude) < 15000 &&
|
|
||||||
(a->bFlags & ~a->mlatFlags & MODES_ACFLAGS_LATLON_VALID) &&
|
|
||||||
(now - a->seenLatLon) < 15000 &&
|
|
||||||
mm->msgtype != 17 &&
|
|
||||||
mm->msgtype != 18) {
|
|
||||||
Modes.stats_current.suppressed_altitude_messages++;
|
|
||||||
} else {
|
|
||||||
a->altitude = mm->altitude;
|
a->altitude = mm->altitude;
|
||||||
a->modeC = (mm->altitude + 49) / 100;
|
a->altitude_modeC = modeC;
|
||||||
a->seenAltitude = now;
|
|
||||||
|
|
||||||
// reporting of HAE and baro altitudes is mutually exclusive
|
|
||||||
// so if we see a baro altitude, assume the HAE altitude is invalid
|
|
||||||
// we will recalculate it from baro + HAE delta below, where possible
|
|
||||||
a->bFlags &= ~MODES_ACFLAGS_ALTITUDE_HAE_VALID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a (new) HAE altitude has been received, copy it to the aircraft structure
|
if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source, now)) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_HAE_VALID) {
|
if (mm->squawk != a->squawk) {
|
||||||
a->altitude_hae = mm->altitude_hae;
|
a->modeAcount = 0; //....zero the hit count
|
||||||
|
|
||||||
// reporting of HAE and baro altitudes is mutually exclusive
|
|
||||||
// if you have both, you're meant to report baro and a HAE delta,
|
|
||||||
// so if we see explicit HAE then assume the delta is invalid too
|
|
||||||
a->bFlags &= ~(MODES_ACFLAGS_ALTITUDE_VALID | MODES_ACFLAGS_HAE_DELTA_VALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a (new) HAE/barometric difference has been received, copy it to the aircraft structure
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID) {
|
|
||||||
a->hae_delta = mm->hae_delta;
|
|
||||||
|
|
||||||
// reporting of HAE and baro altitudes is mutually exclusive
|
|
||||||
// if you have both, you're meant to report baro and a HAE delta,
|
|
||||||
// so if we see a HAE delta then assume the HAE altitude is invalid
|
|
||||||
a->bFlags &= ~MODES_ACFLAGS_ALTITUDE_HAE_VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a (new) SQUAWK has been received, copy it to the aircraft structure
|
|
||||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
|
||||||
if (a->modeA != mm->modeA) {
|
|
||||||
a->modeAcount = 0; // Squawk has changed, so zero the hit count
|
|
||||||
a->modeACflags &= ~MODEAC_MSG_MODEA_HIT;
|
a->modeACflags &= ~MODEAC_MSG_MODEA_HIT;
|
||||||
}
|
}
|
||||||
a->modeA = mm->modeA;
|
a->squawk = mm->squawk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a (new) HEADING has been received, copy it to the aircraft structure
|
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GNSS && accept_data(&a->altitude_gnss_valid, mm->source, now)) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
a->altitude_gnss = mm->altitude;
|
||||||
a->track = mm->heading;
|
|
||||||
a->seenTrack = now;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a (new) SPEED has been received, copy it to the aircraft structure
|
if (mm->gnss_delta_valid && accept_data(&a->gnss_delta_valid, mm->source, now)) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
a->gnss_delta = mm->gnss_delta;
|
||||||
a->speed = mm->velocity;
|
|
||||||
a->seenSpeed = now;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a (new) Vertical Descent rate has been received, copy it to the aircraft structure
|
if (mm->heading_valid && mm->heading_source == HEADING_TRUE && accept_data(&a->heading_valid, mm->source, now)) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {
|
a->heading = mm->heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->heading_valid && mm->heading_source == HEADING_MAGNETIC && accept_data(&a->heading_magnetic_valid, mm->source, now)) {
|
||||||
|
a->heading_magnetic = mm->heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED && accept_data(&a->speed_valid, mm->source, now)) {
|
||||||
|
a->speed = mm->speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->speed_valid && mm->speed_source == SPEED_IAS && accept_data(&a->speed_ias_valid, mm->source, now)) {
|
||||||
|
a->speed_ias = mm->speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->speed_valid && mm->speed_source == SPEED_TAS && accept_data(&a->speed_tas_valid, mm->source, now)) {
|
||||||
|
a->speed_tas = mm->speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->vert_rate_valid && accept_data(&a->vert_rate_valid, mm->source, now)) {
|
||||||
a->vert_rate = mm->vert_rate;
|
a->vert_rate = mm->vert_rate;
|
||||||
|
a->vert_rate_source = mm->vert_rate_source;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a (new) category has been received, copy it to the aircraft structure
|
if (mm->category_valid && accept_data(&a->category_valid, mm->source, now)) {
|
||||||
if (mm->bFlags & MODES_ACFLAGS_CATEGORY_VALID) {
|
|
||||||
a->category = mm->category;
|
a->category = mm->category;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the aircrafts a->bFlags to reflect the newly received mm->bFlags;
|
if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source, now)) {
|
||||||
a->bFlags |= mm->bFlags;
|
a->airground = mm->airground;
|
||||||
|
|
||||||
// If we have a baro altitude and a HAE delta from baro, calculate the HAE altitude
|
|
||||||
if ((a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) && (a->bFlags & MODES_ACFLAGS_HAE_DELTA_VALID)) {
|
|
||||||
a->altitude_hae = a->altitude + a->hae_delta;
|
|
||||||
a->bFlags |= MODES_ACFLAGS_ALTITUDE_HAE_VALID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update mlat flags. The mlat flags indicate which bits in bFlags
|
if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source, now)) {
|
||||||
// were last set based on a mlat-derived message.
|
memcpy(a->callsign, mm->callsign, sizeof(a->callsign));
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FROM_MLAT)
|
}
|
||||||
a->mlatFlags = (a->mlatFlags & a->bFlags) | mm->bFlags;
|
|
||||||
else
|
|
||||||
a->mlatFlags = (a->mlatFlags & a->bFlags) & ~mm->bFlags;
|
|
||||||
|
|
||||||
// Same for TIS-B
|
// CPR, even
|
||||||
if (mm->bFlags & MODES_ACFLAGS_FROM_TISB)
|
if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) {
|
||||||
a->tisbFlags = (a->tisbFlags & a->bFlags) | mm->bFlags;
|
a->cpr_even_airground = mm->airground;
|
||||||
else
|
a->cpr_even_lat = mm->cpr_lat;
|
||||||
a->tisbFlags = (a->tisbFlags & a->bFlags) & ~mm->bFlags;
|
a->cpr_even_lon = mm->cpr_lon;
|
||||||
|
a->cpr_even_nuc = mm->cpr_nucp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPR, odd
|
||||||
|
if (mm->cpr_valid && mm->cpr_odd && accept_data(&a->cpr_odd_valid, mm->source, now)) {
|
||||||
|
a->cpr_odd_airground = mm->airground;
|
||||||
|
a->cpr_odd_lat = mm->cpr_lat;
|
||||||
|
a->cpr_odd_lon = mm->cpr_lon;
|
||||||
|
a->cpr_odd_nuc = mm->cpr_nucp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now handle derived data
|
||||||
|
|
||||||
|
// derive GNSS if we have baro + delta
|
||||||
|
if (compare_validity(&a->altitude_valid, &a->altitude_gnss_valid, now) > 0 &&
|
||||||
|
compare_validity(&a->gnss_delta_valid, &a->altitude_gnss_valid, now) > 0) {
|
||||||
|
// Baro and delta are both more recent than GNSS, derive GNSS from baro + delta
|
||||||
|
a->altitude_gnss = a->altitude + a->gnss_delta;
|
||||||
|
combine_validity(&a->altitude_gnss_valid, &a->altitude_valid, &a->gnss_delta_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've got a new cprlat or cprlon
|
||||||
|
if (mm->cpr_valid) {
|
||||||
|
updatePosition(a, mm, now);
|
||||||
|
}
|
||||||
|
|
||||||
if (mm->msgtype == 32) {
|
if (mm->msgtype == 32) {
|
||||||
int flags = a->modeACflags;
|
int flags = a->modeACflags;
|
||||||
|
@ -670,9 +690,9 @@ static void trackUpdateAircraftModeA(struct aircraft *a)
|
||||||
if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) { // skip any fudged ICAO records
|
if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) { // skip any fudged ICAO records
|
||||||
|
|
||||||
// If both (a) and (b) have valid squawks...
|
// If both (a) and (b) have valid squawks...
|
||||||
if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) {
|
if (trackDataValid(&a->squawk_valid) && trackDataValid(&b->squawk_valid)) {
|
||||||
// ...check for Mode-A == Mode-S Squawk matches
|
// ...check for Mode-A == Mode-S Squawk matches
|
||||||
if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk
|
if (a->squawk == b->squawk) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk
|
||||||
b->modeAcount = a->messages;
|
b->modeAcount = a->messages;
|
||||||
b->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
b->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
||||||
a->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
a->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
||||||
|
@ -684,11 +704,11 @@ static void trackUpdateAircraftModeA(struct aircraft *a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If both (a) and (b) have valid altitudes...
|
// If both (a) and (b) have valid altitudes...
|
||||||
if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) {
|
if (trackDataValid(&a->altitude_valid) && trackDataValid(&b->altitude_valid)) {
|
||||||
// ... check for Mode-C == Mode-S Altitude matches
|
// ... check for Mode-C == Mode-S Altitude matches
|
||||||
if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude
|
if ( (a->altitude_modeC == b->altitude_modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude
|
||||||
|| (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft
|
|| (a->altitude_modeC == b->altitude_modeC + 1) // or this Mode-C - 100 ft
|
||||||
|| (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft
|
|| (a->altitude_modeC + 1 == b->altitude_modeC ) ) { // or this Mode-C + 100 ft
|
||||||
b->modeCcount = a->messages;
|
b->modeCcount = a->messages;
|
||||||
b->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
b->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
||||||
a->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
a->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
||||||
|
@ -748,10 +768,25 @@ static void trackRemoveStaleAircraft(uint64_t now)
|
||||||
prev->next = a->next; free(a); a = prev->next;
|
prev->next = a->next; free(a); a = prev->next;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && (now - a->seenLatLon) > TRACK_AIRCRAFT_POSITION_TTL) {
|
|
||||||
/* Position is too old and no longer valid */
|
#define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0)
|
||||||
a->bFlags &= ~(MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK);
|
EXPIRE(callsign);
|
||||||
}
|
EXPIRE(altitude);
|
||||||
|
EXPIRE(altitude_gnss);
|
||||||
|
EXPIRE(gnss_delta);
|
||||||
|
EXPIRE(speed);
|
||||||
|
EXPIRE(speed_ias);
|
||||||
|
EXPIRE(speed_tas);
|
||||||
|
EXPIRE(heading);
|
||||||
|
EXPIRE(heading_magnetic);
|
||||||
|
EXPIRE(vert_rate);
|
||||||
|
EXPIRE(squawk);
|
||||||
|
EXPIRE(category);
|
||||||
|
EXPIRE(airground);
|
||||||
|
EXPIRE(cpr_odd);
|
||||||
|
EXPIRE(cpr_even);
|
||||||
|
EXPIRE(position);
|
||||||
|
|
||||||
prev = a; a = a->next;
|
prev = a; a = a->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
151
track.h
151
track.h
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// track.h: aircraft state tracking prototypes
|
// track.h: aircraft state tracking prototypes
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk>
|
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
|
||||||
//
|
//
|
||||||
// This file is free software: you may copy, redistribute and/or modify it
|
// This file is free software: you may copy, redistribute and/or modify it
|
||||||
// under the terms of the GNU General Public License as published by the
|
// under the terms of the GNU General Public License as published by the
|
||||||
|
@ -59,63 +59,136 @@
|
||||||
/* Maximum validity of an aircraft position */
|
/* Maximum validity of an aircraft position */
|
||||||
#define TRACK_AIRCRAFT_POSITION_TTL 60000
|
#define TRACK_AIRCRAFT_POSITION_TTL 60000
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
datasource_t source; /* where the data came from */
|
||||||
|
uint64_t updated; /* when it arrived */
|
||||||
|
uint64_t stale; /* when it will become stale */
|
||||||
|
uint64_t expires; /* when it will expire */
|
||||||
|
} data_validity;
|
||||||
|
|
||||||
/* Structure used to describe the state of one tracked aircraft */
|
/* Structure used to describe the state of one tracked aircraft */
|
||||||
struct aircraft {
|
struct aircraft {
|
||||||
uint32_t addr; // ICAO address
|
uint32_t addr; // ICAO address
|
||||||
char flight[16]; // Flight number
|
addrtype_t addrtype; // highest priority address type seen for this aircraft
|
||||||
double signalLevel[8]; // Last 8 Signal Amplitudes
|
|
||||||
int signalNext; // next index of signalLevel to use
|
|
||||||
int altitude; // Altitude (Baro)
|
|
||||||
int altitude_hae; // Altitude (HAE)
|
|
||||||
int hae_delta; // Difference between HAE and Baro altitudes
|
|
||||||
int speed; // Velocity
|
|
||||||
int track; // Angle of flight
|
|
||||||
int vert_rate; // Vertical rate.
|
|
||||||
|
|
||||||
uint64_t seen; // Time (millis) at which the last packet was received
|
uint64_t seen; // Time (millis) at which the last packet was received
|
||||||
uint64_t seenLatLon; // Time (millis) at which lat, lon was measured
|
|
||||||
uint64_t seenAltitude; // Time (millis) at which altitude was measured
|
|
||||||
uint64_t seenSpeed; // Time (millis) at which speed was measured
|
|
||||||
uint64_t seenTrack; // Time (millis) at which track was measured
|
|
||||||
|
|
||||||
int mlatFlags; // Data derived from mlat messages
|
|
||||||
int tisbFlags; // Data derived from TIS-B messages
|
|
||||||
|
|
||||||
long messages; // Number of Mode S messages received
|
long messages; // Number of Mode S messages received
|
||||||
int modeA; // Squawk
|
|
||||||
int modeC; // Altitude
|
double signalLevel[8]; // Last 8 Signal Amplitudes
|
||||||
|
int signalNext; // next index of signalLevel to use
|
||||||
|
|
||||||
|
data_validity callsign_valid;
|
||||||
|
char callsign[9]; // Flight number
|
||||||
|
|
||||||
|
data_validity altitude_valid;
|
||||||
|
int altitude; // Altitude (Baro)
|
||||||
|
unsigned altitude_modeC; // (as a Mode C value)
|
||||||
|
|
||||||
|
data_validity altitude_gnss_valid;
|
||||||
|
int altitude_gnss; // Altitude (GNSS)
|
||||||
|
|
||||||
|
data_validity gnss_delta_valid;
|
||||||
|
int gnss_delta; // Difference between GNSS and Baro altitudes
|
||||||
|
|
||||||
|
data_validity speed_valid;
|
||||||
|
unsigned speed;
|
||||||
|
|
||||||
|
data_validity speed_ias_valid;
|
||||||
|
unsigned speed_ias;
|
||||||
|
|
||||||
|
data_validity speed_tas_valid;
|
||||||
|
unsigned speed_tas;
|
||||||
|
|
||||||
|
data_validity heading_valid;
|
||||||
|
unsigned heading; // Heading (OK it's really the track)
|
||||||
|
|
||||||
|
data_validity heading_magnetic_valid;
|
||||||
|
unsigned heading_magnetic; // Heading
|
||||||
|
|
||||||
|
data_validity vert_rate_valid;
|
||||||
|
int vert_rate; // Vertical rate
|
||||||
|
altitude_source_t vert_rate_source;
|
||||||
|
|
||||||
|
data_validity squawk_valid;
|
||||||
|
unsigned squawk; // Squawk
|
||||||
|
|
||||||
|
data_validity category_valid;
|
||||||
|
unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte
|
||||||
|
|
||||||
|
data_validity airground_valid;
|
||||||
|
airground_t airground; // air/ground status
|
||||||
|
|
||||||
|
data_validity cpr_odd_valid; // Last seen even CPR message
|
||||||
|
airground_t cpr_odd_airground;
|
||||||
|
unsigned cpr_odd_lat;
|
||||||
|
unsigned cpr_odd_lon;
|
||||||
|
unsigned cpr_odd_nuc;
|
||||||
|
|
||||||
|
data_validity cpr_even_valid; // Last seen odd CPR message
|
||||||
|
airground_t cpr_even_airground;
|
||||||
|
unsigned cpr_even_lat;
|
||||||
|
unsigned cpr_even_lon;
|
||||||
|
unsigned cpr_even_nuc;
|
||||||
|
|
||||||
|
data_validity position_valid;
|
||||||
|
double lat, lon; // Coordinated obtained from CPR encoded data
|
||||||
|
unsigned pos_nuc; // NUCp of last computed position
|
||||||
|
|
||||||
long modeAcount; // Mode A Squawk hit Count
|
long modeAcount; // Mode A Squawk hit Count
|
||||||
long modeCcount; // Mode C Altitude hit Count
|
long modeCcount; // Mode C Altitude hit Count
|
||||||
int modeACflags; // Flags for mode A/C recognition
|
int modeACflags; // Flags for mode A/C recognition
|
||||||
|
|
||||||
int fatsv_emitted_altitude; // last FA emitted altitude
|
int fatsv_emitted_altitude; // last FA emitted altitude
|
||||||
int fatsv_emitted_track; // last FA emitted track
|
int fatsv_emitted_altitude_gnss; // -"- GNSS altitude
|
||||||
int fatsv_emitted_speed; // last FA emitted speed
|
int fatsv_emitted_heading; // -"- true track
|
||||||
|
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
|
||||||
|
|
||||||
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted
|
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 aircraft *next; // Next aircraft in our linked list
|
||||||
|
|
||||||
struct modesMessage first_message; // A copy of the first message we received for this aircraft.
|
struct modesMessage first_message; // A copy of the first message we received for this aircraft.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* is this bit of data valid? */
|
||||||
|
static inline int trackDataValid(const data_validity *v)
|
||||||
|
{
|
||||||
|
return (v->source != SOURCE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .. with these constraints? */
|
||||||
|
static inline int trackDataValidEx(const data_validity *v,
|
||||||
|
uint64_t now,
|
||||||
|
uint64_t maxAge,
|
||||||
|
datasource_t minSource)
|
||||||
|
{
|
||||||
|
if (v->source == SOURCE_INVALID)
|
||||||
|
return 0;
|
||||||
|
if (v->source < minSource)
|
||||||
|
return 0;
|
||||||
|
if (v->updated < now && (now - v->updated) > maxAge)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* what's the age of this data? */
|
||||||
|
static inline uint64_t trackDataAge(const data_validity *v,
|
||||||
|
uint64_t now)
|
||||||
|
{
|
||||||
|
if (v->source == SOURCE_INVALID)
|
||||||
|
return ~(uint64_t)0;
|
||||||
|
if (v->updated >= now)
|
||||||
|
return 0;
|
||||||
|
return (now - v->updated);
|
||||||
|
}
|
||||||
|
|
||||||
/* Update aircraft state from data in the provided mesage.
|
/* Update aircraft state from data in the provided mesage.
|
||||||
* Return the tracked aircraft.
|
* Return the tracked aircraft.
|
||||||
|
|
Loading…
Reference in a new issue