This commit is contained in:
Oliver Jowett 2017-12-07 10:34:08 -06:00
parent 6986b3847f
commit 161d2d8ded
8 changed files with 1088 additions and 421 deletions

590
mode_s.c
View file

@ -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) {
int gspeed;
static float decodeMovementFieldV2(unsigned movement) {
// Note : movement codes 0,125,126,127 are all invalid, but they are
// trapped for before this function is called.
if (movement > 123) gspeed = 199; // > 175kt
else if (movement > 108) gspeed = ((movement - 108) * 5) + 100;
else if (movement > 93) gspeed = ((movement - 93) * 2) + 70;
else if (movement > 38) gspeed = ((movement - 38) ) + 15;
else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2;
else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1;
else gspeed = 0;
// Each movement value is a range of speeds;
// we return the midpoint of the range (rounded to the nearest integer)
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 >= 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
@ -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) {
mm->AC = getbits(msg, 20, 32);
if (mm->AC) { // Only attempt to decode if a valid (non zero) altitude is present
mm->altitude = decodeAC13Field(mm->AC, &mm->altitude_unit);
if (mm->altitude != INVALID_ALTITUDE)
mm->altitude_valid = 1;
mm->altitude_source = ALTITUDE_BARO;
mm->altitude_baro = decodeAC13Field(mm->AC, &mm->altitude_baro_unit);
if (mm->altitude_baro != INVALID_ALTITUDE)
mm->altitude_baro_valid = 1;
}
}
@ -729,30 +755,31 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
// Airborne Velocity Message
unsigned char *me = mm->ME;
// 1-5: ME type
// 6-8: ME subtype
mm->mesub = getbits(me, 6, 8);
if (check_imf && getbit(me, 9))
setIMF(mm);
if (mm->mesub < 1 || mm->mesub > 4)
return;
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;
}
}
// 9: IMF or Intent Change
if (check_imf && getbit(me, 9))
setIMF(mm);
// 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) {
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 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);
// 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;
if (mm->gs) {
if (mm->gs.selected > 0) {
float ground_track = atan2(ew_vel, ns_vel) * 180.0 / M_PI;
// We don't want negative values but a 0-360 scale
if (ground_track < 0)
@ -779,6 +806,16 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
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);
if (airspeed) {
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;
}
}
// 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);
if (raw_delta) {
mm->geom_delta_valid = 1;
@ -812,28 +864,37 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf)
// Surface position and movement
unsigned char *me = mm->ME;
if (check_imf && getbit(me, 21))
setIMF(mm);
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_type = CPR_SURFACE;
// 6-12: Movement
unsigned movement = getbits(me, 6, 12);
if (movement > 0 && movement < 125) {
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)) {
mm->heading_valid = 1;
mm->heading = getbits(me, 14, 20) * 360.0 / 128.0;
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)
@ -841,20 +902,34 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf)
// Airborne position and altitude
unsigned char *me = mm->ME;
if (check_imf && getbit(me, 8))
setIMF(mm);
// 6-7: surveillance status
// 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);
if (mm->metype == 0) {
mm->cpr_nucp = 0;
// no position information
} else {
// Catch some common failure modes and don't mark them as valid
// (so they won't be used for positioning)
// 21: T flag (UTC sync or not)
// 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_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) {
// Seen from at least:
// 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_type = CPR_AIRBORNE;
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
mm->altitude = decodeAC12Field(AC12Field, &mm->altitude_unit);
if (mm->altitude != INVALID_ALTITUDE) {
mm->altitude_valid = 1;
altitude_unit_t unit;
int alt = decodeAC12Field(AC12Field, &unit);
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))
setIMF(mm);
if (mm->mesub == 0) { // Target state and status, V1
// TODO: need RTCA/DO-260A
} else if (mm->mesub == 1) { // Target state and status, V2
mm->intent.valid = 1;
if (mm->mesub == 0 && getbit(me, 11) == 0) { // Target state and status, V1
// 8-9: vertical source
switch (getbits(me, 8, 9)) {
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
unsigned is_fms = getbit(me, 9);
unsigned alt_bits = getbits(me, 10, 20);
if (alt_bits != 0) {
if (is_fms) {
mm->intent.fms_altitude_valid = 1;
mm->intent.fms_altitude = (alt_bits - 1) * 32;
mm->nav.fms_altitude_valid = 1;
mm->nav.fms_altitude = (alt_bits - 1) * 32;
} else {
mm->intent.mcp_altitude_valid = 1;
mm->intent.mcp_altitude = (alt_bits - 1) * 32;
mm->nav.mcp_altitude_valid = 1;
mm->nav.mcp_altitude = (alt_bits - 1) * 32;
}
}
unsigned baro_bits = getbits(me, 21, 29);
if (baro_bits != 0) {
mm->intent.alt_setting_valid = 1;
mm->intent.alt_setting = 800.0 + (baro_bits - 1) * 0.8;
mm->nav.qnh_valid = 1;
mm->nav.qnh = 800.0 + (baro_bits - 1) * 0.8;
}
if (getbit(me, 30)) {
mm->intent.heading_valid = 1;
mm->nav.heading_valid = 1;
// two's complement -180..+180, which is conveniently
// 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
// 44: NICbaro
// 45-46: SIL
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: mode bits validity
if (getbit(me, 47)) {
mm->intent.modes_valid = 1;
mm->intent.modes =
(getbit(me, 48) ? INTENT_MODE_AUTOPILOT : 0) |
(getbit(me, 49) ? INTENT_MODE_VNAV : 0) |
(getbit(me, 50) ? INTENT_MODE_ALT_HOLD : 0) |
// 48-54: mode bits
mm->nav.modes_valid = 1;
mm->nav.modes =
(getbit(me, 48) ? NAV_MODE_AUTOPILOT : 0) |
(getbit(me, 49) ? NAV_MODE_VNAV : 0) |
(getbit(me, 50) ? NAV_MODE_ALT_HOLD : 0) |
// 51: IMF
(getbit(me, 52) ? INTENT_MODE_APPROACH : 0) |
// 53: TCAS operational
(getbit(me, 54) ? INTENT_MODE_LNAV : 0);
(getbit(me, 52) ? NAV_MODE_APPROACH : 0) |
(getbit(me, 53) ? NAV_MODE_TCAS : 0) |
(getbit(me, 54) ? NAV_MODE_LNAV : 0);
}
// 55-56 reserved
@ -999,6 +1208,10 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf)
switch (mm->opstatus.version) {
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;
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.nic_supp_a = getbit(me, 44);
mm->opstatus.nac_p = getbits(me, 45, 48);
mm->opstatus.sil = getbits(me, 51, 52);
mm->accuracy.nic_a_valid = 1;
mm->accuracy.nic_a = getbit(me, 44);
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;
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 {
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
}
break;
case 2:
default:
if (getbits(me, 25, 26) == 0) {
mm->opstatus.om_acas_ra = getbit(me, 27);
mm->opstatus.om_ident = getbit(me, 28);
mm->opstatus.om_atc = getbit(me, 29);
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
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_arv = getbit(me, 15);
mm->opstatus.cc_ts = getbit(me, 16);
mm->opstatus.cc_tc = getbits(me, 17, 18);
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
mm->opstatus.cc_poa = getbit(me, 11);
mm->opstatus.cc_1090_in = getbit(me, 12);
mm->opstatus.cc_b2_low = getbit(me, 15);
mm->opstatus.cc_uat_in = getbit(me, 16);
mm->opstatus.cc_nac_v = getbits(me, 17, 19);
mm->opstatus.cc_nic_supp_c = getbit(me, 20);
mm->accuracy.nac_v_valid = 1;
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 = getbits(me, 21, 24);
mm->opstatus.cc_antenna_offset = getbits(me, 33, 40);
}
mm->opstatus.nic_supp_a = getbit(me, 44);
mm->opstatus.nac_p = getbits(me, 45, 48);
mm->opstatus.sil = getbits(me, 51, 52);
mm->accuracy.nic_a_valid = 1;
mm->accuracy.nic_a = getbit(me, 44);
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;
if (mm->mesub == 0) {
mm->opstatus.gva = getbits(me, 49, 50);
mm->opstatus.nic_baro = getbit(me, 53);
mm->accuracy.gva_valid = 1;
mm->accuracy.gva = getbits(me, 49, 50);
mm->accuracy.nic_baro_valid = 1;
mm->accuracy.nic_baro = getbit(me, 53);
} else {
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;
}
}
@ -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) {
switch (airground) {
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];
buf[0] = 0;
if (flags & INTENT_MODE_AUTOPILOT)
if (flags & NAV_MODE_AUTOPILOT)
strcat(buf, "autopilot ");
if (flags & INTENT_MODE_VNAV)
if (flags & NAV_MODE_VNAV)
strcat(buf, "vnav ");
if (flags & INTENT_MODE_ALT_HOLD)
if (flags & NAV_MODE_ALT_HOLD)
strcat(buf, "althold ");
if (flags & INTENT_MODE_APPROACH)
if (flags & NAV_MODE_APPROACH)
strcat(buf, "approach ");
if (flags & INTENT_MODE_LNAV)
if (flags & NAV_MODE_LNAV)
strcat(buf, "lnav ");
if (flags & NAV_MODE_TCAS)
strcat(buf, "tcas ");
if (buf[0] != 0)
buf[strlen(buf)-1] = 0;
@ -1617,11 +1834,16 @@ void displayModesMessage(struct modesMessage *mm) {
airground_to_string(mm->airground));
}
if (mm->altitude_valid) {
printf(" Altitude: %d %s %s\n",
mm->altitude,
altitude_unit_to_string(mm->altitude_unit),
altitude_source_to_string(mm->altitude_source));
if (mm->altitude_baro_valid) {
printf(" Baro altitude: %d %s\n",
mm->altitude_baro,
altitude_unit_to_string(mm->altitude_baro_unit));
}
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) {
@ -1642,7 +1864,14 @@ void displayModesMessage(struct modesMessage *mm) {
}
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) {
@ -1682,21 +1911,24 @@ void displayModesMessage(struct modesMessage *mm) {
if (mm->cpr_valid) {
printf(" CPR type: %s\n"
" CPR odd flag: %s\n"
" CPR NUCp/NIC: %u\n",
" CPR odd flag: %s\n",
cpr_type_to_string(mm->cpr_type),
mm->cpr_odd ? "odd" : "even",
mm->cpr_nucp);
mm->cpr_odd ? "odd" : "even");
if (mm->cpr_decoded) {
printf(" CPR latitude: %.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->cpr_lat,
mm->decoded_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 {
printf(" CPR latitude: (%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) {
printf(" Aircraft Operational Status:\n");
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_poa) printf("POA ");
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_antenna_offset) printf("GPS-OFFSET=%d ", mm->opstatus.cc_antenna_offset);
printf("\n");
@ -1731,53 +1991,43 @@ void displayModesMessage(struct modesMessage *mm) {
if (mm->opstatus.om_ident) printf("IDENT ");
if (mm->opstatus.om_atc) printf("ATC ");
if (mm->opstatus.om_saf) printf("SAF ");
if (mm->opstatus.om_sda) printf("SDA=%d ", mm->opstatus.om_sda);
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)
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));
}
if (mm->intent.valid) {
printf(" Intent:\n");
if (mm->intent.heading_valid)
printf(" Selected heading: %.1f\n", mm->intent.heading);
if (mm->intent.fms_altitude_valid)
printf(" FMS selected altitude: %u ft\n", mm->intent.fms_altitude);
if (mm->intent.mcp_altitude_valid)
printf(" MCP selected altitude: %u ft\n", mm->intent.mcp_altitude);
if (mm->intent.alt_setting_valid)
printf(" Altimeter setting: %.1f millibars\n", mm->intent.alt_setting);
if (mm->intent.altitude_source != INTENT_ALT_INVALID) {
printf(" Target altitude source: ");
switch (mm->intent.altitude_source) {
case INTENT_ALT_AIRCRAFT:
printf("aircraft altitude\n");
break;
case INTENT_ALT_MCP:
printf("MCP selected altitude\n");
break;
case INTENT_ALT_FMS:
printf("FMS selected altitude\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.heading_valid)
printf(" Selected heading: %.1f\n", mm->nav.heading);
if (mm->nav.fms_altitude_valid)
printf(" FMS selected altitude: %u ft\n", mm->nav.fms_altitude);
if (mm->nav.mcp_altitude_valid)
printf(" MCP selected altitude: %u ft\n", mm->nav.mcp_altitude);
if (mm->nav.qnh_valid)
printf(" QNH: %.1f millibars\n", mm->nav.qnh);
if (mm->nav.altitude_source != NAV_ALT_INVALID) {
printf(" Target altitude source: ");
switch (mm->nav.altitude_source) {
case NAV_ALT_AIRCRAFT:
printf("aircraft altitude\n");
break;
case NAV_ALT_MCP:
printf("MCP selected altitude\n");
break;
case NAV_ALT_FMS:
printf("FMS selected altitude\n");
break;
default:
printf("unknown\n");
}
}
if (mm->nav.modes_valid) {
printf(" Nav modes: %s\n", nav_modes_to_string(mm->nav.modes));
}
printf("\n");
fflush(stdout);
}