More WIP
This commit is contained in:
parent
6986b3847f
commit
161d2d8ded
38
comm_b.c
38
comm_b.c
|
@ -374,51 +374,49 @@ static int decodeBDS40(struct modesMessage *mm, bool store)
|
||||||
if (store) {
|
if (store) {
|
||||||
mm->commb_format = COMMB_VERTICAL_INTENT;
|
mm->commb_format = COMMB_VERTICAL_INTENT;
|
||||||
|
|
||||||
mm->intent.valid = 1;
|
|
||||||
|
|
||||||
if (mcp_valid) {
|
if (mcp_valid) {
|
||||||
mm->intent.mcp_altitude_valid = 1;
|
mm->nav.mcp_altitude_valid = 1;
|
||||||
mm->intent.mcp_altitude = mcp_alt;
|
mm->nav.mcp_altitude = mcp_alt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fms_valid) {
|
if (fms_valid) {
|
||||||
mm->intent.fms_altitude_valid = 1;
|
mm->nav.fms_altitude_valid = 1;
|
||||||
mm->intent.fms_altitude = fms_alt;
|
mm->nav.fms_altitude = fms_alt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baro_valid) {
|
if (baro_valid) {
|
||||||
mm->intent.alt_setting_valid = 1;
|
mm->nav.qnh_valid = 1;
|
||||||
mm->intent.alt_setting = baro_setting;
|
mm->nav.qnh = baro_setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode_valid) {
|
if (mode_valid) {
|
||||||
mm->intent.modes_valid = 1;
|
mm->nav.modes_valid = 1;
|
||||||
mm->intent.modes =
|
mm->nav.modes =
|
||||||
((mode_raw & 4) ? INTENT_MODE_VNAV : 0) |
|
((mode_raw & 4) ? NAV_MODE_VNAV : 0) |
|
||||||
((mode_raw & 2) ? INTENT_MODE_ALT_HOLD : 0) |
|
((mode_raw & 2) ? NAV_MODE_ALT_HOLD : 0) |
|
||||||
((mode_raw & 1) ? INTENT_MODE_APPROACH : 0);
|
((mode_raw & 1) ? NAV_MODE_APPROACH : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_valid) {
|
if (source_valid) {
|
||||||
switch (source_raw) {
|
switch (source_raw) {
|
||||||
case 0:
|
case 0:
|
||||||
mm->intent.altitude_source = INTENT_ALT_UNKNOWN;
|
mm->nav.altitude_source = NAV_ALT_UNKNOWN;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
mm->intent.altitude_source = INTENT_ALT_AIRCRAFT;
|
mm->nav.altitude_source = NAV_ALT_AIRCRAFT;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
mm->intent.altitude_source = INTENT_ALT_MCP;
|
mm->nav.altitude_source = NAV_ALT_MCP;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
mm->intent.altitude_source = INTENT_ALT_FMS;
|
mm->nav.altitude_source = NAV_ALT_FMS;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mm->intent.altitude_source = INTENT_ALT_INVALID;
|
mm->nav.altitude_source = NAV_ALT_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mm->intent.altitude_source = INTENT_ALT_INVALID;
|
mm->nav.altitude_source = NAV_ALT_INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +568,7 @@ static int decodeBDS50(struct modesMessage *mm, bool store)
|
||||||
|
|
||||||
if (gs_valid) {
|
if (gs_valid) {
|
||||||
mm->gs_valid = 1;
|
mm->gs_valid = 1;
|
||||||
mm->gs = gs;
|
mm->gs.v0 = mm->gs.v2 = gs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (track_rate_valid) {
|
if (track_rate_valid) {
|
||||||
|
|
94
dump1090.h
94
dump1090.h
|
@ -178,7 +178,7 @@ typedef enum {
|
||||||
} airground_t;
|
} airground_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SIL_PER_SAMPLE, SIL_PER_HOUR
|
SIL_INVALID, SIL_PER_SAMPLE, SIL_PER_HOUR
|
||||||
} sil_type_t;
|
} sil_type_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -186,11 +186,12 @@ typedef enum {
|
||||||
} cpr_type_t;
|
} cpr_type_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
HEADING_INVALID, // Not set
|
||||||
HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north
|
HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north
|
||||||
HEADING_TRUE, // Heading, degrees clockwise from true north
|
HEADING_TRUE, // Heading, degrees clockwise from true north
|
||||||
HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north
|
HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north
|
||||||
HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus
|
HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus
|
||||||
HEADING_TRACK_OR_HEADING // HEADING_GROUND_TRACK or HEADING_REF_DIR depending on the TAH bit in opstatus
|
HEADING_TRACK_OR_HEADING // GROUND_TRACK / MAGNETIC / TRUE depending on the TAH bit in opstatus
|
||||||
} heading_type_t;
|
} heading_type_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -206,12 +207,13 @@ typedef enum {
|
||||||
} commb_format_t;
|
} commb_format_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
INTENT_MODE_AUTOPILOT = 1,
|
NAV_MODE_AUTOPILOT = 1,
|
||||||
INTENT_MODE_VNAV = 2,
|
NAV_MODE_VNAV = 2,
|
||||||
INTENT_MODE_ALT_HOLD = 4,
|
NAV_MODE_ALT_HOLD = 4,
|
||||||
INTENT_MODE_APPROACH = 8,
|
NAV_MODE_APPROACH = 8,
|
||||||
INTENT_MODE_LNAV = 16
|
NAV_MODE_LNAV = 16,
|
||||||
} intent_modes_t;
|
NAV_MODE_TCAS = 32
|
||||||
|
} nav_modes_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
|
||||||
|
|
||||||
|
@ -422,7 +424,8 @@ struct modesMessage {
|
||||||
unsigned char MV[7];
|
unsigned char MV[7];
|
||||||
|
|
||||||
// Decoded data
|
// Decoded data
|
||||||
unsigned altitude_valid : 1;
|
unsigned altitude_baro_valid : 1;
|
||||||
|
unsigned altitude_geom_valid : 1;
|
||||||
unsigned track_valid : 1;
|
unsigned track_valid : 1;
|
||||||
unsigned track_rate_valid : 1;
|
unsigned track_rate_valid : 1;
|
||||||
unsigned heading_valid : 1;
|
unsigned heading_valid : 1;
|
||||||
|
@ -453,10 +456,13 @@ struct modesMessage {
|
||||||
|
|
||||||
commb_format_t commb_format; // Inferred format of a comm-b message
|
commb_format_t commb_format; // Inferred format of a comm-b message
|
||||||
|
|
||||||
// valid if altitude_valid:
|
// valid if altitude_baro_valid:
|
||||||
int altitude; // Altitude in either feet or meters
|
int altitude_baro; // Altitude in either feet or meters
|
||||||
altitude_unit_t altitude_unit; // the unit used for altitude
|
altitude_unit_t altitude_baro_unit; // the unit used for altitude
|
||||||
altitude_source_t altitude_source; // whether the altitude is a barometric altitude or a geometric height
|
|
||||||
|
// valid if altitude_geom_valid:
|
||||||
|
int altitude_geom; // Altitude in either feet or meters
|
||||||
|
altitude_unit_t altitude_geom_unit; // the unit used for altitude
|
||||||
|
|
||||||
// following fields are valid if the corresponding _valid field is set:
|
// following fields are valid if the corresponding _valid field is set:
|
||||||
int geom_delta; // Difference between geometric and baro alt
|
int geom_delta; // Difference between geometric and baro alt
|
||||||
|
@ -464,7 +470,14 @@ struct modesMessage {
|
||||||
heading_type_t heading_type;// how to interpret 'track_or_heading'
|
heading_type_t heading_type;// how to interpret 'track_or_heading'
|
||||||
float track_rate; // Rate of change of track, degrees/second
|
float track_rate; // Rate of change of track, degrees/second
|
||||||
float roll; // Roll, degrees, negative is left roll
|
float roll; // Roll, degrees, negative is left roll
|
||||||
unsigned gs; // Groundspeed, kts, reported directly or computed from from EW and NS velocity
|
struct {
|
||||||
|
// Groundspeed, kts, reported directly or computed from from EW and NS velocity
|
||||||
|
// For surface movement, this has different interpretations for v0 and v2; both
|
||||||
|
// fields are populated. The tracking layer will update "gs.selected".
|
||||||
|
float v0;
|
||||||
|
float v2;
|
||||||
|
float selected;
|
||||||
|
} gs;
|
||||||
unsigned ias; // Indicated airspeed, kts
|
unsigned ias; // Indicated airspeed, kts
|
||||||
unsigned tas; // True airspeed, kts
|
unsigned tas; // True airspeed, kts
|
||||||
double mach; // Mach number
|
double mach; // Mach number
|
||||||
|
@ -485,6 +498,36 @@ struct modesMessage {
|
||||||
// valid if cpr_decoded:
|
// valid if cpr_decoded:
|
||||||
double decoded_lat;
|
double decoded_lat;
|
||||||
double decoded_lon;
|
double decoded_lon;
|
||||||
|
unsigned decoded_nic;
|
||||||
|
unsigned decoded_rc;
|
||||||
|
|
||||||
|
// various integrity/accuracy things
|
||||||
|
struct {
|
||||||
|
unsigned nic_a_valid : 1;
|
||||||
|
unsigned nic_b_valid : 1;
|
||||||
|
unsigned nic_c_valid : 1;
|
||||||
|
unsigned nic_baro_valid : 1;
|
||||||
|
unsigned nac_p_valid : 1;
|
||||||
|
unsigned nac_v_valid : 1;
|
||||||
|
unsigned sil_valid : 1;
|
||||||
|
unsigned gva_valid : 1;
|
||||||
|
unsigned sda_valid : 1;
|
||||||
|
|
||||||
|
unsigned nic_a : 1; // if nic_a_valid
|
||||||
|
unsigned nic_b : 1; // if nic_b_valid
|
||||||
|
unsigned nic_c : 1; // if nic_c_valid
|
||||||
|
unsigned nic_baro : 1; // if nic_baro_valid
|
||||||
|
|
||||||
|
unsigned nac_p : 4; // if nac_p_valid
|
||||||
|
unsigned nac_v : 3; // if nac_v_valid
|
||||||
|
|
||||||
|
unsigned sil : 2; // if sil_valid
|
||||||
|
sil_type_t sil_type; // if sil_valid
|
||||||
|
|
||||||
|
unsigned gva : 2; // if gva_valid
|
||||||
|
|
||||||
|
unsigned sda : 2; // if sda_valid
|
||||||
|
} accuracy;
|
||||||
|
|
||||||
// Operational Status
|
// Operational Status
|
||||||
struct {
|
struct {
|
||||||
|
@ -495,7 +538,6 @@ struct modesMessage {
|
||||||
unsigned om_ident : 1;
|
unsigned om_ident : 1;
|
||||||
unsigned om_atc : 1;
|
unsigned om_atc : 1;
|
||||||
unsigned om_saf : 1;
|
unsigned om_saf : 1;
|
||||||
unsigned om_sda : 2;
|
|
||||||
|
|
||||||
unsigned cc_acas : 1;
|
unsigned cc_acas : 1;
|
||||||
unsigned cc_cdti : 1;
|
unsigned cc_cdti : 1;
|
||||||
|
@ -506,17 +548,8 @@ struct modesMessage {
|
||||||
unsigned cc_uat_in : 1;
|
unsigned cc_uat_in : 1;
|
||||||
unsigned cc_poa : 1;
|
unsigned cc_poa : 1;
|
||||||
unsigned cc_b2_low : 1;
|
unsigned cc_b2_low : 1;
|
||||||
unsigned cc_nac_v : 3;
|
|
||||||
unsigned cc_nic_supp_c : 1;
|
|
||||||
unsigned cc_lw_valid : 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;
|
|
||||||
heading_type_t tah;
|
heading_type_t tah;
|
||||||
heading_type_t hrd;
|
heading_type_t hrd;
|
||||||
|
|
||||||
|
@ -528,23 +561,22 @@ struct modesMessage {
|
||||||
// Target State & Status (ADS-B V2 only)
|
// Target State & Status (ADS-B V2 only)
|
||||||
// Comm-B BDS4,0 Vertical Intent
|
// Comm-B BDS4,0 Vertical Intent
|
||||||
struct {
|
struct {
|
||||||
unsigned valid : 1;
|
|
||||||
|
|
||||||
unsigned heading_valid : 1;
|
unsigned heading_valid : 1;
|
||||||
unsigned fms_altitude_valid : 1;
|
unsigned fms_altitude_valid : 1;
|
||||||
unsigned mcp_altitude_valid : 1;
|
unsigned mcp_altitude_valid : 1;
|
||||||
unsigned alt_setting_valid : 1;
|
unsigned qnh_valid : 1;
|
||||||
unsigned modes_valid : 1;
|
unsigned modes_valid : 1;
|
||||||
|
|
||||||
float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended)
|
float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended)
|
||||||
|
heading_type_t heading_type;
|
||||||
unsigned fms_altitude; // FMS selected altitude
|
unsigned fms_altitude; // FMS selected altitude
|
||||||
unsigned mcp_altitude; // MCP/FCU selected altitude
|
unsigned mcp_altitude; // MCP/FCU selected altitude
|
||||||
float alt_setting; // altimeter setting (QFE or QNH/QNE), millibars
|
float qnh; // altimeter setting (QFE or QNH/QNE), millibars
|
||||||
|
|
||||||
enum { INTENT_ALT_INVALID, INTENT_ALT_UNKNOWN, INTENT_ALT_AIRCRAFT, INTENT_ALT_MCP, INTENT_ALT_FMS } altitude_source;
|
enum { NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS } altitude_source;
|
||||||
|
|
||||||
intent_modes_t modes;
|
nav_modes_t modes;
|
||||||
} intent;
|
} nav;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This one needs modesMessage:
|
// This one needs modesMessage:
|
||||||
|
|
|
@ -162,8 +162,8 @@ void interactiveShowData(void) {
|
||||||
snprintf(strFl, 7," grnd");
|
snprintf(strFl, 7," grnd");
|
||||||
} else if (Modes.use_gnss && trackDataValid(&a->altitude_geom_valid)) {
|
} else if (Modes.use_gnss && trackDataValid(&a->altitude_geom_valid)) {
|
||||||
snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_geom));
|
snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_geom));
|
||||||
} else if (trackDataValid(&a->altitude_valid)) {
|
} else if (trackDataValid(&a->altitude_baro_valid)) {
|
||||||
snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude));
|
snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude_baro));
|
||||||
}
|
}
|
||||||
|
|
||||||
mvprintw(row, 0, "%s%06X %-4s %-4s %-8s %6s %3s %3s %7s %8s %5.1f %5d %2.0f",
|
mvprintw(row, 0, "%s%06X %-4s %-4s %-8s %6s %3s %3s %7s %8s %5.1f %5d %2.0f",
|
||||||
|
|
|
@ -146,10 +146,9 @@ void decodeModeAMessage(struct modesMessage *mm, int ModeA)
|
||||||
if (!mm->spi) {
|
if (!mm->spi) {
|
||||||
int modeC = modeAToModeC(ModeA);
|
int modeC = modeAToModeC(ModeA);
|
||||||
if (modeC != INVALID_ALTITUDE) {
|
if (modeC != INVALID_ALTITUDE) {
|
||||||
mm->altitude = modeC * 100;
|
mm->altitude_baro = modeC * 100;
|
||||||
mm->altitude_unit = UNIT_FEET;
|
mm->altitude_baro_unit = UNIT_FEET;
|
||||||
mm->altitude_source = ALTITUDE_BARO;
|
mm->altitude_baro_valid = 1;
|
||||||
mm->altitude_valid = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
590
mode_s.c
590
mode_s.c
|
@ -177,23 +177,50 @@ static int decodeAC12Field(int AC12Field, altitude_unit_t *unit) {
|
||||||
//
|
//
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
//
|
//
|
||||||
// Decode the 7 bit ground movement field PWL exponential style scale
|
// Decode the 7 bit ground movement field PWL exponential style scale (ADS-B v2)
|
||||||
//
|
//
|
||||||
static unsigned decodeMovementField(unsigned movement) {
|
static float decodeMovementFieldV2(unsigned movement) {
|
||||||
int gspeed;
|
|
||||||
|
|
||||||
// Note : movement codes 0,125,126,127 are all invalid, but they are
|
// Note : movement codes 0,125,126,127 are all invalid, but they are
|
||||||
// trapped for before this function is called.
|
// trapped for before this function is called.
|
||||||
|
|
||||||
if (movement > 123) gspeed = 199; // > 175kt
|
// Each movement value is a range of speeds;
|
||||||
else if (movement > 108) gspeed = ((movement - 108) * 5) + 100;
|
// we return the midpoint of the range (rounded to the nearest integer)
|
||||||
else if (movement > 93) gspeed = ((movement - 93) * 2) + 70;
|
if (movement >= 125) return 0; // invalid
|
||||||
else if (movement > 38) gspeed = ((movement - 38) ) + 15;
|
else if (movement == 124) return 180; // gs > 175kt, pick a value..
|
||||||
else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2;
|
else if (movement >= 109) return 100 + (movement - 109 + 0.5) * 5; // 100 < gs <= 175 in 5kt steps
|
||||||
else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1;
|
else if (movement >= 94) return 70 + (movement - 94 + 0.5) * 2; // 70 < gs <= 100 in 2kt steps
|
||||||
else gspeed = 0;
|
else if (movement >= 39) return 15 + (movement - 39 + 0.5) * 1; // 15 < gs <= 70 in 1kt steps
|
||||||
|
else if (movement >= 13) return 2 + (movement - 13 + 0.5) * 0.50; // 2 < gs <= 15 in 0.5kt steps
|
||||||
|
else if (movement >= 9) return 1 + (movement - 9 + 0.5) * 0.25; // 1 < gs <= 2 in 0.25kt steps
|
||||||
|
else if (movement >= 3) return 0.125 + (movement - 3 + 0.5) * 0.875 / 6; // 0.125 < gs <= 1 in 0.875/6 kt step
|
||||||
|
else if (movement >= 2) return 0.125 / 2; // 0 < gs <= 0.125
|
||||||
|
// 1: stopped, gs = 0
|
||||||
|
// 0: no data
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return (gspeed);
|
//
|
||||||
|
//=========================================================================
|
||||||
|
//
|
||||||
|
// Decode the 7 bit ground movement field PWL exponential style scale (ADS-B v0)
|
||||||
|
//
|
||||||
|
static float decodeMovementFieldV0(unsigned movement) {
|
||||||
|
// Note : movement codes 0,125,126,127 are all invalid, but they are
|
||||||
|
// trapped for before this function is called.
|
||||||
|
|
||||||
|
// Each movement value is a range of speeds;
|
||||||
|
// we return the midpoint of the range
|
||||||
|
if (movement >= 125) return 0; // invalid
|
||||||
|
else if (movement == 124) return 180; // gs >= 175kt, pick a value..
|
||||||
|
else if (movement >= 109) return 100 + (movement - 109 + 0.5) * 5; // 100 < gs <= 175 in 5kt steps
|
||||||
|
else if (movement >= 94) return 70 + (movement - 94 + 0.5) * 2; // 70 < gs <= 100 in 2kt steps
|
||||||
|
else if (movement >= 39) return 15 + (movement - 39 + 0.5) * 1; // 15 < gs <= 70 in 1kt steps
|
||||||
|
else if (movement >= 13) return 2 + (movement - 13 + 0.5) * 0.50; // 2 < gs <= 15 in 0.5kt steps
|
||||||
|
else if (movement >= 9) return 1 + (movement - 9 + 0.5) * 0.25; // 1 < gs <= 2 in 0.25kt steps
|
||||||
|
else if (movement >= 2) return 0.125 + (movement - 3 + 0.5) * 0.125; // 0.125 < gs <= 1 in 0.125kt step
|
||||||
|
// 1: stopped, gs < 0.125kt
|
||||||
|
// 0: no data
|
||||||
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct a decoded native-endian Address Announced field
|
// Correct a decoded native-endian Address Announced field
|
||||||
|
@ -505,10 +532,9 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg)
|
||||||
if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 16 || mm->msgtype == 20) {
|
if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 16 || mm->msgtype == 20) {
|
||||||
mm->AC = getbits(msg, 20, 32);
|
mm->AC = getbits(msg, 20, 32);
|
||||||
if (mm->AC) { // Only attempt to decode if a valid (non zero) altitude is present
|
if (mm->AC) { // Only attempt to decode if a valid (non zero) altitude is present
|
||||||
mm->altitude = decodeAC13Field(mm->AC, &mm->altitude_unit);
|
mm->altitude_baro = decodeAC13Field(mm->AC, &mm->altitude_baro_unit);
|
||||||
if (mm->altitude != INVALID_ALTITUDE)
|
if (mm->altitude_baro != INVALID_ALTITUDE)
|
||||||
mm->altitude_valid = 1;
|
mm->altitude_baro_valid = 1;
|
||||||
mm->altitude_source = ALTITUDE_BARO;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,30 +755,31 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
|
||||||
// Airborne Velocity Message
|
// Airborne Velocity Message
|
||||||
unsigned char *me = mm->ME;
|
unsigned char *me = mm->ME;
|
||||||
|
|
||||||
|
// 1-5: ME type
|
||||||
|
// 6-8: ME subtype
|
||||||
mm->mesub = getbits(me, 6, 8);
|
mm->mesub = getbits(me, 6, 8);
|
||||||
|
|
||||||
if (check_imf && getbit(me, 9))
|
|
||||||
setIMF(mm);
|
|
||||||
|
|
||||||
if (mm->mesub < 1 || mm->mesub > 4)
|
if (mm->mesub < 1 || mm->mesub > 4)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unsigned vert_rate = getbits(me, 38, 46);
|
// 9: IMF or Intent Change
|
||||||
unsigned vert_rate_is_geom = getbit(me, 36);
|
if (check_imf && getbit(me, 9))
|
||||||
if (vert_rate) {
|
setIMF(mm);
|
||||||
int rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64);
|
|
||||||
if (vert_rate_is_geom) {
|
|
||||||
mm->geom_rate = rate;
|
|
||||||
mm->geom_rate_valid = 1;
|
|
||||||
} else {
|
|
||||||
mm->baro_rate = rate;
|
|
||||||
mm->baro_rate_valid = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 10: reserved
|
||||||
|
|
||||||
|
// 11-13: NACv (NUCr in v0, maps directly to NACv in v2)
|
||||||
|
mm->accuracy.nac_v_valid = 1;
|
||||||
|
mm->accuracy.nac_v = getbits(me, 11, 13);
|
||||||
|
|
||||||
|
// 14-35: speed/velocity depending on subtype
|
||||||
switch (mm->mesub) {
|
switch (mm->mesub) {
|
||||||
case 1: case 2:
|
case 1: case 2:
|
||||||
{
|
{
|
||||||
|
// 14: E/W direction
|
||||||
|
// 15-24: E/W speed
|
||||||
|
// 25: N/S direction
|
||||||
|
// 26-35: N/S speed
|
||||||
unsigned ew_raw = getbits(me, 15, 24);
|
unsigned ew_raw = getbits(me, 15, 24);
|
||||||
unsigned ns_raw = getbits(me, 26, 35);
|
unsigned ns_raw = getbits(me, 26, 35);
|
||||||
|
|
||||||
|
@ -761,10 +788,10 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
|
||||||
int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mm->mesub == 2) ? 4 : 1);
|
int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mm->mesub == 2) ? 4 : 1);
|
||||||
|
|
||||||
// Compute velocity and angle from the two speed components
|
// Compute velocity and angle from the two speed components
|
||||||
mm->gs = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5);
|
mm->gs.v0 = mm->gs.v2 = mm->gs.selected = sqrtf((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5);
|
||||||
mm->gs_valid = 1;
|
mm->gs_valid = 1;
|
||||||
|
|
||||||
if (mm->gs) {
|
if (mm->gs.selected > 0) {
|
||||||
float ground_track = atan2(ew_vel, ns_vel) * 180.0 / M_PI;
|
float ground_track = atan2(ew_vel, ns_vel) * 180.0 / M_PI;
|
||||||
// We don't want negative values but a 0-360 scale
|
// We don't want negative values but a 0-360 scale
|
||||||
if (ground_track < 0)
|
if (ground_track < 0)
|
||||||
|
@ -779,6 +806,16 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
|
||||||
|
|
||||||
case 3: case 4:
|
case 3: case 4:
|
||||||
{
|
{
|
||||||
|
// 14: heading status
|
||||||
|
// 15-24: heading
|
||||||
|
if (getbit(me, 14)) {
|
||||||
|
mm->heading_valid = 1;
|
||||||
|
mm->heading = getbits(me, 15, 24) * 360.0 / 1024.0;
|
||||||
|
mm->heading_type = HEADING_MAGNETIC_OR_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25: airspeed type
|
||||||
|
// 26-35: airspeed
|
||||||
unsigned airspeed = getbits(me, 26, 35);
|
unsigned airspeed = getbits(me, 26, 35);
|
||||||
if (airspeed) {
|
if (airspeed) {
|
||||||
unsigned speed = (airspeed - 1) * (mm->mesub == 4 ? 4 : 1);
|
unsigned speed = (airspeed - 1) * (mm->mesub == 4 ? 4 : 1);
|
||||||
|
@ -791,15 +828,30 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getbit(me, 14)) {
|
|
||||||
mm->heading_valid = 1;
|
|
||||||
mm->heading = getbits(me, 15, 24) * 360.0 / 1024.0;
|
|
||||||
mm->heading_type = HEADING_MAGNETIC_OR_TRUE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 36: vert rate source
|
||||||
|
// 37: vert rate sign
|
||||||
|
// 38-46: vert rate magnitude
|
||||||
|
unsigned vert_rate = getbits(me, 38, 46);
|
||||||
|
unsigned vert_rate_is_geom = getbit(me, 36);
|
||||||
|
if (vert_rate) {
|
||||||
|
int rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64);
|
||||||
|
if (vert_rate_is_geom) {
|
||||||
|
mm->geom_rate = rate;
|
||||||
|
mm->geom_rate_valid = 1;
|
||||||
|
} else {
|
||||||
|
mm->baro_rate = rate;
|
||||||
|
mm->baro_rate_valid = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 47-48: reserved
|
||||||
|
|
||||||
|
// 49: baro/geom delta sign
|
||||||
|
// 50-56: baro/geom delta magnitude
|
||||||
unsigned raw_delta = getbits(me, 50, 56);
|
unsigned raw_delta = getbits(me, 50, 56);
|
||||||
if (raw_delta) {
|
if (raw_delta) {
|
||||||
mm->geom_delta_valid = 1;
|
mm->geom_delta_valid = 1;
|
||||||
|
@ -812,28 +864,37 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf)
|
||||||
// Surface position and movement
|
// Surface position and movement
|
||||||
unsigned char *me = mm->ME;
|
unsigned char *me = mm->ME;
|
||||||
|
|
||||||
if (check_imf && getbit(me, 21))
|
|
||||||
setIMF(mm);
|
|
||||||
|
|
||||||
mm->airground = AG_GROUND; // definitely.
|
mm->airground = AG_GROUND; // definitely.
|
||||||
mm->cpr_lat = getbits(me, 23, 39);
|
|
||||||
mm->cpr_lon = getbits(me, 40, 56);
|
|
||||||
mm->cpr_odd = getbit(me, 22);
|
|
||||||
mm->cpr_nucp = (14 - mm->metype);
|
|
||||||
mm->cpr_valid = 1;
|
mm->cpr_valid = 1;
|
||||||
mm->cpr_type = CPR_SURFACE;
|
mm->cpr_type = CPR_SURFACE;
|
||||||
|
|
||||||
|
// 6-12: Movement
|
||||||
unsigned movement = getbits(me, 6, 12);
|
unsigned movement = getbits(me, 6, 12);
|
||||||
if (movement > 0 && movement < 125) {
|
if (movement > 0 && movement < 125) {
|
||||||
mm->gs_valid = 1;
|
mm->gs_valid = 1;
|
||||||
mm->gs = decodeMovementField(movement);
|
mm->gs.selected = mm->gs.v0 = decodeMovementFieldV0(movement); // assumed v0 until told otherwise
|
||||||
|
mm->gs.v2 = decodeMovementFieldV2(movement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 13: Heading/track status
|
||||||
|
// 14-20: Heading/track
|
||||||
if (getbit(me, 13)) {
|
if (getbit(me, 13)) {
|
||||||
mm->heading_valid = 1;
|
mm->heading_valid = 1;
|
||||||
mm->heading = getbits(me, 14, 20) * 360.0 / 128.0;
|
mm->heading = getbits(me, 14, 20) * 360.0 / 128.0;
|
||||||
mm->heading_type = HEADING_TRACK_OR_HEADING;
|
mm->heading_type = HEADING_TRACK_OR_HEADING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 21: IMF or T flag
|
||||||
|
if (check_imf && getbit(me, 21))
|
||||||
|
setIMF(mm);
|
||||||
|
|
||||||
|
// 22: F flag (odd/even)
|
||||||
|
mm->cpr_odd = getbit(me, 22);
|
||||||
|
|
||||||
|
// 23-39: CPR encoded latitude
|
||||||
|
mm->cpr_lat = getbits(me, 23, 39);
|
||||||
|
// 40-56: CPR encoded longitude
|
||||||
|
mm->cpr_lon = getbits(me, 40, 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf)
|
static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf)
|
||||||
|
@ -841,20 +902,34 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf)
|
||||||
// Airborne position and altitude
|
// Airborne position and altitude
|
||||||
unsigned char *me = mm->ME;
|
unsigned char *me = mm->ME;
|
||||||
|
|
||||||
if (check_imf && getbit(me, 8))
|
// 6-7: surveillance status
|
||||||
setIMF(mm);
|
// 8: IMF or NIC supplement-B
|
||||||
|
|
||||||
|
if (check_imf) {
|
||||||
|
if (getbit(me, 8))
|
||||||
|
setIMF(mm);
|
||||||
|
} else {
|
||||||
|
// NIC-B (v2) or SAF (v0/v1)
|
||||||
|
mm->accuracy.nic_b_valid = 1;
|
||||||
|
mm->accuracy.nic_b = getbit(me, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9-20: altitude
|
||||||
unsigned AC12Field = getbits(me, 9, 20);
|
unsigned AC12Field = getbits(me, 9, 20);
|
||||||
|
|
||||||
if (mm->metype == 0) {
|
if (mm->metype == 0) {
|
||||||
mm->cpr_nucp = 0;
|
// no position information
|
||||||
} else {
|
} else {
|
||||||
// Catch some common failure modes and don't mark them as valid
|
// 21: T flag (UTC sync or not)
|
||||||
// (so they won't be used for positioning)
|
// 22: F flag (odd or even)
|
||||||
|
// 23-39: CPR encoded latitude
|
||||||
|
// 40-56: CPR encoded longitude
|
||||||
|
|
||||||
mm->cpr_lat = getbits(me, 23, 39);
|
mm->cpr_lat = getbits(me, 23, 39);
|
||||||
mm->cpr_lon = getbits(me, 40, 56);
|
mm->cpr_lon = getbits(me, 40, 56);
|
||||||
|
|
||||||
|
// Catch some common failure modes and don't mark them as valid
|
||||||
|
// (so they won't be used for positioning)
|
||||||
if (AC12Field == 0 && mm->cpr_lon == 0 && (mm->cpr_lat & 0x0fff) == 0 && mm->metype == 15) {
|
if (AC12Field == 0 && mm->cpr_lon == 0 && (mm->cpr_lat & 0x0fff) == 0 && mm->metype == 15) {
|
||||||
// Seen from at least:
|
// Seen from at least:
|
||||||
// 400F3F (Eurocopter ECC155 B1) - Bristow Helicopters
|
// 400F3F (Eurocopter ECC155 B1) - Bristow Helicopters
|
||||||
|
@ -868,23 +943,23 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf)
|
||||||
mm->cpr_valid = 1;
|
mm->cpr_valid = 1;
|
||||||
mm->cpr_type = CPR_AIRBORNE;
|
mm->cpr_type = CPR_AIRBORNE;
|
||||||
mm->cpr_odd = getbit(me, 22);
|
mm->cpr_odd = getbit(me, 22);
|
||||||
|
|
||||||
if (mm->metype == 18 || mm->metype == 22)
|
|
||||||
mm->cpr_nucp = 0;
|
|
||||||
else if (mm->metype < 18)
|
|
||||||
mm->cpr_nucp = (18 - mm->metype);
|
|
||||||
else
|
|
||||||
mm->cpr_nucp = (29 - mm->metype);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present
|
if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present
|
||||||
mm->altitude = decodeAC12Field(AC12Field, &mm->altitude_unit);
|
altitude_unit_t unit;
|
||||||
if (mm->altitude != INVALID_ALTITUDE) {
|
int alt = decodeAC12Field(AC12Field, &unit);
|
||||||
mm->altitude_valid = 1;
|
if (alt != INVALID_ALTITUDE) {
|
||||||
|
if (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) {
|
||||||
|
mm->altitude_geom = alt;
|
||||||
|
mm->altitude_geom_unit = unit;
|
||||||
|
mm->altitude_geom_valid = 1;
|
||||||
|
} else {
|
||||||
|
mm->altitude_baro = alt;
|
||||||
|
mm->altitude_baro_unit = unit;
|
||||||
|
mm->altitude_baro_valid = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mm->altitude_source = (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) ? ALTITUDE_GEOM : ALTITUDE_BARO;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,52 +1006,186 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf)
|
||||||
if (check_imf && getbit(me, 51))
|
if (check_imf && getbit(me, 51))
|
||||||
setIMF(mm);
|
setIMF(mm);
|
||||||
|
|
||||||
if (mm->mesub == 0) { // Target state and status, V1
|
if (mm->mesub == 0 && getbit(me, 11) == 0) { // Target state and status, V1
|
||||||
// TODO: need RTCA/DO-260A
|
// 8-9: vertical source
|
||||||
} else if (mm->mesub == 1) { // Target state and status, V2
|
switch (getbits(me, 8, 9)) {
|
||||||
mm->intent.valid = 1;
|
case 1:
|
||||||
|
mm->nav.altitude_source = NAV_ALT_MCP;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mm->nav.altitude_source = NAV_ALT_AIRCRAFT;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mm->nav.altitude_source = NAV_ALT_FMS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 10: target altitude type (ignored)
|
||||||
|
// 11: backward compatibility bit, always 0
|
||||||
|
// 12-13: target alt capabilities (ignored)
|
||||||
|
// 14-15: vertical mode
|
||||||
|
switch (getbits(me, 14, 15)) {
|
||||||
|
case 1: // "acquiring"
|
||||||
|
mm->nav.modes_valid = 1;
|
||||||
|
if (mm->nav.altitude_source == NAV_ALT_FMS) {
|
||||||
|
mm->nav.modes |= NAV_MODE_VNAV;
|
||||||
|
} else {
|
||||||
|
mm->nav.modes |= NAV_MODE_AUTOPILOT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // "maintaining"
|
||||||
|
mm->nav.modes_valid = 1;
|
||||||
|
if (mm->nav.altitude_source == NAV_ALT_FMS) {
|
||||||
|
mm->nav.modes |= NAV_MODE_VNAV;
|
||||||
|
} else if (mm->nav.altitude_source == NAV_ALT_AIRCRAFT) {
|
||||||
|
mm->nav.modes |= NAV_MODE_ALT_HOLD;
|
||||||
|
} else {
|
||||||
|
mm->nav.modes |= NAV_MODE_AUTOPILOT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16-25: altitude
|
||||||
|
int alt = -1000 + 100 * getbits(me, 16, 25);
|
||||||
|
switch (mm->nav.altitude_source) {
|
||||||
|
case NAV_ALT_MCP:
|
||||||
|
mm->nav.mcp_altitude_valid = 1;
|
||||||
|
mm->nav.mcp_altitude = alt;
|
||||||
|
break;
|
||||||
|
case NAV_ALT_FMS:
|
||||||
|
mm->nav.fms_altitude_valid = 1;
|
||||||
|
mm->nav.fms_altitude = alt;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 26-27: horizontal data source
|
||||||
|
unsigned h_source = getbits(me, 26, 27);
|
||||||
|
if (h_source != 0) {
|
||||||
|
// 28-36: target heading/track
|
||||||
|
mm->nav.heading_valid = 1;
|
||||||
|
mm->nav.heading = getbits(me, 28, 36);
|
||||||
|
// 37: track vs heading
|
||||||
|
if (getbit(me, 37)) {
|
||||||
|
mm->nav.heading_type = HEADING_GROUND_TRACK;
|
||||||
|
} else {
|
||||||
|
mm->nav.heading_type = HEADING_MAGNETIC_OR_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 38-39: horiontal mode
|
||||||
|
switch (getbits(me, 38, 39)) {
|
||||||
|
case 1: // acquiring
|
||||||
|
case 2: // maintaining
|
||||||
|
mm->nav.modes_valid = 1;
|
||||||
|
if (h_source == 3) { // FMS
|
||||||
|
mm->nav.modes |= NAV_MODE_LNAV;
|
||||||
|
} else {
|
||||||
|
mm->nav.modes |= NAV_MODE_AUTOPILOT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 40-43: NACp
|
||||||
|
mm->accuracy.nac_p_valid = 1;
|
||||||
|
mm->accuracy.nac_p = getbits(me, 40, 43);
|
||||||
|
|
||||||
|
// 44: NICbaro
|
||||||
|
mm->accuracy.nic_baro_valid = 1;
|
||||||
|
mm->accuracy.nic_baro = getbit(me, 44);
|
||||||
|
|
||||||
|
// 45-46: SIL
|
||||||
|
mm->accuracy.sil_valid = 1;
|
||||||
|
mm->accuracy.sil = getbits(me, 45, 46);
|
||||||
|
mm->accuracy.sil_type = SIL_INVALID;
|
||||||
|
|
||||||
|
// 47-51: reserved
|
||||||
|
|
||||||
|
// 52-53: TCAS status
|
||||||
|
switch (getbits(me, 52, 53)) {
|
||||||
|
case 1:
|
||||||
|
mm->nav.modes_valid = 1;
|
||||||
|
// no tcas
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
mm->nav.modes_valid = 1;
|
||||||
|
mm->nav.modes |= NAV_MODE_TCAS;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
// assume TCAS if we had any other modes
|
||||||
|
// but don't enable modes just for this
|
||||||
|
mm->nav.modes |= NAV_MODE_TCAS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 54-56: emergency/priority (ignored)
|
||||||
|
} else if (mm->mesub == 1) { // Target state and status, V2
|
||||||
// 8: SIL
|
// 8: SIL
|
||||||
unsigned is_fms = getbit(me, 9);
|
unsigned is_fms = getbit(me, 9);
|
||||||
|
|
||||||
unsigned alt_bits = getbits(me, 10, 20);
|
unsigned alt_bits = getbits(me, 10, 20);
|
||||||
if (alt_bits != 0) {
|
if (alt_bits != 0) {
|
||||||
if (is_fms) {
|
if (is_fms) {
|
||||||
mm->intent.fms_altitude_valid = 1;
|
mm->nav.fms_altitude_valid = 1;
|
||||||
mm->intent.fms_altitude = (alt_bits - 1) * 32;
|
mm->nav.fms_altitude = (alt_bits - 1) * 32;
|
||||||
} else {
|
} else {
|
||||||
mm->intent.mcp_altitude_valid = 1;
|
mm->nav.mcp_altitude_valid = 1;
|
||||||
mm->intent.mcp_altitude = (alt_bits - 1) * 32;
|
mm->nav.mcp_altitude = (alt_bits - 1) * 32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned baro_bits = getbits(me, 21, 29);
|
unsigned baro_bits = getbits(me, 21, 29);
|
||||||
if (baro_bits != 0) {
|
if (baro_bits != 0) {
|
||||||
mm->intent.alt_setting_valid = 1;
|
mm->nav.qnh_valid = 1;
|
||||||
mm->intent.alt_setting = 800.0 + (baro_bits - 1) * 0.8;
|
mm->nav.qnh = 800.0 + (baro_bits - 1) * 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getbit(me, 30)) {
|
if (getbit(me, 30)) {
|
||||||
mm->intent.heading_valid = 1;
|
mm->nav.heading_valid = 1;
|
||||||
// two's complement -180..+180, which is conveniently
|
// two's complement -180..+180, which is conveniently
|
||||||
// also the same as unsigned 0..360
|
// also the same as unsigned 0..360
|
||||||
mm->intent.heading = getbits(me, 31, 39) * 180.0 / 256.0;
|
mm->nav.heading = getbits(me, 31, 39) * 180.0 / 256.0;
|
||||||
|
mm->nav.heading_type = HEADING_MAGNETIC_OR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 40-43: NACp
|
// 40-43: NACp
|
||||||
// 44: NICbaro
|
mm->accuracy.nac_p_valid = 1;
|
||||||
// 45-46: SIL
|
mm->accuracy.nac_p = getbits(me, 40, 43);
|
||||||
|
|
||||||
|
// 44: NICbaro
|
||||||
|
mm->accuracy.nic_baro_valid = 1;
|
||||||
|
mm->accuracy.nic_baro = getbit(me, 44);
|
||||||
|
|
||||||
|
// 45-46: SIL
|
||||||
|
mm->accuracy.sil_valid = 1;
|
||||||
|
mm->accuracy.sil = getbits(me, 45, 46);
|
||||||
|
mm->accuracy.sil_type = SIL_INVALID;
|
||||||
|
|
||||||
|
// 47: mode bits validity
|
||||||
if (getbit(me, 47)) {
|
if (getbit(me, 47)) {
|
||||||
mm->intent.modes_valid = 1;
|
// 48-54: mode bits
|
||||||
mm->intent.modes =
|
mm->nav.modes_valid = 1;
|
||||||
(getbit(me, 48) ? INTENT_MODE_AUTOPILOT : 0) |
|
mm->nav.modes =
|
||||||
(getbit(me, 49) ? INTENT_MODE_VNAV : 0) |
|
(getbit(me, 48) ? NAV_MODE_AUTOPILOT : 0) |
|
||||||
(getbit(me, 50) ? INTENT_MODE_ALT_HOLD : 0) |
|
(getbit(me, 49) ? NAV_MODE_VNAV : 0) |
|
||||||
|
(getbit(me, 50) ? NAV_MODE_ALT_HOLD : 0) |
|
||||||
// 51: IMF
|
// 51: IMF
|
||||||
(getbit(me, 52) ? INTENT_MODE_APPROACH : 0) |
|
(getbit(me, 52) ? NAV_MODE_APPROACH : 0) |
|
||||||
// 53: TCAS operational
|
(getbit(me, 53) ? NAV_MODE_TCAS : 0) |
|
||||||
(getbit(me, 54) ? INTENT_MODE_LNAV : 0);
|
(getbit(me, 54) ? NAV_MODE_LNAV : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 55-56 reserved
|
// 55-56 reserved
|
||||||
|
@ -999,6 +1208,10 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf)
|
||||||
|
|
||||||
switch (mm->opstatus.version) {
|
switch (mm->opstatus.version) {
|
||||||
case 0:
|
case 0:
|
||||||
|
if (mm->mesub == 0 && getbits(me, 9, 10) == 0) {
|
||||||
|
mm->opstatus.cc_acas = !getbit(me, 12);
|
||||||
|
mm->opstatus.cc_cdti = getbit(me, 13);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1024,59 +1237,72 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf)
|
||||||
mm->opstatus.cc_lw = getbits(me, 21, 24);
|
mm->opstatus.cc_lw = getbits(me, 21, 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
mm->opstatus.nic_supp_a = getbit(me, 44);
|
mm->accuracy.nic_a_valid = 1;
|
||||||
mm->opstatus.nac_p = getbits(me, 45, 48);
|
mm->accuracy.nic_a = getbit(me, 44);
|
||||||
mm->opstatus.sil = getbits(me, 51, 52);
|
mm->accuracy.nac_p_valid = 1;
|
||||||
|
mm->accuracy.nac_p = getbits(me, 45, 48);
|
||||||
|
mm->accuracy.sil_valid = 1;
|
||||||
|
mm->accuracy.sil = getbits(me, 51, 52);
|
||||||
|
|
||||||
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
|
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
|
||||||
|
|
||||||
if (mm->mesub == 0) {
|
if (mm->mesub == 0) {
|
||||||
mm->opstatus.nic_baro = getbit(me, 53);
|
mm->accuracy.nic_baro_valid = 1;
|
||||||
|
mm->accuracy.nic_baro = getbit(me, 53);
|
||||||
} else {
|
} else {
|
||||||
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
|
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
default:
|
|
||||||
if (getbits(me, 25, 26) == 0) {
|
if (getbits(me, 25, 26) == 0) {
|
||||||
mm->opstatus.om_acas_ra = getbit(me, 27);
|
mm->opstatus.om_acas_ra = getbit(me, 27);
|
||||||
mm->opstatus.om_ident = getbit(me, 28);
|
mm->opstatus.om_ident = getbit(me, 28);
|
||||||
mm->opstatus.om_atc = getbit(me, 29);
|
mm->opstatus.om_atc = getbit(me, 29);
|
||||||
mm->opstatus.om_saf = getbit(me, 30);
|
mm->opstatus.om_saf = getbit(me, 30);
|
||||||
mm->opstatus.om_sda = getbits(me, 31, 32);
|
mm->accuracy.sda_valid = 1;
|
||||||
|
mm->accuracy.sda = getbits(me, 31, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) {
|
if (mm->mesub == 0 && getbits(me, 9, 10) == 0) {
|
||||||
// airborne
|
// airborne
|
||||||
mm->opstatus.cc_acas = getbit(me, 11);
|
mm->opstatus.cc_acas = getbit(me, 11); // nb inverted sense versus v0/v1
|
||||||
mm->opstatus.cc_1090_in = getbit(me, 12);
|
mm->opstatus.cc_1090_in = getbit(me, 12);
|
||||||
mm->opstatus.cc_arv = getbit(me, 15);
|
mm->opstatus.cc_arv = getbit(me, 15);
|
||||||
mm->opstatus.cc_ts = getbit(me, 16);
|
mm->opstatus.cc_ts = getbit(me, 16);
|
||||||
mm->opstatus.cc_tc = getbits(me, 17, 18);
|
mm->opstatus.cc_tc = getbits(me, 17, 18);
|
||||||
mm->opstatus.cc_uat_in = getbit(me, 19);
|
mm->opstatus.cc_uat_in = getbit(me, 19);
|
||||||
} else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) {
|
} else if (mm->mesub == 1 && getbits(me, 9, 10) == 0) {
|
||||||
// surface
|
// surface
|
||||||
mm->opstatus.cc_poa = getbit(me, 11);
|
mm->opstatus.cc_poa = getbit(me, 11);
|
||||||
mm->opstatus.cc_1090_in = getbit(me, 12);
|
mm->opstatus.cc_1090_in = getbit(me, 12);
|
||||||
mm->opstatus.cc_b2_low = getbit(me, 15);
|
mm->opstatus.cc_b2_low = getbit(me, 15);
|
||||||
mm->opstatus.cc_uat_in = getbit(me, 16);
|
mm->opstatus.cc_uat_in = getbit(me, 16);
|
||||||
mm->opstatus.cc_nac_v = getbits(me, 17, 19);
|
mm->accuracy.nac_v_valid = 1;
|
||||||
mm->opstatus.cc_nic_supp_c = getbit(me, 20);
|
mm->accuracy.nac_v = getbits(me, 17, 19);
|
||||||
|
mm->accuracy.nic_c_valid = 1;
|
||||||
|
mm->accuracy.nic_c = getbit(me, 20);
|
||||||
mm->opstatus.cc_lw_valid = 1;
|
mm->opstatus.cc_lw_valid = 1;
|
||||||
mm->opstatus.cc_lw = getbits(me, 21, 24);
|
mm->opstatus.cc_lw = getbits(me, 21, 24);
|
||||||
mm->opstatus.cc_antenna_offset = getbits(me, 33, 40);
|
mm->opstatus.cc_antenna_offset = getbits(me, 33, 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
mm->opstatus.nic_supp_a = getbit(me, 44);
|
mm->accuracy.nic_a_valid = 1;
|
||||||
mm->opstatus.nac_p = getbits(me, 45, 48);
|
mm->accuracy.nic_a = getbit(me, 44);
|
||||||
mm->opstatus.sil = getbits(me, 51, 52);
|
mm->accuracy.nac_p_valid = 1;
|
||||||
|
mm->accuracy.nac_p = getbits(me, 45, 48);
|
||||||
|
mm->accuracy.sil_valid = 1;
|
||||||
|
mm->accuracy.sil = getbits(me, 51, 52);
|
||||||
|
mm->accuracy.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR;
|
||||||
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
|
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
|
||||||
if (mm->mesub == 0) {
|
if (mm->mesub == 0) {
|
||||||
mm->opstatus.gva = getbits(me, 49, 50);
|
mm->accuracy.gva_valid = 1;
|
||||||
mm->opstatus.nic_baro = getbit(me, 53);
|
mm->accuracy.gva = getbits(me, 49, 50);
|
||||||
|
mm->accuracy.nic_baro_valid = 1;
|
||||||
|
mm->accuracy.nic_baro = getbit(me, 53);
|
||||||
} else {
|
} else {
|
||||||
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
|
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
|
||||||
}
|
}
|
||||||
mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1240,17 +1466,6 @@ static const char *altitude_unit_to_string(altitude_unit_t unit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *altitude_source_to_string(altitude_source_t source) {
|
|
||||||
switch (source) {
|
|
||||||
case ALTITUDE_BARO:
|
|
||||||
return "barometric";
|
|
||||||
case ALTITUDE_GEOM:
|
|
||||||
return "geometric";
|
|
||||||
default:
|
|
||||||
return "(unknown altitude source)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *airground_to_string(airground_t airground) {
|
static const char *airground_to_string(airground_t airground) {
|
||||||
switch (airground) {
|
switch (airground) {
|
||||||
case AG_GROUND:
|
case AG_GROUND:
|
||||||
|
@ -1344,21 +1559,23 @@ static const char *commb_format_to_string(commb_format_t format) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *intent_modes_to_string(intent_modes_t flags)
|
static const char *nav_modes_to_string(nav_modes_t flags)
|
||||||
{
|
{
|
||||||
static char buf[128];
|
static char buf[128];
|
||||||
|
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
if (flags & INTENT_MODE_AUTOPILOT)
|
if (flags & NAV_MODE_AUTOPILOT)
|
||||||
strcat(buf, "autopilot ");
|
strcat(buf, "autopilot ");
|
||||||
if (flags & INTENT_MODE_VNAV)
|
if (flags & NAV_MODE_VNAV)
|
||||||
strcat(buf, "vnav ");
|
strcat(buf, "vnav ");
|
||||||
if (flags & INTENT_MODE_ALT_HOLD)
|
if (flags & NAV_MODE_ALT_HOLD)
|
||||||
strcat(buf, "althold ");
|
strcat(buf, "althold ");
|
||||||
if (flags & INTENT_MODE_APPROACH)
|
if (flags & NAV_MODE_APPROACH)
|
||||||
strcat(buf, "approach ");
|
strcat(buf, "approach ");
|
||||||
if (flags & INTENT_MODE_LNAV)
|
if (flags & NAV_MODE_LNAV)
|
||||||
strcat(buf, "lnav ");
|
strcat(buf, "lnav ");
|
||||||
|
if (flags & NAV_MODE_TCAS)
|
||||||
|
strcat(buf, "tcas ");
|
||||||
|
|
||||||
if (buf[0] != 0)
|
if (buf[0] != 0)
|
||||||
buf[strlen(buf)-1] = 0;
|
buf[strlen(buf)-1] = 0;
|
||||||
|
@ -1617,11 +1834,16 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
airground_to_string(mm->airground));
|
airground_to_string(mm->airground));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->altitude_valid) {
|
if (mm->altitude_baro_valid) {
|
||||||
printf(" Altitude: %d %s %s\n",
|
printf(" Baro altitude: %d %s\n",
|
||||||
mm->altitude,
|
mm->altitude_baro,
|
||||||
altitude_unit_to_string(mm->altitude_unit),
|
altitude_unit_to_string(mm->altitude_baro_unit));
|
||||||
altitude_source_to_string(mm->altitude_source));
|
}
|
||||||
|
|
||||||
|
if (mm->altitude_geom_valid) {
|
||||||
|
printf(" Geom altitude: %d %s\n",
|
||||||
|
mm->altitude_geom,
|
||||||
|
altitude_unit_to_string(mm->altitude_geom_unit));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->geom_delta_valid) {
|
if (mm->geom_delta_valid) {
|
||||||
|
@ -1642,7 +1864,14 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->gs_valid) {
|
if (mm->gs_valid) {
|
||||||
printf(" Groundspeed: %u kt\n", mm->gs);
|
printf(" Groundspeed: %.1f kt", mm->gs.selected);
|
||||||
|
if (mm->gs.v0 != mm->gs.selected) {
|
||||||
|
printf(" (v0: %.1f kt)", mm->gs.v0);
|
||||||
|
}
|
||||||
|
if (mm->gs.v2 != mm->gs.selected) {
|
||||||
|
printf(" (v2: %.1f kt)", mm->gs.v2);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->ias_valid) {
|
if (mm->ias_valid) {
|
||||||
|
@ -1682,21 +1911,24 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
|
|
||||||
if (mm->cpr_valid) {
|
if (mm->cpr_valid) {
|
||||||
printf(" CPR type: %s\n"
|
printf(" CPR type: %s\n"
|
||||||
" CPR odd flag: %s\n"
|
" CPR odd flag: %s\n",
|
||||||
" CPR NUCp/NIC: %u\n",
|
|
||||||
cpr_type_to_string(mm->cpr_type),
|
cpr_type_to_string(mm->cpr_type),
|
||||||
mm->cpr_odd ? "odd" : "even",
|
mm->cpr_odd ? "odd" : "even");
|
||||||
mm->cpr_nucp);
|
|
||||||
|
|
||||||
if (mm->cpr_decoded) {
|
if (mm->cpr_decoded) {
|
||||||
printf(" CPR latitude: %.5f (%u)\n"
|
printf(" CPR latitude: %.5f (%u)\n"
|
||||||
" CPR longitude: %.5f (%u)\n"
|
" CPR longitude: %.5f (%u)\n"
|
||||||
" CPR decoding: %s\n",
|
" CPR decoding: %s\n"
|
||||||
|
" NIC: %u\n"
|
||||||
|
" Rc: %.3f km / %.1f NM\n",
|
||||||
mm->decoded_lat,
|
mm->decoded_lat,
|
||||||
mm->cpr_lat,
|
mm->cpr_lat,
|
||||||
mm->decoded_lon,
|
mm->decoded_lon,
|
||||||
mm->cpr_lon,
|
mm->cpr_lon,
|
||||||
mm->cpr_relative ? "local" : "global");
|
mm->cpr_relative ? "local" : "global",
|
||||||
|
mm->decoded_nic,
|
||||||
|
mm->decoded_rc / 1000.0,
|
||||||
|
mm->decoded_rc / 1852.0);
|
||||||
} else {
|
} else {
|
||||||
printf(" CPR latitude: (%u)\n"
|
printf(" CPR latitude: (%u)\n"
|
||||||
" CPR longitude: (%u)\n"
|
" CPR longitude: (%u)\n"
|
||||||
|
@ -1706,6 +1938,36 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.nic_a_valid) {
|
||||||
|
printf(" NIC-A: %d\n", mm->accuracy.nic_a);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.nic_b_valid) {
|
||||||
|
printf(" NIC-B: %d\n", mm->accuracy.nic_b);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.nic_c_valid) {
|
||||||
|
printf(" NIC-C: %d\n", mm->accuracy.nic_c);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.nic_baro_valid) {
|
||||||
|
printf(" NIC-baro: %d\n", mm->accuracy.nic_baro);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.nac_p_valid) {
|
||||||
|
printf(" NACp: %d\n", mm->accuracy.nac_p);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.nac_v_valid) {
|
||||||
|
printf(" NACv: %d\n", mm->accuracy.nac_v);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.gva_valid) {
|
||||||
|
printf(" GVA: %d\n", mm->accuracy.gva);
|
||||||
|
}
|
||||||
|
if (mm->accuracy.sil_valid) {
|
||||||
|
printf(" SIL: %d (%s)\n",
|
||||||
|
mm->accuracy.sil,
|
||||||
|
(mm->accuracy.sil_type == SIL_PER_HOUR ? "per hour" : "per sample"));
|
||||||
|
}
|
||||||
|
if (mm->accuracy.sda_valid) {
|
||||||
|
printf(" SDA: %d\n", mm->accuracy.sda);
|
||||||
|
}
|
||||||
|
|
||||||
if (mm->opstatus.valid) {
|
if (mm->opstatus.valid) {
|
||||||
printf(" Aircraft Operational Status:\n");
|
printf(" Aircraft Operational Status:\n");
|
||||||
printf(" Version: %d\n", mm->opstatus.version);
|
printf(" Version: %d\n", mm->opstatus.version);
|
||||||
|
@ -1720,8 +1982,6 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
if (mm->opstatus.cc_uat_in) printf("UATIN ");
|
if (mm->opstatus.cc_uat_in) printf("UATIN ");
|
||||||
if (mm->opstatus.cc_poa) printf("POA ");
|
if (mm->opstatus.cc_poa) printf("POA ");
|
||||||
if (mm->opstatus.cc_b2_low) printf("B2-LOW ");
|
if (mm->opstatus.cc_b2_low) printf("B2-LOW ");
|
||||||
if (mm->opstatus.cc_nac_v) printf("NACv=%d ", mm->opstatus.cc_nac_v);
|
|
||||||
if (mm->opstatus.cc_nic_supp_c) printf("NIC-C=1 ");
|
|
||||||
if (mm->opstatus.cc_lw_valid) printf("L/W=%d ", mm->opstatus.cc_lw);
|
if (mm->opstatus.cc_lw_valid) printf("L/W=%d ", mm->opstatus.cc_lw);
|
||||||
if (mm->opstatus.cc_antenna_offset) printf("GPS-OFFSET=%d ", mm->opstatus.cc_antenna_offset);
|
if (mm->opstatus.cc_antenna_offset) printf("GPS-OFFSET=%d ", mm->opstatus.cc_antenna_offset);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
@ -1731,53 +1991,43 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
if (mm->opstatus.om_ident) printf("IDENT ");
|
if (mm->opstatus.om_ident) printf("IDENT ");
|
||||||
if (mm->opstatus.om_atc) printf("ATC ");
|
if (mm->opstatus.om_atc) printf("ATC ");
|
||||||
if (mm->opstatus.om_saf) printf("SAF ");
|
if (mm->opstatus.om_saf) printf("SAF ");
|
||||||
if (mm->opstatus.om_sda) printf("SDA=%d ", mm->opstatus.om_sda);
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
if (mm->opstatus.nic_supp_a) printf(" NIC-A: %d\n", mm->opstatus.nic_supp_a);
|
|
||||||
if (mm->opstatus.nac_p) printf(" NACp: %d\n", mm->opstatus.nac_p);
|
|
||||||
if (mm->opstatus.gva) printf(" GVA: %d\n", mm->opstatus.gva);
|
|
||||||
if (mm->opstatus.sil) printf(" SIL: %d (%s)\n", mm->opstatus.sil, (mm->opstatus.sil_type == SIL_PER_HOUR ? "per hour" : "per sample"));
|
|
||||||
if (mm->opstatus.nic_baro) printf(" NICbaro: %d\n", mm->opstatus.nic_baro);
|
|
||||||
|
|
||||||
if (mm->mesub == 1)
|
if (mm->mesub == 1)
|
||||||
printf(" Track/heading: %s\n", heading_type_to_string(mm->opstatus.tah));
|
printf(" Track/heading: %s\n", heading_type_to_string(mm->opstatus.tah));
|
||||||
printf(" Heading ref dir: %s\n", heading_type_to_string(mm->opstatus.hrd));
|
printf(" Heading ref dir: %s\n", heading_type_to_string(mm->opstatus.hrd));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->intent.valid) {
|
if (mm->nav.heading_valid)
|
||||||
printf(" Intent:\n");
|
printf(" Selected heading: %.1f\n", mm->nav.heading);
|
||||||
if (mm->intent.heading_valid)
|
if (mm->nav.fms_altitude_valid)
|
||||||
printf(" Selected heading: %.1f\n", mm->intent.heading);
|
printf(" FMS selected altitude: %u ft\n", mm->nav.fms_altitude);
|
||||||
if (mm->intent.fms_altitude_valid)
|
if (mm->nav.mcp_altitude_valid)
|
||||||
printf(" FMS selected altitude: %u ft\n", mm->intent.fms_altitude);
|
printf(" MCP selected altitude: %u ft\n", mm->nav.mcp_altitude);
|
||||||
if (mm->intent.mcp_altitude_valid)
|
if (mm->nav.qnh_valid)
|
||||||
printf(" MCP selected altitude: %u ft\n", mm->intent.mcp_altitude);
|
printf(" QNH: %.1f millibars\n", mm->nav.qnh);
|
||||||
if (mm->intent.alt_setting_valid)
|
if (mm->nav.altitude_source != NAV_ALT_INVALID) {
|
||||||
printf(" Altimeter setting: %.1f millibars\n", mm->intent.alt_setting);
|
printf(" Target altitude source: ");
|
||||||
|
switch (mm->nav.altitude_source) {
|
||||||
if (mm->intent.altitude_source != INTENT_ALT_INVALID) {
|
case NAV_ALT_AIRCRAFT:
|
||||||
printf(" Target altitude source: ");
|
printf("aircraft altitude\n");
|
||||||
switch (mm->intent.altitude_source) {
|
break;
|
||||||
case INTENT_ALT_AIRCRAFT:
|
case NAV_ALT_MCP:
|
||||||
printf("aircraft altitude\n");
|
printf("MCP selected altitude\n");
|
||||||
break;
|
break;
|
||||||
case INTENT_ALT_MCP:
|
case NAV_ALT_FMS:
|
||||||
printf("MCP selected altitude\n");
|
printf("FMS selected altitude\n");
|
||||||
break;
|
break;
|
||||||
case INTENT_ALT_FMS:
|
default:
|
||||||
printf("FMS selected altitude\n");
|
printf("unknown\n");
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("unknown\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mm->intent.modes_valid) {
|
|
||||||
printf(" Active modes: %s\n", intent_modes_to_string(mm->intent.modes));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mm->nav.modes_valid) {
|
||||||
|
printf(" Nav modes: %s\n", nav_modes_to_string(mm->nav.modes));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
259
net_io.c
259
net_io.c
|
@ -582,31 +582,29 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
|
||||||
else {p += sprintf(p, ",");}
|
else {p += sprintf(p, ",");}
|
||||||
|
|
||||||
// Field 12 is the altitude (if we have it)
|
// Field 12 is the altitude (if we have it)
|
||||||
if (mm->altitude_valid) {
|
if (Modes.use_gnss) {
|
||||||
if (Modes.use_gnss) {
|
if (mm->altitude_geom_valid) {
|
||||||
if (mm->altitude_source == ALTITUDE_GEOM) {
|
p += sprintf(p, ",%dH", mm->altitude_geom);
|
||||||
p += sprintf(p, ",%dH", mm->altitude);
|
} else if (mm->altitude_baro_valid && trackDataValid(&a->geom_delta_valid)) {
|
||||||
} else if (trackDataValid(&a->geom_delta_valid)) {
|
p += sprintf(p, ",%dH", mm->altitude_baro + a->geom_delta);
|
||||||
p += sprintf(p, ",%dH", mm->altitude + a->geom_delta);
|
} else if (mm->altitude_baro_valid) {
|
||||||
} else {
|
p += sprintf(p, ",%d", mm->altitude_baro);
|
||||||
p += sprintf(p, ",%d", mm->altitude);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (mm->altitude_source == ALTITUDE_BARO) {
|
p += sprintf(p, ",");
|
||||||
p += sprintf(p, ",%d", mm->altitude);
|
|
||||||
} else if (trackDataValid(&a->geom_delta_valid)) {
|
|
||||||
p += sprintf(p, ",%d", mm->altitude - a->geom_delta);
|
|
||||||
} else {
|
|
||||||
p += sprintf(p, ",");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",");
|
if (mm->altitude_baro_valid) {
|
||||||
|
p += sprintf(p, ",%d", mm->altitude_baro);
|
||||||
|
} else if (mm->altitude_geom_valid && trackDataValid(&a->geom_delta_valid)) {
|
||||||
|
p += sprintf(p, ",%d", mm->altitude_geom - a->geom_delta);
|
||||||
|
} else {
|
||||||
|
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->gs_valid) {
|
if (mm->gs_valid) {
|
||||||
p += sprintf(p, ",%u", mm->gs);
|
p += sprintf(p, ",%.0f", mm->gs.selected);
|
||||||
} else {
|
} else {
|
||||||
p += sprintf(p, ",");
|
p += sprintf(p, ",");
|
||||||
}
|
}
|
||||||
|
@ -1111,8 +1109,10 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s
|
||||||
p += snprintf(p, end-p, "\"callsign\",");
|
p += snprintf(p, end-p, "\"callsign\",");
|
||||||
if (a->position_valid.source == source)
|
if (a->position_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"lat\",\"lon\",");
|
p += snprintf(p, end-p, "\"lat\",\"lon\",");
|
||||||
if (a->altitude_valid.source == source)
|
if (a->altitude_baro_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"altitude\",");
|
p += snprintf(p, end-p, "\"altitude\",");
|
||||||
|
if (a->altitude_geom_valid.source == source)
|
||||||
|
p += snprintf(p, end-p, "\"alt_geom\",");
|
||||||
if (a->track_valid.source == source)
|
if (a->track_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"track\",");
|
p += snprintf(p, end-p, "\"track\",");
|
||||||
if (a->mag_heading_valid.source == source)
|
if (a->mag_heading_valid.source == source)
|
||||||
|
@ -1129,8 +1129,6 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s
|
||||||
p += snprintf(p, end-p, "\"baro_rate\",");
|
p += snprintf(p, end-p, "\"baro_rate\",");
|
||||||
if (a->geom_rate_valid.source == source)
|
if (a->geom_rate_valid.source == source)
|
||||||
p += snprintf(p, end-p, "\"geom_rate\",");
|
p += snprintf(p, end-p, "\"geom_rate\",");
|
||||||
if (a->category_valid.source == source)
|
|
||||||
p += snprintf(p, end-p, "\"category\",");
|
|
||||||
if (p[-1] != '[')
|
if (p[-1] != '[')
|
||||||
--p;
|
--p;
|
||||||
p += snprintf(p, end-p, "]");
|
p += snprintf(p, end-p, "]");
|
||||||
|
@ -1138,22 +1136,23 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
intent_modes_t flag;
|
nav_modes_t flag;
|
||||||
const char *name;
|
const char *name;
|
||||||
} intent_modes_names[] = {
|
} nav_modes_names[] = {
|
||||||
{ INTENT_MODE_AUTOPILOT, "autopilot" },
|
{ NAV_MODE_AUTOPILOT, "autopilot" },
|
||||||
{ INTENT_MODE_VNAV, "vnav" },
|
{ NAV_MODE_VNAV, "vnav" },
|
||||||
{ INTENT_MODE_ALT_HOLD, "althold" },
|
{ NAV_MODE_ALT_HOLD, "althold" },
|
||||||
{ INTENT_MODE_APPROACH, "approach" },
|
{ NAV_MODE_APPROACH, "approach" },
|
||||||
{ INTENT_MODE_LNAV, "lnav" },
|
{ NAV_MODE_LNAV, "lnav" },
|
||||||
|
{ NAV_MODE_TCAS, "tcas" },
|
||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *append_intent_modes(char *p, char *end, intent_modes_t flags, const char *quote, const char *sep)
|
static char *append_nav_modes(char *p, char *end, nav_modes_t flags, const char *quote, const char *sep)
|
||||||
{
|
{
|
||||||
int first = 1;
|
int first = 1;
|
||||||
for (int i = 0; intent_modes_names[i].name; ++i) {
|
for (int i = 0; nav_modes_names[i].name; ++i) {
|
||||||
if (!(flags & intent_modes_names[i].flag)) {
|
if (!(flags & nav_modes_names[i].flag)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1162,16 +1161,16 @@ static char *append_intent_modes(char *p, char *end, intent_modes_t flags, const
|
||||||
}
|
}
|
||||||
|
|
||||||
first = 0;
|
first = 0;
|
||||||
p += snprintf(p, end-p, "%s%s%s", quote, intent_modes_names[i].name, quote);
|
p += snprintf(p, end-p, "%s%s%s", quote, nav_modes_names[i].name, quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *intent_modes_string(intent_modes_t flags) {
|
static const char *nav_modes_string(nav_modes_t flags) {
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
append_intent_modes(buf, buf + sizeof(buf), flags, "", " ");
|
append_nav_modes(buf, buf + sizeof(buf), flags, "", " ");
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1234,12 +1233,12 @@ char *generateAircraftJson(const char *url_path, int *len) {
|
||||||
if (trackDataValid(&a->callsign_valid))
|
if (trackDataValid(&a->callsign_valid))
|
||||||
p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign));
|
p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign));
|
||||||
if (trackDataValid(&a->position_valid))
|
if (trackDataValid(&a->position_valid))
|
||||||
p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->position_valid.updated)/1000.0);
|
p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nic\":%u,\"rc\":%u\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nic, a->pos_rc, (now - a->position_valid.updated)/1000.0);
|
||||||
if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND)
|
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 {
|
else {
|
||||||
if (trackDataValid(&a->altitude_valid))
|
if (trackDataValid(&a->altitude_baro_valid))
|
||||||
p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude);
|
p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude_baro);
|
||||||
if (trackDataValid(&a->altitude_geom_valid))
|
if (trackDataValid(&a->altitude_geom_valid))
|
||||||
p += snprintf(p, end-p, ",\"alt_geom\":%d", a->altitude_geom);
|
p += snprintf(p, end-p, ",\"alt_geom\":%d", a->altitude_geom);
|
||||||
}
|
}
|
||||||
|
@ -1256,7 +1255,7 @@ char *generateAircraftJson(const char *url_path, int *len) {
|
||||||
if (trackDataValid(&a->true_heading_valid))
|
if (trackDataValid(&a->true_heading_valid))
|
||||||
p += snprintf(p, end-p, ",\"true_heading\":%.1f", a->true_heading);
|
p += snprintf(p, end-p, ",\"true_heading\":%.1f", a->true_heading);
|
||||||
if (trackDataValid(&a->gs_valid))
|
if (trackDataValid(&a->gs_valid))
|
||||||
p += snprintf(p, end-p, ",\"gs\":%u", a->gs);
|
p += snprintf(p, end-p, ",\"gs\":%.1f", a->gs);
|
||||||
if (trackDataValid(&a->ias_valid))
|
if (trackDataValid(&a->ias_valid))
|
||||||
p += snprintf(p, end-p, ",\"ias\":%u", a->ias);
|
p += snprintf(p, end-p, ",\"ias\":%u", a->ias);
|
||||||
if (trackDataValid(&a->tas_valid))
|
if (trackDataValid(&a->tas_valid))
|
||||||
|
@ -1265,19 +1264,19 @@ char *generateAircraftJson(const char *url_path, int *len) {
|
||||||
p += snprintf(p, end-p, ",\"mach\":%.3f", a->mach);
|
p += snprintf(p, end-p, ",\"mach\":%.3f", a->mach);
|
||||||
if (trackDataValid(&a->roll_valid))
|
if (trackDataValid(&a->roll_valid))
|
||||||
p += snprintf(p, end-p, ",\"roll\":%.1f", a->roll);
|
p += snprintf(p, end-p, ",\"roll\":%.1f", a->roll);
|
||||||
if (trackDataValid(&a->category_valid))
|
if (a->category != 0)
|
||||||
p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category);
|
p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category);
|
||||||
if (trackDataValid(&a->intent_altitude_valid))
|
if (trackDataValid(&a->nav_altitude_valid))
|
||||||
p += snprintf(p, end-p, ",\"intent_alt\":%d", a->intent_altitude);
|
p += snprintf(p, end-p, ",\"nav_alt\":%d", a->nav_altitude);
|
||||||
if (trackDataValid(&a->intent_heading_valid))
|
if (trackDataValid(&a->nav_heading_valid))
|
||||||
p += snprintf(p, end-p, ",\"intent_heading\":%.1f", a->intent_heading);
|
p += snprintf(p, end-p, ",\"nav_heading\":%.1f", a->nav_heading);
|
||||||
if (trackDataValid(&a->intent_modes_valid)) {
|
if (trackDataValid(&a->nav_modes_valid)) {
|
||||||
p += snprintf(p, end-p, ",\"intent_modes\":[");
|
p += snprintf(p, end-p, ",\"nav_modes\":[");
|
||||||
p = append_intent_modes(p, end, a->intent_modes, "\"", ",");
|
p = append_nav_modes(p, end, a->nav_modes, "\"", ",");
|
||||||
p += snprintf(p, end-p, "]");
|
p += snprintf(p, end-p, "]");
|
||||||
}
|
}
|
||||||
if (trackDataValid(&a->alt_setting_valid))
|
if (trackDataValid(&a->nav_qnh_valid))
|
||||||
p += snprintf(p, end-p, ",\"alt_setting\":%.1f", a->alt_setting);
|
p += snprintf(p, end-p, ",\"nav_qnh\":%.1f", a->nav_qnh);
|
||||||
|
|
||||||
p += snprintf(p, end-p, ",\"mlat\":");
|
p += snprintf(p, end-p, ",\"mlat\":");
|
||||||
p = append_flags(p, end, a, SOURCE_MLAT);
|
p = append_flags(p, end, a, SOURCE_MLAT);
|
||||||
|
@ -1757,13 +1756,13 @@ static void modesReadFromClient(struct client *c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap)
|
__attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap)
|
||||||
{
|
{
|
||||||
p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap);
|
p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *safe_snprintf(char *p, char *end, const char *format, ...)
|
__attribute__ ((format (printf,3,4))) static char *safe_snprintf(char *p, char *end, const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
|
@ -1773,7 +1772,7 @@ static char *safe_snprintf(char *p, char *end, const char *format, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...)
|
__attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
|
@ -1913,7 +1912,7 @@ static inline float heading_difference(float h1, float h2)
|
||||||
return (d < 180) ? d : (360 - d);
|
return (d < 180) ? d : (360 - d);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircraft *a, const data_validity *source, const char *format, ...)
|
__attribute__ ((format (printf,6,7))) static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircraft *a, const data_validity *source, const char *format, ...)
|
||||||
{
|
{
|
||||||
const char *sourcetype;
|
const char *sourcetype;
|
||||||
switch (source->source) {
|
switch (source->source) {
|
||||||
|
@ -1966,15 +1965,51 @@ static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircr
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *airground_string(airground_t ag)
|
||||||
|
{
|
||||||
|
switch (ag) {
|
||||||
|
case AG_AIRBORNE:
|
||||||
|
return "A+";
|
||||||
|
case AG_GROUND:
|
||||||
|
return "G+";
|
||||||
|
default:
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeFATSVBanner()
|
||||||
|
{
|
||||||
|
char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE);
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
char *end = p + TSV_MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
p = appendFATSV(p, end, "faup1090_format_version", "%s", "2");
|
||||||
|
|
||||||
|
--p; // remove last tab
|
||||||
|
p = safe_snprintf(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));
|
||||||
|
}
|
||||||
|
|
||||||
static void writeFATSV()
|
static void writeFATSV()
|
||||||
{
|
{
|
||||||
struct aircraft *a;
|
struct aircraft *a;
|
||||||
static uint64_t next_update;
|
static uint64_t next_update;
|
||||||
|
static int first_run = 1;
|
||||||
|
|
||||||
if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) {
|
if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) {
|
||||||
return; // not enabled or no active connections
|
return; // not enabled or no active connections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (first_run) {
|
||||||
|
writeFATSVBanner();
|
||||||
|
first_run = 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t now = mstime();
|
uint64_t now = mstime();
|
||||||
if (now < next_update) {
|
if (now < next_update) {
|
||||||
return;
|
return;
|
||||||
|
@ -1995,9 +2030,10 @@ static void writeFATSV()
|
||||||
}
|
}
|
||||||
|
|
||||||
// some special cases:
|
// some special cases:
|
||||||
int altValid = trackDataValid(&a->altitude_valid);
|
int altValid = trackDataValid(&a->altitude_baro_valid);
|
||||||
int airgroundValid = trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED; // for non-ADS-B transponders, only trust DF11 CA field
|
int airgroundValid = trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED; // for non-ADS-B transponders, only trust DF11 CA field
|
||||||
int gsValid = trackDataValid(&a->gs_valid);
|
int gsValid = trackDataValid(&a->gs_valid);
|
||||||
|
int squawkValid = trackDataValid(&a->squawk_valid);
|
||||||
int callsignValid = trackDataValid(&a->callsign_valid) && strcmp(a->callsign, " ") != 0;
|
int callsignValid = trackDataValid(&a->callsign_valid) && strcmp(a->callsign, " ") != 0;
|
||||||
int positionValid = trackDataValid(&a->position_valid);
|
int positionValid = trackDataValid(&a->position_valid);
|
||||||
|
|
||||||
|
@ -2005,14 +2041,14 @@ static void writeFATSV()
|
||||||
// When on the ground, ADS-B transponders don't emit an ADS-B message that includes
|
// 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
|
// altitude, so a corrupted Mode S altitude response from some other in-the-air AC
|
||||||
// might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000".
|
// might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000".
|
||||||
if (airgroundValid && a->airground == AG_GROUND && a->altitude_valid.source < SOURCE_MODE_S_CHECKED)
|
if (airgroundValid && a->airground == AG_GROUND && a->altitude_baro_valid.source < SOURCE_MODE_S_CHECKED)
|
||||||
altValid = 0;
|
altValid = 0;
|
||||||
|
|
||||||
// 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
|
||||||
int changed =
|
int changed =
|
||||||
(altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) ||
|
(altValid && abs(a->altitude_baro - a->fatsv_emitted_altitude_baro) >= 50) ||
|
||||||
(trackDataValid(&a->altitude_geom_valid) && abs(a->altitude_geom - a->fatsv_emitted_altitude_gnss) >= 50) ||
|
(trackDataValid(&a->altitude_geom_valid) && abs(a->altitude_geom - a->fatsv_emitted_altitude_geom) >= 50) ||
|
||||||
(trackDataValid(&a->baro_rate_valid) && abs(a->baro_rate - a->fatsv_emitted_baro_rate) > 500) ||
|
(trackDataValid(&a->baro_rate_valid) && abs(a->baro_rate - a->fatsv_emitted_baro_rate) > 500) ||
|
||||||
(trackDataValid(&a->geom_rate_valid) && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) ||
|
(trackDataValid(&a->geom_rate_valid) && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) ||
|
||||||
(trackDataValid(&a->track_valid) && heading_difference(a->track, a->fatsv_emitted_track) >= 2) ||
|
(trackDataValid(&a->track_valid) && heading_difference(a->track, a->fatsv_emitted_track) >= 2) ||
|
||||||
|
@ -2020,19 +2056,20 @@ static void writeFATSV()
|
||||||
(trackDataValid(&a->roll_valid) && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) ||
|
(trackDataValid(&a->roll_valid) && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) ||
|
||||||
(trackDataValid(&a->mag_heading_valid) && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) ||
|
(trackDataValid(&a->mag_heading_valid) && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) ||
|
||||||
(trackDataValid(&a->true_heading_valid) && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) ||
|
(trackDataValid(&a->true_heading_valid) && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) ||
|
||||||
(gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) ||
|
(gsValid && fabs(a->gs - a->fatsv_emitted_gs) >= 25) ||
|
||||||
(trackDataValid(&a->ias_valid) && unsigned_difference(a->ias, a->fatsv_emitted_speed_ias) >= 25) ||
|
(trackDataValid(&a->ias_valid) && unsigned_difference(a->ias, a->fatsv_emitted_ias) >= 25) ||
|
||||||
(trackDataValid(&a->tas_valid) && unsigned_difference(a->tas, a->fatsv_emitted_speed_tas) >= 25) ||
|
(trackDataValid(&a->tas_valid) && unsigned_difference(a->tas, a->fatsv_emitted_tas) >= 25) ||
|
||||||
(trackDataValid(&a->mach_valid) && fabs(a->mach - a->fatsv_emitted_mach) >= 0.02);
|
(trackDataValid(&a->mach_valid) && fabs(a->mach - a->fatsv_emitted_mach) >= 0.02);
|
||||||
|
|
||||||
int immediate =
|
int immediate =
|
||||||
(trackDataValid(&a->intent_altitude_valid) && unsigned_difference(a->intent_altitude, a->fatsv_emitted_intent_altitude) > 50) ||
|
(trackDataValid(&a->nav_altitude_valid) && unsigned_difference(a->nav_altitude, a->fatsv_emitted_nav_altitude) > 50) ||
|
||||||
(trackDataValid(&a->intent_heading_valid) && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) ||
|
(trackDataValid(&a->nav_heading_valid) && heading_difference(a->nav_heading, a->fatsv_emitted_nav_heading) > 2) ||
|
||||||
(trackDataValid(&a->intent_modes_valid) && a->intent_modes != a->fatsv_emitted_intent_modes) ||
|
(trackDataValid(&a->nav_modes_valid) && a->nav_modes != a->fatsv_emitted_nav_modes) ||
|
||||||
(trackDataValid(&a->alt_setting_valid) && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) || // 0.8 is the ES message resolution
|
(trackDataValid(&a->nav_qnh_valid) && fabs(a->nav_qnh - a->fatsv_emitted_nav_qnh) > 0.8) || // 0.8 is the ES message resolution
|
||||||
(callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) ||
|
(callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) ||
|
||||||
(airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) ||
|
(airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) ||
|
||||||
(airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE);
|
(airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE) ||
|
||||||
|
(squawkValid && a->squawk != a->fatsv_emitted_squawk);
|
||||||
|
|
||||||
uint64_t minAge;
|
uint64_t minAge;
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
|
@ -2042,11 +2079,11 @@ static void writeFATSV()
|
||||||
// don't send mode S very often
|
// don't send mode S very often
|
||||||
minAge = 30000;
|
minAge = 30000;
|
||||||
} else if ((airgroundValid && a->airground == AG_GROUND) ||
|
} else if ((airgroundValid && a->airground == AG_GROUND) ||
|
||||||
(altValid && a->altitude < 500 && (!gsValid || a->gs < 200)) ||
|
(altValid && a->altitude_baro < 500 && (!gsValid || a->gs < 200)) ||
|
||||||
(gsValid && a->gs < 100 && (!altValid || a->altitude < 1000))) {
|
(gsValid && a->gs < 100 && (!altValid || a->altitude_baro < 1000))) {
|
||||||
// we are probably on the ground, increase the update rate
|
// we are probably on the ground, increase the update rate
|
||||||
minAge = 1000;
|
minAge = 1000;
|
||||||
} else if (!altValid || a->altitude < 10000) {
|
} else if (!altValid || a->altitude_baro < 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
|
||||||
minAge = (changed ? 5000 : 10000);
|
minAge = (changed ? 5000 : 10000);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2065,13 +2102,20 @@ static void writeFATSV()
|
||||||
p = appendFATSV(p, end, "clock", "%" PRIu64, messageNow() / 1000);
|
p = appendFATSV(p, end, "clock", "%" PRIu64, messageNow() / 1000);
|
||||||
p = appendFATSV(p, end, (a->addr & MODES_NON_ICAO_ADDRESS) ? "otherid" : "hexid", "%06X", a->addr & 0xFFFFFF);
|
p = appendFATSV(p, end, (a->addr & MODES_NON_ICAO_ADDRESS) ? "otherid" : "hexid", "%06X", a->addr & 0xFFFFFF);
|
||||||
|
|
||||||
if (a->addrtype != ADDR_ADSB_ICAO) {
|
// for fields we only emit on change,
|
||||||
|
// occasionally re-emit them all
|
||||||
|
int forceEmit = (now - a->fatsv_last_force_emit) > 600000;
|
||||||
|
|
||||||
|
// these don't change often / at all, only emit when they change
|
||||||
|
if (forceEmit || a->addrtype != a->fatsv_emitted_addrtype) {
|
||||||
p = appendFATSV(p, end, "addrtype", "%s", addrtype_short_string(a->addrtype));
|
p = appendFATSV(p, end, "addrtype", "%s", addrtype_short_string(a->addrtype));
|
||||||
}
|
}
|
||||||
|
if (forceEmit || a->adsb_version != a->fatsv_emitted_adsb_version) {
|
||||||
if (a->adsb_version >= 0) {
|
|
||||||
p = appendFATSV(p, end, "adsbVer", "%d", a->adsb_version);
|
p = appendFATSV(p, end, "adsbVer", "%d", a->adsb_version);
|
||||||
}
|
}
|
||||||
|
if (forceEmit || a->category != a->fatsv_emitted_category) {
|
||||||
|
p = appendFATSV(p, end, "category", "%02X", a->category);
|
||||||
|
}
|
||||||
|
|
||||||
// only emit alt, speed, latlon, track etc if they have been received since the last time
|
// only emit alt, speed, latlon, track etc if they have been received since the last time
|
||||||
// and are not stale
|
// and are not stale
|
||||||
|
@ -2079,34 +2123,35 @@ static void writeFATSV()
|
||||||
char *dataStart = p;
|
char *dataStart = p;
|
||||||
|
|
||||||
// special cases
|
// special cases
|
||||||
if (altValid)
|
|
||||||
p = appendFATSVMeta(p, end, "alt", a, &a->altitude_valid, "%d", a->altitude);
|
|
||||||
if (airgroundValid)
|
if (airgroundValid)
|
||||||
p = appendFATSVMeta(p, end, "ag", a, &a->airground_valid, "%s", a->airground == AG_GROUND ? "G+" : "A+");
|
p = appendFATSVMeta(p, end, "airGround", a, &a->airground_valid, "%s", airground_string(a->airground));
|
||||||
if (strcmp(a->callsign, " ") != 0)
|
if (squawkValid)
|
||||||
|
p = appendFATSVMeta(p, end, "squawk", a, &a->squawk_valid, "%04x", a->squawk);
|
||||||
|
if (callsignValid)
|
||||||
p = appendFATSVMeta(p, end, "ident", a, &a->callsign_valid, "{%s}", a->callsign);
|
p = appendFATSVMeta(p, end, "ident", a, &a->callsign_valid, "{%s}", a->callsign);
|
||||||
if (positionValid)
|
if (altValid)
|
||||||
p = appendFATSVMeta(p, end, "pos", a, &a->position_valid, "{%.5f %.5f}", a->lat, a->lon);
|
p = appendFATSVMeta(p, end, "alt", a, &a->altitude_baro_valid, "%d", a->altitude_baro);
|
||||||
|
if (positionValid) {
|
||||||
|
p = appendFATSVMeta(p, end, "position", a, &a->position_valid, "{%.5f %.5f %u %u}", a->lat, a->lon, a->pos_nic, a->pos_rc);
|
||||||
|
p = appendFATSVMeta(p, end, "nac_p", a, &a->nac_p_valid, "%u", a->nac_p);
|
||||||
|
}
|
||||||
|
|
||||||
p = appendFATSVMeta(p, end, "squawk", a, &a->squawk_valid, "%04x", a->squawk);
|
p = appendFATSVMeta(p, end, "alt_gnss", a, &a->altitude_geom_valid, "%d", a->altitude_geom);
|
||||||
p = appendFATSVMeta(p, end, "altGeo", a, &a->altitude_geom_valid, "%d", a->altitude_geom);
|
p = appendFATSVMeta(p, end, "vrate", a, &a->baro_rate_valid, "%d", a->baro_rate);
|
||||||
p = appendFATSVMeta(p, end, "vrate", a, &a->baro_rate_valid, "%d", a->baro_rate);
|
p = appendFATSVMeta(p, end, "vrate_geom", a, &a->geom_rate_valid, "%d", a->geom_rate);
|
||||||
p = appendFATSVMeta(p, end, "vrateGeo", a, &a->geom_rate_valid, "%d", a->geom_rate);
|
p = appendFATSVMeta(p, end, "speed", a, &a->gs_valid, "%.1f", a->gs);
|
||||||
p = appendFATSVMeta(p, end, "gs", a, &a->gs_valid, "%u", a->gs);
|
p = appendFATSVMeta(p, end, "speed_ias", a, &a->ias_valid, "%u", a->ias);
|
||||||
p = appendFATSVMeta(p, end, "ias", a, &a->ias_valid, "%u", a->ias);
|
p = appendFATSVMeta(p, end, "speed_tas", a, &a->tas_valid, "%u", a->tas);
|
||||||
p = appendFATSVMeta(p, end, "tas", a, &a->tas_valid, "%u", a->tas);
|
p = appendFATSVMeta(p, end, "mach", a, &a->mach_valid, "%.3f", a->mach);
|
||||||
p = appendFATSVMeta(p, end, "mach", a, &a->mach_valid, "%.3f", a->mach);
|
p = appendFATSVMeta(p, end, "track", a, &a->track_valid, "%.1f", a->track);
|
||||||
p = appendFATSVMeta(p, end, "trk", a, &a->track_valid, "%.0f", a->track);
|
p = appendFATSVMeta(p, end, "track_rate", a, &a->track_rate_valid, "%.2f", a->track_rate);
|
||||||
p = appendFATSVMeta(p, end, "trkRate", a, &a->track_rate_valid, "%.2f", a->track_rate);
|
p = appendFATSVMeta(p, end, "roll", a, &a->roll_valid, "%.1f", a->roll);
|
||||||
p = appendFATSVMeta(p, end, "roll", a, &a->roll_valid, "%.1f", a->roll);
|
p = appendFATSVMeta(p, end, "heading_magnetic", a, &a->mag_heading_valid, "%.1f", a->mag_heading);
|
||||||
p = appendFATSVMeta(p, end, "hdgMag", a, &a->mag_heading_valid, "%.0f", a->mag_heading);
|
p = appendFATSVMeta(p, end, "heading_true", a, &a->true_heading_valid, "%.1f", a->true_heading);
|
||||||
p = appendFATSVMeta(p, end, "hdgTrue", a, &a->true_heading_valid, "%.0f", a->true_heading);
|
p = appendFATSVMeta(p, end, "nav_alt", a, &a->nav_altitude_valid, "%u", a->nav_altitude);
|
||||||
if (a->category != 0xA0)
|
p = appendFATSVMeta(p, end, "nav_heading", a, &a->nav_heading_valid, "%.1f", a->nav_heading);
|
||||||
p = appendFATSVMeta(p, end, "category", a, &a->category_valid, "%02X", a->category);
|
p = appendFATSVMeta(p, end, "nav_modes", a, &a->nav_modes_valid, "{%s}", nav_modes_string(a->nav_modes));
|
||||||
p = appendFATSVMeta(p, end, "selAlt", a, &a->intent_altitude_valid,"%u", a->intent_altitude);
|
p = appendFATSVMeta(p, end, "nav_qnh", a, &a->nav_qnh_valid, "%.1f", a->nav_qnh);
|
||||||
p = appendFATSVMeta(p, end, "selHdg", a, &a->intent_heading_valid, "%.0f", a->intent_heading);
|
|
||||||
p = appendFATSVMeta(p, end, "selModes", a, &a->intent_modes_valid, "{%s}", intent_modes_string(a->intent_modes));
|
|
||||||
p = appendFATSVMeta(p, end, "qnh", a, &a->alt_setting_valid, "%.1f", a->alt_setting);
|
|
||||||
|
|
||||||
// if we didn't get anything interesting, bail out.
|
// if we didn't get anything interesting, bail out.
|
||||||
// We don't need to do anything special to unwind prepareWrite().
|
// We don't need to do anything special to unwind prepareWrite().
|
||||||
|
@ -2122,12 +2167,13 @@ static void writeFATSV()
|
||||||
else
|
else
|
||||||
fprintf(stderr, "fatsv: output too large (max %d, overran by %d)\n", TSV_MAX_PACKET_SIZE, (int) (p - end));
|
fprintf(stderr, "fatsv: output too large (max %d, overran by %d)\n", TSV_MAX_PACKET_SIZE, (int) (p - end));
|
||||||
|
|
||||||
a->fatsv_emitted_altitude = a->altitude;
|
a->fatsv_emitted_altitude_baro = a->altitude_baro;
|
||||||
a->fatsv_emitted_altitude_gnss = a->altitude_geom;
|
a->fatsv_emitted_altitude_geom = a->altitude_geom;
|
||||||
a->fatsv_emitted_baro_rate = a->baro_rate;
|
a->fatsv_emitted_baro_rate = a->baro_rate;
|
||||||
a->fatsv_emitted_geom_rate = a->geom_rate;
|
a->fatsv_emitted_geom_rate = a->geom_rate;
|
||||||
a->fatsv_emitted_speed = a->gs;
|
a->fatsv_emitted_gs = a->gs;
|
||||||
a->fatsv_emitted_speed_ias = a->ias;
|
a->fatsv_emitted_ias = a->ias;
|
||||||
|
a->fatsv_emitted_tas = a->tas;
|
||||||
a->fatsv_emitted_mach = a->mach;
|
a->fatsv_emitted_mach = a->mach;
|
||||||
a->fatsv_emitted_track = a->track;
|
a->fatsv_emitted_track = a->track;
|
||||||
a->fatsv_emitted_track_rate = a->track_rate;
|
a->fatsv_emitted_track_rate = a->track_rate;
|
||||||
|
@ -2135,12 +2181,19 @@ static void writeFATSV()
|
||||||
a->fatsv_emitted_mag_heading = a->mag_heading;
|
a->fatsv_emitted_mag_heading = a->mag_heading;
|
||||||
a->fatsv_emitted_true_heading = a->true_heading;
|
a->fatsv_emitted_true_heading = a->true_heading;
|
||||||
a->fatsv_emitted_airground = a->airground;
|
a->fatsv_emitted_airground = a->airground;
|
||||||
a->fatsv_emitted_intent_altitude = a->intent_altitude;
|
a->fatsv_emitted_nav_altitude = a->nav_altitude;
|
||||||
a->fatsv_emitted_intent_heading = a->intent_heading;
|
a->fatsv_emitted_nav_heading = a->nav_heading;
|
||||||
a->fatsv_emitted_intent_modes = a->intent_modes;
|
a->fatsv_emitted_nav_modes = a->nav_modes;
|
||||||
a->fatsv_emitted_alt_setting = a->alt_setting;
|
a->fatsv_emitted_nav_qnh = a->nav_qnh;
|
||||||
memcpy(a->fatsv_emitted_callsign, a->callsign, sizeof(a->fatsv_emitted_callsign));
|
memcpy(a->fatsv_emitted_callsign, a->callsign, sizeof(a->fatsv_emitted_callsign));
|
||||||
|
a->fatsv_emitted_addrtype = a->addrtype;
|
||||||
|
a->fatsv_emitted_adsb_version = a->adsb_version;
|
||||||
|
a->fatsv_emitted_category = a->category;
|
||||||
|
a->fatsv_emitted_squawk = a->squawk;
|
||||||
a->fatsv_last_emitted = now;
|
a->fatsv_last_emitted = now;
|
||||||
|
if (forceEmit) {
|
||||||
|
a->fatsv_last_force_emit = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
439
track.c
439
track.c
|
@ -76,15 +76,22 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
|
||||||
a->signalLevel[i] = 1e-5;
|
a->signalLevel[i] = 1e-5;
|
||||||
a->signalNext = 0;
|
a->signalNext = 0;
|
||||||
|
|
||||||
|
// defaults until we see a message otherwise
|
||||||
|
a->adsb_version = -1;
|
||||||
|
a->adsb_hrd = HEADING_MAGNETIC;
|
||||||
|
a->adsb_tah = HEADING_GROUND_TRACK;
|
||||||
|
|
||||||
|
// prime FATSV defaults we only emit on change
|
||||||
|
|
||||||
// start off with the "last emitted" ACAS RA being blank (just the BDS 3,0
|
// start off with the "last emitted" ACAS RA being blank (just the BDS 3,0
|
||||||
// or ES type code)
|
// or ES type code)
|
||||||
a->fatsv_emitted_bds_30[0] = 0x30;
|
a->fatsv_emitted_bds_30[0] = 0x30;
|
||||||
a->fatsv_emitted_es_acas_ra[0] = 0xE2;
|
a->fatsv_emitted_es_acas_ra[0] = 0xE2;
|
||||||
|
a->fatsv_emitted_adsb_version = -1;
|
||||||
|
a->fatsv_emitted_addrtype = ADDR_UNKNOWN;
|
||||||
|
|
||||||
// defaults until we see an op status message
|
// don't immediately emit, let some data build up
|
||||||
a->adsb_version = -1;
|
a->fatsv_last_emitted = a->fatsv_last_force_emit = messageNow();
|
||||||
a->adsb_hrd = HEADING_MAGNETIC;
|
|
||||||
a->adsb_tah = HEADING_GROUND_TRACK;
|
|
||||||
|
|
||||||
// Copy the first message so we can emit it later when a second message arrives.
|
// Copy the first message so we can emit it later when a second message arrives.
|
||||||
a->first_message = *mm;
|
a->first_message = *mm;
|
||||||
|
@ -92,7 +99,7 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
|
||||||
// initialize data validity ages
|
// initialize data validity ages
|
||||||
#define F(f,s,e) do { a->f##_valid.stale_interval = (s) * 1000; a->f##_valid.expire_interval = (e) * 1000; } while (0)
|
#define F(f,s,e) do { a->f##_valid.stale_interval = (s) * 1000; a->f##_valid.expire_interval = (e) * 1000; } while (0)
|
||||||
F(callsign, 60, 70); // ADS-B or Comm-B
|
F(callsign, 60, 70); // ADS-B or Comm-B
|
||||||
F(altitude, 15, 70); // ADS-B or Mode S
|
F(altitude_baro, 15, 70); // ADS-B or Mode S
|
||||||
F(altitude_geom, 60, 70); // ADS-B only
|
F(altitude_geom, 60, 70); // ADS-B only
|
||||||
F(geom_delta, 60, 70); // ADS-B only
|
F(geom_delta, 60, 70); // ADS-B only
|
||||||
F(gs, 60, 70); // ADS-B or Comm-B
|
F(gs, 60, 70); // ADS-B or Comm-B
|
||||||
|
@ -107,14 +114,22 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
|
||||||
F(baro_rate, 60, 70); // ADS-B or Comm-B
|
F(baro_rate, 60, 70); // ADS-B or Comm-B
|
||||||
F(geom_rate, 60, 70); // ADS-B or Comm-B
|
F(geom_rate, 60, 70); // ADS-B or Comm-B
|
||||||
F(squawk, 15, 70); // ADS-B or Mode S
|
F(squawk, 15, 70); // ADS-B or Mode S
|
||||||
F(category, 60, 70); // ADS-B only
|
|
||||||
F(airground, 15, 70); // ADS-B or Mode S
|
F(airground, 15, 70); // ADS-B or Mode S
|
||||||
F(alt_setting, 60, 70); // Comm-B only
|
F(nav_qnh, 60, 70); // Comm-B only
|
||||||
F(intent_altitude, 60, 70); // ADS-B or Comm-B
|
F(nav_altitude, 60, 70); // ADS-B or Comm-B
|
||||||
F(intent_modes, 60, 70); // ADS-B or Comm-B
|
F(nav_heading, 60, 70); // ADS-B or Comm-B
|
||||||
|
F(nav_modes, 60, 70); // ADS-B or Comm-B
|
||||||
F(cpr_odd, 60, 70); // ADS-B only
|
F(cpr_odd, 60, 70); // ADS-B only
|
||||||
F(cpr_even, 60, 70); // ADS-B only
|
F(cpr_even, 60, 70); // ADS-B only
|
||||||
F(position, 60, 70); // ADS-B only
|
F(position, 60, 70); // ADS-B only
|
||||||
|
F(nic_a, 60, 70); // ADS-B only
|
||||||
|
F(nic_c, 60, 70); // ADS-B only
|
||||||
|
F(nic_baro, 60, 70); // ADS-B only
|
||||||
|
F(nac_p, 60, 70); // ADS-B only
|
||||||
|
F(nac_v, 60, 70); // ADS-B only
|
||||||
|
F(sil, 60, 70); // ADS-B only
|
||||||
|
F(gva, 60, 70); // ADS-B only
|
||||||
|
F(sda, 60, 70); // ADS-B only
|
||||||
#undef F
|
#undef F
|
||||||
|
|
||||||
Modes.stats_current.unique_aircraft++;
|
Modes.stats_current.unique_aircraft++;
|
||||||
|
@ -150,8 +165,8 @@ static int accept_data(data_validity *d, datasource_t source)
|
||||||
|
|
||||||
d->source = source;
|
d->source = source;
|
||||||
d->updated = messageNow();
|
d->updated = messageNow();
|
||||||
d->stale = messageNow() + d->stale_interval;
|
d->stale = messageNow() + (d->stale_interval ? d->stale_interval : 60000);
|
||||||
d->expires = messageNow() + d->expire_interval;
|
d->expires = messageNow() + (d->expire_interval ? d->expire_interval : 70000);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,13 +302,16 @@ static int speed_check(struct aircraft *a, double lat, double lon, int surface)
|
||||||
return inrange;
|
return inrange;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nuc)
|
static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nic, unsigned *rc)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
int fflag = mm->cpr_odd;
|
int fflag = mm->cpr_odd;
|
||||||
int surface = (mm->cpr_type == CPR_SURFACE);
|
int surface = (mm->cpr_type == CPR_SURFACE);
|
||||||
|
|
||||||
*nuc = (a->cpr_even_nuc < a->cpr_odd_nuc ? a->cpr_even_nuc : a->cpr_odd_nuc); // worst of the two positions
|
// derive NIC, Rc from the worse of the two position
|
||||||
|
// smaller NIC is worse; larger Rc is worse
|
||||||
|
*nic = (a->cpr_even_nic < a->cpr_odd_nic ? a->cpr_even_nic : a->cpr_odd_nic);
|
||||||
|
*rc = (a->cpr_even_rc > a->cpr_odd_rc ? a->cpr_even_rc : a->cpr_odd_rc);
|
||||||
|
|
||||||
if (surface) {
|
if (surface) {
|
||||||
// surface global CPR
|
// surface global CPR
|
||||||
|
@ -303,8 +321,6 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat,
|
||||||
if (trackDataValid(&a->position_valid)) { // Ok to try aircraft relative first
|
if (trackDataValid(&a->position_valid)) { // Ok to try aircraft relative first
|
||||||
reflat = a->lat;
|
reflat = a->lat;
|
||||||
reflon = a->lon;
|
reflon = a->lon;
|
||||||
if (a->pos_nuc < *nuc)
|
|
||||||
*nuc = a->pos_nuc;
|
|
||||||
} else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) {
|
} else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) {
|
||||||
reflat = Modes.fUserLat;
|
reflat = Modes.fUserLat;
|
||||||
reflon = Modes.fUserLon;
|
reflon = Modes.fUserLon;
|
||||||
|
@ -356,7 +372,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat,
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// check speed limit
|
// check speed limit
|
||||||
if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, surface)) {
|
if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && a->pos_rc <= *rc && !speed_check(a, *lat, *lon, surface)) {
|
||||||
Modes.stats_current.cpr_global_speed_checks++;
|
Modes.stats_current.cpr_global_speed_checks++;
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
@ -364,7 +380,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nuc)
|
static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nic, unsigned *rc)
|
||||||
{
|
{
|
||||||
// relative CPR
|
// relative CPR
|
||||||
// find reference location
|
// find reference location
|
||||||
|
@ -374,14 +390,22 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat,
|
||||||
int fflag = mm->cpr_odd;
|
int fflag = mm->cpr_odd;
|
||||||
int surface = (mm->cpr_type == CPR_SURFACE);
|
int surface = (mm->cpr_type == CPR_SURFACE);
|
||||||
|
|
||||||
*nuc = mm->cpr_nucp;
|
if (fflag) {
|
||||||
|
*nic = a->cpr_odd_nic;
|
||||||
|
*rc = a->cpr_odd_rc;
|
||||||
|
} else {
|
||||||
|
*nic = a->cpr_even_nic;
|
||||||
|
*rc = a->cpr_even_rc;
|
||||||
|
}
|
||||||
|
|
||||||
if (trackDataValid(&a->position_valid)) {
|
if (trackDataValid(&a->position_valid)) {
|
||||||
reflat = a->lat;
|
reflat = a->lat;
|
||||||
reflon = a->lon;
|
reflon = a->lon;
|
||||||
|
|
||||||
if (a->pos_nuc < *nuc)
|
if (a->pos_nic < *nic)
|
||||||
*nuc = a->pos_nuc;
|
*nic = a->pos_nic;
|
||||||
|
if (a->pos_rc < *rc)
|
||||||
|
*rc = a->pos_rc;
|
||||||
|
|
||||||
range_limit = 50e3;
|
range_limit = 50e3;
|
||||||
} else if (!surface && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) {
|
} else if (!surface && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) {
|
||||||
|
@ -430,7 +454,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check speed limit
|
// check speed limit
|
||||||
if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, surface)) {
|
if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && a->pos_rc <= *rc && !speed_check(a, *lat, *lon, surface)) {
|
||||||
#ifdef DEBUG_CPR_CHECKS
|
#ifdef DEBUG_CPR_CHECKS
|
||||||
fprintf(stderr, "Speed check for %06X with local decoding failed\n", a->addr);
|
fprintf(stderr, "Speed check for %06X with local decoding failed\n", a->addr);
|
||||||
#endif
|
#endif
|
||||||
|
@ -454,7 +478,8 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm)
|
||||||
int location_result = -1;
|
int location_result = -1;
|
||||||
uint64_t 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_nic = 0;
|
||||||
|
unsigned new_rc = 0;
|
||||||
int surface;
|
int surface;
|
||||||
|
|
||||||
surface = (mm->cpr_type == CPR_SURFACE);
|
surface = (mm->cpr_type == CPR_SURFACE);
|
||||||
|
@ -463,7 +488,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm)
|
||||||
++Modes.stats_current.cpr_surface;
|
++Modes.stats_current.cpr_surface;
|
||||||
|
|
||||||
// Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
|
// Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
|
||||||
if (mm->gs_valid && mm->gs <= 25)
|
if (mm->gs_valid && mm->gs.selected <= 25)
|
||||||
max_elapsed = 50000;
|
max_elapsed = 50000;
|
||||||
else
|
else
|
||||||
max_elapsed = 25000;
|
max_elapsed = 25000;
|
||||||
|
@ -480,7 +505,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm)
|
||||||
a->cpr_odd_type == a->cpr_even_type &&
|
a->cpr_odd_type == a->cpr_even_type &&
|
||||||
time_between(a->cpr_odd_valid.updated, a->cpr_even_valid.updated) <= max_elapsed) {
|
time_between(a->cpr_odd_valid.updated, a->cpr_even_valid.updated) <= max_elapsed) {
|
||||||
|
|
||||||
location_result = doGlobalCPR(a, mm, &new_lat, &new_lon, &new_nuc);
|
location_result = doGlobalCPR(a, mm, &new_lat, &new_lon, &new_nic, &new_rc);
|
||||||
|
|
||||||
if (location_result == -2) {
|
if (location_result == -2) {
|
||||||
#ifdef DEBUG_CPR_CHECKS
|
#ifdef DEBUG_CPR_CHECKS
|
||||||
|
@ -511,7 +536,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm)
|
||||||
|
|
||||||
// Otherwise try relative CPR.
|
// Otherwise try relative CPR.
|
||||||
if (location_result == -1) {
|
if (location_result == -1) {
|
||||||
location_result = doLocalCPR(a, mm, &new_lat, &new_lon, &new_nuc);
|
location_result = doLocalCPR(a, mm, &new_lat, &new_lon, &new_nic, &new_rc);
|
||||||
|
|
||||||
if (location_result < 0) {
|
if (location_result < 0) {
|
||||||
Modes.stats_current.cpr_local_skipped++;
|
Modes.stats_current.cpr_local_skipped++;
|
||||||
|
@ -532,16 +557,247 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm)
|
||||||
mm->cpr_decoded = 1;
|
mm->cpr_decoded = 1;
|
||||||
mm->decoded_lat = new_lat;
|
mm->decoded_lat = new_lat;
|
||||||
mm->decoded_lon = new_lon;
|
mm->decoded_lon = new_lon;
|
||||||
|
mm->decoded_nic = new_nic;
|
||||||
|
mm->decoded_rc = new_rc;
|
||||||
|
|
||||||
// Update aircraft state
|
// Update aircraft state
|
||||||
a->lat = new_lat;
|
a->lat = new_lat;
|
||||||
a->lon = new_lon;
|
a->lon = new_lon;
|
||||||
a->pos_nuc = new_nuc;
|
a->pos_nic = new_nic;
|
||||||
|
a->pos_rc = new_rc;
|
||||||
|
|
||||||
update_range_histogram(new_lat, new_lon);
|
update_range_histogram(new_lat, new_lon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned compute_nic(unsigned metype, unsigned version, unsigned nic_a, unsigned nic_b, unsigned nic_c)
|
||||||
|
{
|
||||||
|
switch (metype) {
|
||||||
|
case 5: // surface
|
||||||
|
case 9: // airborne
|
||||||
|
case 20: // airborne, GNSS altitude
|
||||||
|
return 11;
|
||||||
|
|
||||||
|
case 6: // surface
|
||||||
|
case 10: // airborne
|
||||||
|
case 21: // airborne, GNSS altitude
|
||||||
|
return 10;
|
||||||
|
|
||||||
|
case 7: // surface
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && !nic_c) {
|
||||||
|
return 9;
|
||||||
|
} else {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
} else if (version == 1) {
|
||||||
|
if (nic_a) {
|
||||||
|
return 9;
|
||||||
|
} else {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 8: // surface
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && nic_c) {
|
||||||
|
return 7;
|
||||||
|
} else if (nic_a && !nic_c) {
|
||||||
|
return 6;
|
||||||
|
} else if (!nic_a && nic_c) {
|
||||||
|
return 6;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 11: // airborne
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && nic_b) {
|
||||||
|
return 9;
|
||||||
|
} else {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
} else if (version == 1) {
|
||||||
|
if (nic_a) {
|
||||||
|
return 9;
|
||||||
|
} else {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 12: // airborne
|
||||||
|
return 7;
|
||||||
|
|
||||||
|
case 13: // airborne
|
||||||
|
return 6;
|
||||||
|
|
||||||
|
case 14: // airborne
|
||||||
|
return 5;
|
||||||
|
|
||||||
|
case 15: // airborne
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case 16: // airborne
|
||||||
|
if (nic_a && nic_b) {
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 17: // airborne
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, unsigned nic_b, unsigned nic_c)
|
||||||
|
{
|
||||||
|
switch (metype) {
|
||||||
|
case 5: // surface
|
||||||
|
case 9: // airborne
|
||||||
|
case 20: // airborne, GNSS altitude
|
||||||
|
return 8; // 7.5m
|
||||||
|
|
||||||
|
case 6: // surface
|
||||||
|
case 10: // airborne
|
||||||
|
case 21: // airborne, GNSS altitude
|
||||||
|
return 25;
|
||||||
|
|
||||||
|
case 7: // surface
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && !nic_c) {
|
||||||
|
return 75;
|
||||||
|
} else {
|
||||||
|
return 186; // 185.2m, 0.1NM
|
||||||
|
}
|
||||||
|
} else if (version == 1) {
|
||||||
|
if (nic_a) {
|
||||||
|
return 75;
|
||||||
|
} else {
|
||||||
|
return 186; // 185.2m, 0.1NM
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 186; // 185.2m, 0.1NM
|
||||||
|
}
|
||||||
|
|
||||||
|
case 8: // surface
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && nic_c) {
|
||||||
|
return 371; // 370.4m, 0.2NM
|
||||||
|
} else if (nic_a && !nic_c) {
|
||||||
|
return 556; // 555.6m, 0.3NM
|
||||||
|
} else if (!nic_a && nic_c) {
|
||||||
|
return 926; // 926m, 0.5NM
|
||||||
|
} else {
|
||||||
|
return RC_UNKNOWN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return RC_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 11: // airborne
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && nic_b) {
|
||||||
|
return 75;
|
||||||
|
} else {
|
||||||
|
return 186; // 370.4m, 0.2NM
|
||||||
|
}
|
||||||
|
} else if (version == 1) {
|
||||||
|
if (nic_a) {
|
||||||
|
return 75;
|
||||||
|
} else {
|
||||||
|
return 186; // 370.4m, 0.2NM
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 186; // 370.4m, 0.2NM
|
||||||
|
}
|
||||||
|
|
||||||
|
case 12: // airborne
|
||||||
|
return 371; // 370.4m, 0.2NM
|
||||||
|
|
||||||
|
case 13: // airborne
|
||||||
|
if (version == 2) {
|
||||||
|
if (!nic_a && nic_b) {
|
||||||
|
return 556; // 555.6m, 0.3NM
|
||||||
|
} else if (!nic_a && !nic_b) {
|
||||||
|
return 926; // 926m, 0.5NM
|
||||||
|
} else if (nic_a && nic_b) {
|
||||||
|
return 1112; // 1111.2m, 0.6NM
|
||||||
|
} else {
|
||||||
|
return 1112; // bad combination, assume worst Rc
|
||||||
|
}
|
||||||
|
} else if (version == 1) {
|
||||||
|
if (nic_a) {
|
||||||
|
return 1112; // 1111.2m, 0.6NM
|
||||||
|
} else {
|
||||||
|
return 926; // 926m, 0.5NM
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 926; // 926m, 0.5NM
|
||||||
|
}
|
||||||
|
|
||||||
|
case 14: // airborne
|
||||||
|
return 1852; // 1.0NM
|
||||||
|
|
||||||
|
case 15: // airborne
|
||||||
|
return 3704; // 2NM
|
||||||
|
|
||||||
|
case 16: // airborne
|
||||||
|
if (version == 2) {
|
||||||
|
if (nic_a && nic_b) {
|
||||||
|
return 7408; // 4NM
|
||||||
|
} else {
|
||||||
|
return 14816; // 8NM
|
||||||
|
}
|
||||||
|
} else if (version == 1) {
|
||||||
|
if (nic_a) {
|
||||||
|
return 7408; // 4NM
|
||||||
|
} else {
|
||||||
|
return 14816; // 8NM
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 18510; // 10NM
|
||||||
|
}
|
||||||
|
|
||||||
|
case 17: // airborne
|
||||||
|
return 37040; // 20NM
|
||||||
|
|
||||||
|
default:
|
||||||
|
return RC_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compute_nic_rc_from_message(struct modesMessage *mm, struct aircraft *a, unsigned *nic, unsigned *rc)
|
||||||
|
{
|
||||||
|
int nic_a = (trackDataValid(&a->nic_a_valid) && a->nic_a);
|
||||||
|
int nic_b = (mm->accuracy.nic_b_valid && mm->accuracy.nic_b);
|
||||||
|
int nic_c = (trackDataValid(&a->nic_c_valid) && a->nic_c);
|
||||||
|
|
||||||
|
*nic = compute_nic(mm->metype, a->adsb_version, nic_a, nic_b, nic_c);
|
||||||
|
*rc = compute_rc(mm->metype, a->adsb_version, nic_a, nic_b, nic_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int altitude_to_feet(int raw, altitude_unit_t unit)
|
||||||
|
{
|
||||||
|
switch (unit) {
|
||||||
|
case UNIT_METERS:
|
||||||
|
return raw / 0.3048;
|
||||||
|
case UNIT_FEET:
|
||||||
|
return raw;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
//
|
//
|
||||||
|
@ -583,16 +839,34 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
if (mm->source == SOURCE_ADSB && a->adsb_version < 0)
|
if (mm->source == SOURCE_ADSB && a->adsb_version < 0)
|
||||||
a->adsb_version = 0;
|
a->adsb_version = 0;
|
||||||
|
|
||||||
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source)) {
|
// category shouldn't change over time, don't bother with metadata
|
||||||
|
if (mm->category_valid) {
|
||||||
|
a->category = mm->category;
|
||||||
|
}
|
||||||
|
|
||||||
|
// operational status message
|
||||||
|
// done early to update version / HRD / TAH
|
||||||
|
if (mm->opstatus.valid) {
|
||||||
|
a->adsb_version = mm->opstatus.version;
|
||||||
|
if (mm->opstatus.hrd != HEADING_INVALID) {
|
||||||
|
a->adsb_hrd = mm->opstatus.hrd;
|
||||||
|
}
|
||||||
|
if (mm->opstatus.tah != HEADING_INVALID) {
|
||||||
|
a->adsb_tah = mm->opstatus.tah;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->altitude_baro_valid && accept_data(&a->altitude_baro_valid, mm->source)) {
|
||||||
|
int alt = altitude_to_feet(mm->altitude_baro, mm->altitude_baro_unit);
|
||||||
if (a->modeC_hit) {
|
if (a->modeC_hit) {
|
||||||
int new_modeC = (a->altitude + 49) / 100;
|
int new_modeC = (a->altitude_baro + 49) / 100;
|
||||||
int old_modeC = (mm->altitude + 49) / 100;
|
int old_modeC = (alt + 49) / 100;
|
||||||
if (new_modeC != old_modeC) {
|
if (new_modeC != old_modeC) {
|
||||||
a->modeC_hit = 0;
|
a->modeC_hit = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a->altitude = mm->altitude;
|
a->altitude_baro = alt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source)) {
|
if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source)) {
|
||||||
|
@ -602,8 +876,8 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
a->squawk = mm->squawk;
|
a->squawk = mm->squawk;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GEOM && accept_data(&a->altitude_geom_valid, mm->source)) {
|
if (mm->altitude_geom_valid && accept_data(&a->altitude_geom_valid, mm->source)) {
|
||||||
a->altitude_geom = mm->altitude;
|
a->altitude_geom = altitude_to_feet(mm->altitude_geom, mm->altitude_geom_unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->geom_delta_valid && accept_data(&a->geom_delta_valid, mm->source)) {
|
if (mm->geom_delta_valid && accept_data(&a->geom_delta_valid, mm->source)) {
|
||||||
|
@ -635,8 +909,11 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
a->roll = mm->roll;
|
a->roll = mm->roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->gs_valid && accept_data(&a->gs_valid, mm->source)) {
|
if (mm->gs_valid) {
|
||||||
a->gs = mm->gs;
|
mm->gs.selected = (a->adsb_version == 2 ? mm->gs.v2 : mm->gs.v0);
|
||||||
|
if (accept_data(&a->gs_valid, mm->source)) {
|
||||||
|
a->gs = mm->gs.selected;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->ias_valid && accept_data(&a->ias_valid, mm->source)) {
|
if (mm->ias_valid && accept_data(&a->ias_valid, mm->source)) {
|
||||||
|
@ -659,10 +936,6 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
a->geom_rate = mm->geom_rate;
|
a->geom_rate = mm->geom_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->category_valid && accept_data(&a->category_valid, mm->source)) {
|
|
||||||
a->category = mm->category;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source)) {
|
if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source)) {
|
||||||
a->airground = mm->airground;
|
a->airground = mm->airground;
|
||||||
}
|
}
|
||||||
|
@ -673,22 +946,22 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
|
|
||||||
// prefer MCP over FMS
|
// prefer MCP over FMS
|
||||||
// unless the source says otherwise
|
// unless the source says otherwise
|
||||||
if (mm->intent.mcp_altitude_valid && mm->intent.altitude_source != INTENT_ALT_FMS && accept_data(&a->intent_altitude_valid, mm->source)) {
|
if (mm->nav.mcp_altitude_valid && mm->nav.altitude_source != NAV_ALT_FMS && accept_data(&a->nav_altitude_valid, mm->source)) {
|
||||||
a->intent_altitude = mm->intent.mcp_altitude;
|
a->nav_altitude = mm->nav.mcp_altitude;
|
||||||
} else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source)) {
|
} else if (mm->nav.fms_altitude_valid && accept_data(&a->nav_altitude_valid, mm->source)) {
|
||||||
a->intent_altitude = mm->intent.fms_altitude;
|
a->nav_altitude = mm->nav.fms_altitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source)) {
|
if (mm->nav.heading_valid && accept_data(&a->nav_heading_valid, mm->source)) {
|
||||||
a->intent_heading = mm->intent.heading;
|
a->nav_heading = mm->nav.heading;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->intent.modes_valid && accept_data(&a->intent_modes_valid, mm->source)) {
|
if (mm->nav.modes_valid && accept_data(&a->nav_modes_valid, mm->source)) {
|
||||||
a->intent_modes = mm->intent.modes;
|
a->nav_modes = mm->nav.modes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source)) {
|
if (mm->nav.qnh_valid && accept_data(&a->nav_qnh_valid, mm->source)) {
|
||||||
a->alt_setting = mm->intent.alt_setting;
|
a->nav_qnh = mm->nav.qnh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPR, even
|
// CPR, even
|
||||||
|
@ -696,7 +969,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
a->cpr_even_type = mm->cpr_type;
|
a->cpr_even_type = mm->cpr_type;
|
||||||
a->cpr_even_lat = mm->cpr_lat;
|
a->cpr_even_lat = mm->cpr_lat;
|
||||||
a->cpr_even_lon = mm->cpr_lon;
|
a->cpr_even_lon = mm->cpr_lon;
|
||||||
a->cpr_even_nuc = mm->cpr_nucp;
|
compute_nic_rc_from_message(mm, a, &a->cpr_even_nic, &a->cpr_even_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPR, odd
|
// CPR, odd
|
||||||
|
@ -704,26 +977,52 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
a->cpr_odd_type = mm->cpr_type;
|
a->cpr_odd_type = mm->cpr_type;
|
||||||
a->cpr_odd_lat = mm->cpr_lat;
|
a->cpr_odd_lat = mm->cpr_lat;
|
||||||
a->cpr_odd_lon = mm->cpr_lon;
|
a->cpr_odd_lon = mm->cpr_lon;
|
||||||
a->cpr_odd_nuc = mm->cpr_nucp;
|
compute_nic_rc_from_message(mm, a, &a->cpr_odd_nic, &a->cpr_odd_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// operational status message
|
if (mm->accuracy.sda_valid && accept_data(&a->sda_valid, mm->source)) {
|
||||||
if (mm->opstatus.valid) {
|
a->sda = mm->accuracy.sda;
|
||||||
a->adsb_version = mm->opstatus.version;
|
}
|
||||||
if (mm->opstatus.version > 0) {
|
|
||||||
a->adsb_hrd = mm->opstatus.hrd;
|
if (mm->accuracy.nic_a_valid && accept_data(&a->nic_a_valid, mm->source)) {
|
||||||
a->adsb_tah = mm->opstatus.tah;
|
a->nic_a = mm->accuracy.nic_a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.nic_c_valid && accept_data(&a->nic_c_valid, mm->source)) {
|
||||||
|
a->nic_c = mm->accuracy.nic_c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.nac_p_valid && accept_data(&a->nac_p_valid, mm->source)) {
|
||||||
|
a->nac_p = mm->accuracy.nac_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.nac_v_valid && accept_data(&a->nac_v_valid, mm->source)) {
|
||||||
|
a->nac_v = mm->accuracy.nac_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.sil_valid && accept_data(&a->sil_valid, mm->source)) {
|
||||||
|
a->sil = mm->accuracy.sil;
|
||||||
|
if (mm->accuracy.sil_type != SIL_INVALID) {
|
||||||
|
a->sil_type = mm->accuracy.sil_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.gva_valid && accept_data(&a->gva_valid, mm->source)) {
|
||||||
|
a->gva = mm->accuracy.gva;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm->accuracy.sda_valid && accept_data(&a->sda_valid, mm->source)) {
|
||||||
|
a->sda = mm->accuracy.sda;
|
||||||
|
}
|
||||||
|
|
||||||
// Now handle derived data
|
// Now handle derived data
|
||||||
|
|
||||||
// derive geometric altitude if we have baro + delta
|
// derive geometric altitude if we have baro + delta
|
||||||
if (compare_validity(&a->altitude_valid, &a->altitude_geom_valid) > 0 &&
|
if (compare_validity(&a->altitude_baro_valid, &a->altitude_geom_valid) > 0 &&
|
||||||
compare_validity(&a->geom_delta_valid, &a->altitude_geom_valid) > 0) {
|
compare_validity(&a->geom_delta_valid, &a->altitude_geom_valid) > 0) {
|
||||||
// Baro and delta are both more recent than geometric, derive geometric from baro + delta
|
// Baro and delta are both more recent than geometric, derive geometric from baro + delta
|
||||||
a->altitude_geom = a->altitude + a->geom_delta;
|
a->altitude_geom = a->altitude_baro + a->geom_delta;
|
||||||
combine_validity(&a->altitude_geom_valid, &a->altitude_valid, &a->geom_delta_valid);
|
combine_validity(&a->altitude_geom_valid, &a->altitude_baro_valid, &a->geom_delta_valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've got a new cprlat or cprlon
|
// If we've got a new cprlat or cprlon
|
||||||
|
@ -762,8 +1061,8 @@ static void trackMatchAC(uint64_t now)
|
||||||
}
|
}
|
||||||
|
|
||||||
// match on Mode C (+/- 100ft)
|
// match on Mode C (+/- 100ft)
|
||||||
if (trackDataValid(&a->altitude_valid)) {
|
if (trackDataValid(&a->altitude_baro_valid)) {
|
||||||
int modeC = (a->altitude + 49) / 100;
|
int modeC = (a->altitude_baro + 49) / 100;
|
||||||
|
|
||||||
unsigned modeA = modeCToModeA(modeC);
|
unsigned modeA = modeCToModeA(modeC);
|
||||||
unsigned i = modeAToIndex(modeA);
|
unsigned i = modeAToIndex(modeA);
|
||||||
|
@ -844,7 +1143,7 @@ static void trackRemoveStaleAircraft(uint64_t now)
|
||||||
|
|
||||||
#define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0)
|
#define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0)
|
||||||
EXPIRE(callsign);
|
EXPIRE(callsign);
|
||||||
EXPIRE(altitude);
|
EXPIRE(altitude_baro);
|
||||||
EXPIRE(altitude_geom);
|
EXPIRE(altitude_geom);
|
||||||
EXPIRE(geom_delta);
|
EXPIRE(geom_delta);
|
||||||
EXPIRE(gs);
|
EXPIRE(gs);
|
||||||
|
@ -859,15 +1158,21 @@ static void trackRemoveStaleAircraft(uint64_t now)
|
||||||
EXPIRE(baro_rate);
|
EXPIRE(baro_rate);
|
||||||
EXPIRE(geom_rate);
|
EXPIRE(geom_rate);
|
||||||
EXPIRE(squawk);
|
EXPIRE(squawk);
|
||||||
EXPIRE(category);
|
|
||||||
EXPIRE(airground);
|
EXPIRE(airground);
|
||||||
EXPIRE(alt_setting);
|
EXPIRE(nav_qnh);
|
||||||
EXPIRE(intent_altitude);
|
EXPIRE(nav_altitude);
|
||||||
EXPIRE(intent_heading);
|
EXPIRE(nav_heading);
|
||||||
EXPIRE(intent_modes);
|
EXPIRE(nav_modes);
|
||||||
EXPIRE(cpr_odd);
|
EXPIRE(cpr_odd);
|
||||||
EXPIRE(cpr_even);
|
EXPIRE(cpr_even);
|
||||||
EXPIRE(position);
|
EXPIRE(position);
|
||||||
|
EXPIRE(nic_a);
|
||||||
|
EXPIRE(nic_c);
|
||||||
|
EXPIRE(nic_baro);
|
||||||
|
EXPIRE(nac_p);
|
||||||
|
EXPIRE(sil);
|
||||||
|
EXPIRE(gva);
|
||||||
|
EXPIRE(sda);
|
||||||
#undef EXPIRE
|
#undef EXPIRE
|
||||||
prev = a; a = a->next;
|
prev = a; a = a->next;
|
||||||
}
|
}
|
||||||
|
|
78
track.h
78
track.h
|
@ -64,6 +64,9 @@
|
||||||
*/
|
*/
|
||||||
#define TRACK_MODEAC_MIN_MESSAGES 4
|
#define TRACK_MODEAC_MIN_MESSAGES 4
|
||||||
|
|
||||||
|
/* Special value for Rc unknown (100NM) */
|
||||||
|
#define RC_UNKNOWN 185200
|
||||||
|
|
||||||
// data moves through three states:
|
// data moves through three states:
|
||||||
// fresh: data is valid. Updates from a less reliable source are not accepted.
|
// fresh: data is valid. Updates from a less reliable source are not accepted.
|
||||||
// stale: data is valid. Updates from a less reliable source are accepted.
|
// stale: data is valid. Updates from a less reliable source are accepted.
|
||||||
|
@ -92,8 +95,8 @@ struct aircraft {
|
||||||
data_validity callsign_valid;
|
data_validity callsign_valid;
|
||||||
char callsign[9]; // Flight number
|
char callsign[9]; // Flight number
|
||||||
|
|
||||||
data_validity altitude_valid;
|
data_validity altitude_baro_valid;
|
||||||
int altitude; // Altitude (Baro)
|
int altitude_baro; // Altitude (Baro)
|
||||||
|
|
||||||
data_validity altitude_geom_valid;
|
data_validity altitude_geom_valid;
|
||||||
int altitude_geom; // Altitude (Geometric)
|
int altitude_geom; // Altitude (Geometric)
|
||||||
|
@ -102,7 +105,7 @@ struct aircraft {
|
||||||
int geom_delta; // Difference between Geometric and Baro altitudes
|
int geom_delta; // Difference between Geometric and Baro altitudes
|
||||||
|
|
||||||
data_validity gs_valid;
|
data_validity gs_valid;
|
||||||
unsigned gs;
|
float gs;
|
||||||
|
|
||||||
data_validity ias_valid;
|
data_validity ias_valid;
|
||||||
unsigned ias;
|
unsigned ias;
|
||||||
|
@ -137,49 +140,71 @@ struct aircraft {
|
||||||
data_validity squawk_valid;
|
data_validity squawk_valid;
|
||||||
unsigned squawk; // Squawk
|
unsigned squawk; // Squawk
|
||||||
|
|
||||||
data_validity category_valid;
|
|
||||||
unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte
|
unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte
|
||||||
|
|
||||||
data_validity airground_valid;
|
data_validity airground_valid;
|
||||||
airground_t airground; // air/ground status
|
airground_t airground; // air/ground status
|
||||||
|
|
||||||
data_validity alt_setting_valid;
|
data_validity nav_qnh_valid;
|
||||||
float alt_setting; // Altimeter setting (QNH/QFE), millibars
|
float nav_qnh; // Altimeter setting (QNH/QFE), millibars
|
||||||
|
|
||||||
data_validity intent_altitude_valid;
|
data_validity nav_altitude_valid;
|
||||||
unsigned intent_altitude; // intent altitude (FMS or FCU selected altitude)
|
unsigned nav_altitude; // FMS or FCU selected altitude
|
||||||
|
|
||||||
data_validity intent_heading_valid;
|
data_validity nav_heading_valid;
|
||||||
float intent_heading; // intent heading, degrees (0-359)
|
float nav_heading; // target heading, degrees (0-359)
|
||||||
|
|
||||||
data_validity intent_modes_valid;
|
data_validity nav_modes_valid;
|
||||||
intent_modes_t intent_modes; // enabled modes (autopilot, vnav, etc)
|
nav_modes_t nav_modes; // enabled modes (autopilot, vnav, etc)
|
||||||
|
|
||||||
data_validity cpr_odd_valid; // Last seen even CPR message
|
data_validity cpr_odd_valid; // Last seen even CPR message
|
||||||
cpr_type_t cpr_odd_type;
|
cpr_type_t cpr_odd_type;
|
||||||
unsigned cpr_odd_lat;
|
unsigned cpr_odd_lat;
|
||||||
unsigned cpr_odd_lon;
|
unsigned cpr_odd_lon;
|
||||||
unsigned cpr_odd_nuc;
|
unsigned cpr_odd_nic;
|
||||||
|
unsigned cpr_odd_rc;
|
||||||
|
|
||||||
data_validity cpr_even_valid; // Last seen odd CPR message
|
data_validity cpr_even_valid; // Last seen odd CPR message
|
||||||
cpr_type_t cpr_even_type;
|
cpr_type_t cpr_even_type;
|
||||||
unsigned cpr_even_lat;
|
unsigned cpr_even_lat;
|
||||||
unsigned cpr_even_lon;
|
unsigned cpr_even_lon;
|
||||||
unsigned cpr_even_nuc;
|
unsigned cpr_even_nic;
|
||||||
|
unsigned cpr_even_rc;
|
||||||
|
|
||||||
data_validity position_valid;
|
data_validity position_valid;
|
||||||
double lat, lon; // Coordinated obtained from CPR encoded data
|
double lat, lon; // Coordinated obtained from CPR encoded data
|
||||||
unsigned pos_nuc; // NUCp of last computed position
|
unsigned pos_nic; // NIC of last computed position
|
||||||
|
unsigned pos_rc; // Rc of last computed position
|
||||||
|
|
||||||
|
// data extracted from opstatus etc
|
||||||
int adsb_version; // ADS-B version (from ADS-B operational status); -1 means no ADS-B messages seen
|
int adsb_version; // ADS-B version (from ADS-B operational status); -1 means no ADS-B messages seen
|
||||||
heading_type_t adsb_hrd; // Heading Reference Direction setting (from ADS-B operational status)
|
heading_type_t adsb_hrd; // Heading Reference Direction setting (from ADS-B operational status)
|
||||||
heading_type_t adsb_tah; // Track Angle / Heading setting (from ADS-B operational status)
|
heading_type_t adsb_tah; // Track Angle / Heading setting (from ADS-B operational status)
|
||||||
|
|
||||||
|
data_validity nic_a_valid;
|
||||||
|
data_validity nic_c_valid;
|
||||||
|
data_validity nic_baro_valid;
|
||||||
|
data_validity nac_p_valid;
|
||||||
|
data_validity nac_v_valid;
|
||||||
|
data_validity sil_valid;
|
||||||
|
data_validity gva_valid;
|
||||||
|
data_validity sda_valid;
|
||||||
|
|
||||||
|
unsigned nic_a : 1; // NIC supplement A from opstatus
|
||||||
|
unsigned nic_c : 1; // NIC supplement C from opstatus
|
||||||
|
unsigned nic_baro : 1; // NIC baro supplement from TSS or opstatus
|
||||||
|
unsigned nac_p : 4; // NACp from TSS or opstatus
|
||||||
|
unsigned nac_v : 3; // NACv from opstatus
|
||||||
|
unsigned sil : 2; // SIL from TS or opstatus
|
||||||
|
sil_type_t sil_type; // SIL supplement from TS or opstatus
|
||||||
|
unsigned gva : 2; // GVA from opstatus
|
||||||
|
unsigned sda : 2; // SDA from opstatus
|
||||||
|
|
||||||
int modeA_hit; // did our squawk match a possible mode A reply in the last check period?
|
int modeA_hit; // did our squawk match a possible mode A reply in the last check period?
|
||||||
int modeC_hit; // did our altitude match a possible mode C reply in the last check period?
|
int modeC_hit; // did our altitude match a possible mode C reply in the last check period?
|
||||||
|
|
||||||
int fatsv_emitted_altitude; // last FA emitted altitude
|
int fatsv_emitted_altitude_baro; // last FA emitted altitude
|
||||||
int fatsv_emitted_altitude_gnss; // -"- GNSS altitude
|
int fatsv_emitted_altitude_geom; // -"- GNSS altitude
|
||||||
int fatsv_emitted_baro_rate; // -"- barometric rate
|
int fatsv_emitted_baro_rate; // -"- barometric rate
|
||||||
int fatsv_emitted_geom_rate; // -"- geometric rate
|
int fatsv_emitted_geom_rate; // -"- geometric rate
|
||||||
float fatsv_emitted_track; // -"- true track
|
float fatsv_emitted_track; // -"- true track
|
||||||
|
@ -187,22 +212,27 @@ struct aircraft {
|
||||||
float fatsv_emitted_mag_heading; // -"- magnetic heading
|
float fatsv_emitted_mag_heading; // -"- magnetic heading
|
||||||
float fatsv_emitted_true_heading; // -"- true heading
|
float fatsv_emitted_true_heading; // -"- true heading
|
||||||
float fatsv_emitted_roll; // -"- roll angle
|
float fatsv_emitted_roll; // -"- roll angle
|
||||||
unsigned fatsv_emitted_speed; // -"- groundspeed
|
float fatsv_emitted_gs; // -"- groundspeed
|
||||||
unsigned fatsv_emitted_speed_ias; // -"- IAS
|
unsigned fatsv_emitted_ias; // -"- IAS
|
||||||
unsigned fatsv_emitted_speed_tas; // -"- TAS
|
unsigned fatsv_emitted_tas; // -"- TAS
|
||||||
float fatsv_emitted_mach; // -"- Mach number
|
float fatsv_emitted_mach; // -"- Mach number
|
||||||
airground_t fatsv_emitted_airground; // -"- air/ground state
|
airground_t fatsv_emitted_airground; // -"- air/ground state
|
||||||
unsigned fatsv_emitted_intent_altitude; // -"- intent altitude
|
unsigned fatsv_emitted_nav_altitude; // -"- target altitude
|
||||||
float fatsv_emitted_intent_heading; // -"- intent heading
|
float fatsv_emitted_nav_heading; // -"- target heading
|
||||||
intent_modes_t fatsv_emitted_intent_modes; // -"- enabled modes
|
nav_modes_t fatsv_emitted_nav_modes; // -"- enabled navigation modes
|
||||||
float fatsv_emitted_alt_setting; // -"- altimeter setting
|
float fatsv_emitted_nav_qnh; // -"- altimeter setting
|
||||||
unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message
|
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_bds_30[7]; // -"- BDS 3,0 message
|
||||||
unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message
|
unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message
|
||||||
unsigned char fatsv_emitted_es_acas_ra[7]; // -"- ES ACAS RA report message
|
unsigned char fatsv_emitted_es_acas_ra[7]; // -"- ES ACAS RA report message
|
||||||
char fatsv_emitted_callsign[9]; // -"- callsign
|
char fatsv_emitted_callsign[9]; // -"- callsign
|
||||||
|
addrtype_t fatsv_emitted_addrtype; // -"- address type (assumed ADSB_ICAO initially)
|
||||||
|
int fatsv_emitted_adsb_version; // -"- ADS-B version (assumed non-ADS-B initially)
|
||||||
|
unsigned fatsv_emitted_category; // -"- ADS-B emitter category (assumed A0 initially)
|
||||||
|
unsigned fatsv_emitted_squawk; // -"- squawk
|
||||||
|
|
||||||
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted
|
uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted
|
||||||
|
uint64_t fatsv_last_force_emit; // time (millis) we last emitted only-on-change data
|
||||||
|
|
||||||
struct aircraft *next; // Next aircraft in our linked list
|
struct aircraft *next; // Next aircraft in our linked list
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue