diff --git a/comm_b.c b/comm_b.c index 196ec14..a9b08fb 100644 --- a/comm_b.c +++ b/comm_b.c @@ -29,8 +29,10 @@ static int decodeBDS17(struct modesMessage *mm, bool store); static int decodeBDS20(struct modesMessage *mm, bool store); static int decodeBDS30(struct modesMessage *mm, bool store); static int decodeBDS40(struct modesMessage *mm, bool store); +static int decodeBDS44(struct modesMessage *mm, bool store); static int decodeBDS50(struct modesMessage *mm, bool store); static int decodeBDS60(struct modesMessage *mm, bool store); +static int decodeBDS05(struct modesMessage *mm, bool store); static CommBDecoderFn comm_b_decoders[] = { &decodeEmptyResponse, @@ -40,17 +42,18 @@ static CommBDecoderFn comm_b_decoders[] = { &decodeBDS17, &decodeBDS40, &decodeBDS50, - &decodeBDS60 + &decodeBDS60, + &decodeBDS44, + &decodeBDS05 }; void decodeCommB(struct modesMessage *mm) { - mm->commb_format = COMMB_UNKNOWN; - // If DR or UM are set, this message is _probably_ noise // as nothing really seems to use the multisite broadcast stuff? // Also skip anything that had errors corrected if (mm->DR != 0 || mm->UM != 0 || mm->correctedbits > 0) { + mm->commb_format = COMMB_NOT_DECODED; return; } @@ -77,12 +80,39 @@ void decodeCommB(struct modesMessage *mm) // decode it bestDecoder(mm, true); } + } else { + mm->commb_format = COMMB_UNKNOWN; } } static int decodeEmptyResponse(struct modesMessage *mm, bool store) { - for (unsigned i = 0; i < 7; ++i) { + // 00000000000000 is a common response. Ignore it. + // + // Also, it's common to see responses that look like this: + // 40000000000000 + // 50000000000000 + // 60000000000000 + // typically in grouped bursts (one of each message) from + // the same aircraft. + // + // I speculate that these are response to interrogations for + // BDS 4,0 5,0 and 6,0 respectively where the transponder + // doesn't support the register or has no data loaded for it. + // Treat them like empty responses. + + switch (mm->MB[0]) { + case 0x00: + case 0x40: + case 0x50: + case 0x60: + break; + + default: + return 0; + } + + for (unsigned i = 1; i < 7; ++i) { if (mm->MB[i] != 0) { return 0; } @@ -148,10 +178,10 @@ static int decodeBDS17(struct modesMessage *mm, bool store) score -= 2; } if (getbit(msg, 13)) { // 4,4 meterological routine report - score -= 2; + score -= 1; } if (getbit(msg, 14)) { // 4,4 meterological hazard report - score -= 2; + score -= 1; } if (getbit(msg, 20)) { // 5,4 waypoint 1 score -= 2; @@ -173,8 +203,11 @@ static int decodeBDS17(struct modesMessage *mm, bool store) } else if (!getbit(msg, 1) && !getbit(msg, 2) && !getbit(msg, 3) && !getbit(msg, 4) && !getbit(msg, 5) && !getbit(msg, 6)) { // not ES capable score += 1; + } else if (!getbit(msg, 1) && !getbit(msg, 2) && getbit(msg, 3) && getbit(msg, 4) && getbit(msg, 5)) { + // ES with no position data + score += 3; } else { - // partial ES support, unlikely + // other combinations, unlikely score -= 12; } @@ -740,3 +773,213 @@ static int decodeBDS60(struct modesMessage *mm, bool store) return score; } + +// BDS4,4 Meterological routine air report +static int decodeBDS44(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + unsigned source = getbits(msg, 1, 4); + + unsigned wind_valid = getbit(msg, 5); + unsigned windspeed_raw = getbits(msg, 6, 14); + unsigned winddir_raw = getbits(msg, 15, 23); + + // ICAO 9871 is inconsistent, it claims: + // bit 24 sign + // bits 25..34 static air temperature, MSB = 64C, LSB=0.25C, range -128C..+128C + // + // .. but this does not actually work, there is one too many bits in the bitfield + // for the claimed values. + // + // Based on observed data, the most plausible actual layout is: + // + // bit 24 status + // bit 25 sign + // bits 26..34 static air temperature, MSB=64C, LSB=0.25C, range -128C..+128C + + unsigned sat_valid = getbit(msg, 24); + unsigned sat_sign = getbit(msg, 25); + unsigned sat_raw = getbits(msg, 26, 34); + + unsigned asp_valid = getbit(msg, 35); + unsigned asp_raw = getbits(msg, 36, 46); + + unsigned turbulence_valid = getbit(msg, 47); + unsigned turbulence_raw = getbits(msg, 48, 49); + + unsigned humidity_valid = getbit(msg, 50); + unsigned humidity_raw = getbits(msg, 51, 56); + + if (source == MRAR_SOURCE_INVALID || source >= MRAR_SOURCE_RESERVED) + return 0; // invalid or reserved source + + if (!wind_valid || !sat_valid) + return 0; // all valid messages seen in the wild have at least temp + wind + + if (!asp_valid && asp_raw != 0) + return 0; // ASP not valid, but non-zero values in the ASP field + + if (!turbulence_valid && turbulence_raw != 0) + return 0; // turbulence not valid, but non-zero values in the turbulence field + + if (!humidity_valid && humidity_raw != 0) + return 0; // humidity not valid, but non-zero values in the humidity field + + int score = 0; + + float wind_speed = 0; + float wind_dir = 0; + if (wind_valid) { + wind_dir = winddir_raw * (180.0 / 256.0); + wind_speed = windspeed_raw; + + if (windspeed_raw == 0) // possible but uncommon + score += 2; + else if (wind_speed <= 250) + score += 19; + else + return 0; + } else { + score += 1; + } + + float sat = 0; + if (sat_valid) { + sat = sat_raw * 0.25; + if (sat_sign) + sat -= 128; + if (sat == 0) // possible but uncommon + score += 2; + else if (sat >= -80 && sat <= 60) + score += 11; + else + return 0; + } else { + score += 1; + } + + float asp = 0; + if (asp_valid) { + asp = asp_raw; + if (asp >= 25 && asp <= 1100) + score += 12; + else + return 0; + } else { + score += 1; + } + + hazard_t turbulence = HAZARD_NIL; + if (turbulence_valid) { + turbulence = (hazard_t) turbulence_raw; + score += 3; + } else { + score += 1; + } + + float humidity = 0; + if (humidity_valid) { + humidity = humidity_raw * (100.0 / 64.0); + score += 7; + } else { + score += 1; + } + + if (source == MRAR_SOURCE_DMEDME && wind_valid && sat_valid && score > 0) { + // Some GICB messages can be easily mistaken for a MRAR: + // + // GICB bit 1: BDS 0,5 ES airborne position = 0 ] + // GICB bit 2: BDS 0,6 ES surface position = 0 ] + // GICB bit 3: BDS 0,7 ES status = 1 ] + // GICB bit 4: BDS 0,8 ES type & identification = 1 ] -> MRAR source = 3 + // GICB bit 5: BDS 0,9 ES airborne velocity = 1 -> MRAR wind valid bit + // GICB bit 24: BDS 6,0 heading and speed report = 1 -> MRAR temp valid bit + // most trailing bits = 0 + // + // so only treat this as MRAR as a last resort + score = 1; + } + + if (store) { + mm->commb_format = COMMB_MRAR; + mm->mrar_source_valid = 1; + mm->mrar_source = (mrar_source_t) source; + + if (wind_valid) { + mm->wind_valid = 1; + mm->wind_speed = wind_speed; + mm->wind_dir = wind_dir; + } + + if (sat_valid) { + mm->temperature_valid = 1; + mm->temperature = sat; + } + + if (asp_valid) { + mm->pressure_valid = 1; + mm->pressure = asp; + } + + if (turbulence_valid) { + mm->turbulence_valid = 1; + mm->turbulence = turbulence; + } + + if (humidity_valid) { + mm->humidity_valid = 1; + mm->humidity = humidity; + } + } + + return score; +} + +// BDS0,5 extended squitter airborne position +// (apparently this gets queried via comm-b sometimes??) +// We don't try to _use_ this as a position, but we can +// at least try to recognize it, to exclude other +// comm-b types (in particular they can be mistaken for MRAR) +static int decodeBDS05(struct modesMessage *mm, bool store) +{ + // We recognize these by matching the position altitude against + // the altitude in the surrounding message, so we need a + // DF20 not a DF21 + if (mm->msgtype != 20) + return 0; + + unsigned char *msg = mm->MB; + + unsigned typecode = getbits(msg, 1, 5); + if (typecode < 9 || typecode > 18) + return 0; // only consider typecodes that could be an airborne position with baro altitude + + unsigned t_bit = getbit(msg, 21); + if (t_bit) // unlikely + return 0; + + unsigned ac12 = getbits(msg, 9, 20); + if (!ac12) + return 0; + + // Insert M=0 to make an AC13 value, match against the + // AC13 value in the surrounding message + unsigned ac13 = ((ac12 & 0x0FC0) << 1) | (ac12 & 0x003F); + if (mm->AC != ac13) + return 0; // no altitude match + + unsigned lat = getbits(msg, 23, 39); + unsigned lon = getbits(msg, 40, 56); + if (lat == 0 || lon == 0) // unlikely position + return 0; + + if (store) { + mm->commb_format = COMMB_AIRBORNE_POSITION; + // No further decoding done, we don't really trust this + // enough to use as real input to CPR + } + + // Score this high enough to override everything else + return 100; +} diff --git a/dump1090.h b/dump1090.h index 2beeea7..0ec6def 100644 --- a/dump1090.h +++ b/dump1090.h @@ -202,6 +202,7 @@ typedef enum { typedef enum { COMMB_UNKNOWN, COMMB_AMBIGUOUS, + COMMB_NOT_DECODED, COMMB_EMPTY_RESPONSE, COMMB_DATALINK_CAPS, COMMB_GICB_CAPS, @@ -209,7 +210,9 @@ typedef enum { COMMB_ACAS_RA, COMMB_VERTICAL_INTENT, COMMB_TRACK_TURN, - COMMB_HEADING_SPEED + COMMB_HEADING_SPEED, + COMMB_MRAR, + COMMB_AIRBORNE_POSITION } commb_format_t; typedef enum { @@ -237,6 +240,24 @@ typedef enum { NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS } nav_altitude_source_t; +// BDS4,4 MRAR - FOM/Source values +typedef enum { + MRAR_SOURCE_INVALID = 0, + MRAR_SOURCE_INS = 1, + MRAR_SOURCE_GNSS = 2, + MRAR_SOURCE_DMEDME = 3, + MRAR_SOURCE_VORDME = 4, + MRAR_SOURCE_RESERVED = 5 +} mrar_source_t; + +// BDS4,4 and BDS4,5 hazard reports +typedef enum { + HAZARD_NIL = 0, + HAZARD_LIGHT = 1, + HAZARD_MODERATE = 2, + HAZARD_SEVERE = 3 +} hazard_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds @@ -364,6 +385,7 @@ struct _Modes { // Internal state uint64_t json_stats_interval; // Interval between rewriting the json stats file, in milliseconds int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact double faup_rate_multiplier; // Multiplier to adjust rate of faup1090 messages emitted + bool faup_upload_unknown_commb; // faup1090: should we upload Comm-B messages that weren't in a recognized format? int json_aircraft_history_next; struct { @@ -611,6 +633,22 @@ struct modesMessage { nav_modes_t modes; } nav; + + // BDS 4,4 MRAR + unsigned mrar_source_valid : 1; + unsigned wind_valid : 1; + unsigned temperature_valid : 1; + unsigned pressure_valid : 1; + unsigned turbulence_valid : 1; + unsigned humidity_valid : 1; + + mrar_source_t mrar_source; + float wind_speed; // kts + float wind_dir; // degrees + float temperature; // degrees C + float pressure; // hPa + hazard_t turbulence; // NIL/LIGHT/MODERATE/SEVERE + float humidity; // 0-100 % }; // This one needs modesMessage: diff --git a/mode_s.c b/mode_s.c index 617a23c..6509abc 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1681,6 +1681,8 @@ static const char *commb_format_to_string(commb_format_t format) { return "empty response"; case COMMB_AMBIGUOUS: return "ambiguous format"; + case COMMB_NOT_DECODED: + return "not decoded"; case COMMB_DATALINK_CAPS: return "BDS1,0 Datalink capabilities"; case COMMB_GICB_CAPS: @@ -1695,6 +1697,10 @@ static const char *commb_format_to_string(commb_format_t format) { return "BDS5,0 Track and turn report"; case COMMB_HEADING_SPEED: return "BDS6,0 Heading and speed report"; + case COMMB_MRAR: + return "BDS4,4 Meterological routine air report"; + case COMMB_AIRBORNE_POSITION: + return "BDS0,5 Extended squitter airborne position"; default: return "unknown format"; } @@ -1748,6 +1754,29 @@ static const char *emergency_to_string(emergency_t emergency) } } +static const char *mrar_source_to_string(mrar_source_t source) +{ + switch (source) { + case MRAR_SOURCE_INVALID: return "invalid"; + case MRAR_SOURCE_INS: return "INS"; + case MRAR_SOURCE_GNSS: return "GNSS"; + case MRAR_SOURCE_DMEDME: return "DME/DME"; + case MRAR_SOURCE_VORDME: return "VOR/DME"; + default: return "reserved"; + } +} + +static const char *hazard_to_string(hazard_t hazard) +{ + switch (hazard) { + case HAZARD_NIL: return "nil"; + case HAZARD_LIGHT: return "light"; + case HAZARD_MODERATE: return "moderate"; + case HAZARD_SEVERE: return "severe"; + default: return "invalid hazard severity"; + } +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -2213,6 +2242,21 @@ void displayModesMessage(struct modesMessage *mm) { printf(" Emergency/priority: %s\n", emergency_to_string(mm->emergency)); } + if (mm->mrar_source_valid) + printf(" MRAR FOM/Source: %s\n", mrar_source_to_string(mm->mrar_source)); + if (mm->wind_valid) { + printf(" Wind speed: %.0f kt\n", mm->wind_speed); + printf(" Wind direction: %.1f degrees\n", mm->wind_dir); + } + if (mm->temperature_valid) + printf(" Air temperature: %.1f degrees C\n", mm->temperature); + if (mm->pressure_valid) + printf(" Static pressure: %.0f hPa\n", mm->pressure); + if (mm->turbulence_valid) + printf(" Turbulence: %s\n", hazard_to_string(mm->turbulence)); + if (mm->humidity_valid) + printf(" Humidity: %.0f%%\n", mm->humidity); + printf("\n"); fflush(stdout); } diff --git a/net_io.c b/net_io.c index 2077a2e..d66dad7 100644 --- a/net_io.c +++ b/net_io.c @@ -1128,7 +1128,7 @@ static int handleFaupCommand(struct client *c, char *p) { // Traverse through message for commands while (msg_field != NULL) { - if (strcmp(msg_field, "upload_rate_multiplier") == 0) { + if (!strcmp(msg_field, "upload_rate_multiplier")) { msg_field = strtok (NULL, "\t"); multiplier = atof(msg_field); @@ -1142,6 +1142,14 @@ static int handleFaupCommand(struct client *c, char *p) { Modes.faup_rate_multiplier = multiplier; break; } + + if (!strcmp(msg_field, "upload_unknown_commb")) { + msg_field = strtok (NULL, "\t"); + unsigned enable = atoi(msg_field); + fprintf(stderr, "handleFaupCommand(): %s upload of unknown Comm-B messages\n", enable ? "Enabling" : "Disabling"); + Modes.faup_upload_unknown_commb = enable; + break; + } msg_field = strtok (NULL, "\t"); } @@ -1606,6 +1614,29 @@ static const char *nav_altitude_source_enum_string(nav_altitude_source_t src) } } +static const char *mrar_source_enum_string(mrar_source_t src) +{ + switch (src) { + case MRAR_SOURCE_INVALID: return "invalid"; + case MRAR_SOURCE_INS: return "ins"; + case MRAR_SOURCE_GNSS: return "gnss"; + case MRAR_SOURCE_DMEDME: return "dmedme"; + case MRAR_SOURCE_VORDME: return "vordme"; + default: return "reserved"; + } +} + +static const char *hazard_enum_string(hazard_t hazard) +{ + switch (hazard) { + case HAZARD_NIL: return "nil"; + case HAZARD_LIGHT: return "light"; + case HAZARD_MODERATE: return "moderate"; + case HAZARD_SEVERE: return "severe"; + default: return "invalid"; + } +} + char *generateAircraftJson(const char *url_path, int *len) { uint64_t now = mstime(); struct aircraft *a; @@ -1709,12 +1740,23 @@ char *generateAircraftJson(const char *url_path, int *len) { p = safe_snprintf(p, end, ",\"gva\":%u", a->gva); if (trackDataValid(&a->sda_valid)) p = safe_snprintf(p, end, ",\"sda\":%u", a->sda); + if (trackDataValid(&a->mrar_source_valid)) + p = safe_snprintf(p, end, ",\"mrar_source\":\"%s\"", mrar_source_enum_string(a->mrar_source)); + if (trackDataValid(&a->wind_valid)) + p = safe_snprintf(p, end, ",\"wind_speed\":%.0f,\"wind_dir\":%.1f", a->wind_speed, a->wind_dir); + if (trackDataValid(&a->temperature_valid)) + p = safe_snprintf(p, end, ",\"temperature\":%.2f", a->temperature); + if (trackDataValid(&a->pressure_valid)) + p = safe_snprintf(p, end, ",\"pressure\":%.0f", a->pressure); + if (trackDataValid(&a->turbulence_valid)) + p = safe_snprintf(p, end, ",\"turbulence\":\"%s\"", hazard_enum_string(a->turbulence)); + if (trackDataValid(&a->humidity_valid)) + p = safe_snprintf(p, end, ",\"humidity\":%.1f", a->humidity); if (a->modeA_hit) p = safe_snprintf(p, end, ",\"modea\":true"); if (a->modeC_hit) p = safe_snprintf(p, end, ",\"modec\":true"); - p = safe_snprintf(p, end, ",\"mlat\":"); p = append_flags(p, end, a, SOURCE_MLAT); p = safe_snprintf(p, end, ",\"tisb\":"); @@ -2259,7 +2301,7 @@ __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *en } #define TSV_MAX_PACKET_SIZE 800 -#define TSV_VERSION "8E" +#define TSV_VERSION "9E" static void writeFATSVPositionUpdate(float lat, float lon, float alt) { @@ -2354,6 +2396,23 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) } break; + case COMMB_GICB_CAPS: + // BDS 1,7: common usage GICB capability report + if (memcmp(mm->MB, a->fatsv_emitted_bds_17, 7) != 0) { + memcpy(a->fatsv_emitted_bds_17, mm->MB, 7); + writeFATSVEventMessage(mm, "gicb_caps", mm->MB, 7); + } + break; + + case COMMB_UNKNOWN: + // If enabled, upload raw unrecognized Comm-B messages + // for server-side analysis + if (Modes.faup_upload_unknown_commb && memcmp(mm->MB, a->fatsv_emitted_unknown_commb, 7) != 0) { + memcpy(a->fatsv_emitted_unknown_commb, mm->MB, 7); + writeFATSVEventMessage(mm, "unknown_commb", mm->MB, 7); + } + break; + default: // nothing break; @@ -2528,7 +2587,13 @@ static void writeFATSV() (airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || (airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE) || (squawkValid && a->squawk != a->fatsv_emitted_squawk) || - (trackDataValid(&a->emergency_valid) && a->emergency != a->fatsv_emitted_emergency); + (trackDataValid(&a->emergency_valid) && a->emergency != a->fatsv_emitted_emergency) || + (trackDataValid(&a->mrar_source_valid) && a->mrar_source_valid.updated > a->fatsv_last_emitted) || + (trackDataValid(&a->wind_valid) && a->wind_valid.updated > a->fatsv_last_emitted) || + (trackDataValid(&a->pressure_valid) && a->pressure_valid.updated > a->fatsv_last_emitted) || + (trackDataValid(&a->temperature_valid) && a->temperature_valid.updated > a->fatsv_last_emitted) || + (trackDataValid(&a->turbulence_valid) && a->turbulence_valid.updated > a->fatsv_last_emitted) || + (trackDataValid(&a->humidity_valid) && a->humidity_valid.updated > a->fatsv_last_emitted); uint64_t minAge; double adjustedMinAge; @@ -2630,10 +2695,17 @@ static void writeFATSV() p = appendFATSVMeta(p, end, "nav_alt_mcp", a, &a->nav_altitude_mcp_valid, "%u", a->nav_altitude_mcp); p = appendFATSVMeta(p, end, "nav_alt_fms", a, &a->nav_altitude_fms_valid, "%u", a->nav_altitude_fms); p = appendFATSVMeta(p, end, "nav_alt_src", a, &a->nav_altitude_src_valid, "%s", nav_altitude_source_enum_string(a->nav_altitude_src)); - p = appendFATSVMeta(p, end, "nav_heading", a, &a->nav_heading_valid, "%.1f", a->nav_heading); - p = appendFATSVMeta(p, end, "nav_modes", a, &a->nav_modes_valid, "{%s}", nav_modes_flags_string(a->nav_modes)); - p = appendFATSVMeta(p, end, "nav_qnh", a, &a->nav_qnh_valid, "%.1f", a->nav_qnh); - p = appendFATSVMeta(p, end, "emergency", a, &a->emergency_valid, "%s", emergency_enum_string(a->emergency)); + p = appendFATSVMeta(p, end, "nav_heading", a, &a->nav_heading_valid, "%.1f", a->nav_heading); + p = appendFATSVMeta(p, end, "nav_modes", a, &a->nav_modes_valid, "{%s}", nav_modes_flags_string(a->nav_modes)); + p = appendFATSVMeta(p, end, "nav_qnh", a, &a->nav_qnh_valid, "%.1f", a->nav_qnh); + p = appendFATSVMeta(p, end, "emergency", a, &a->emergency_valid, "%s", emergency_enum_string(a->emergency)); + p = appendFATSVMeta(p, end, "mrar_source", a, &a->mrar_source_valid, "%s", mrar_source_enum_string(a->mrar_source)); + p = appendFATSVMeta(p, end, "wind_speed", a, &a->wind_valid, "%.0f", a->wind_speed); + p = appendFATSVMeta(p, end, "wind_dir", a, &a->wind_valid, "%.1f", a->wind_dir); + p = appendFATSVMeta(p, end, "temperature", a, &a->temperature_valid, "%.2f", a->temperature); + p = appendFATSVMeta(p, end, "pressure", a, &a->pressure_valid, "%.0f", a->pressure); + p = appendFATSVMeta(p, end, "turbulence", a, &a->turbulence_valid, "%s", hazard_enum_string(a->turbulence)); + p = appendFATSVMeta(p, end, "humidity", a, &a->humidity_valid, "%.0f", a->humidity); // if we didn't get anything interesting, bail out. // We don't need to do anything special to unwind prepareWrite(). diff --git a/track.c b/track.c index aab634e..ac9590d 100644 --- a/track.c +++ b/track.c @@ -130,6 +130,12 @@ static struct aircraft *trackCreateAircraft(struct modesMessage *mm) { F(sil, 60, 70); // ADS-B only F(gva, 60, 70); // ADS-B only F(sda, 60, 70); // ADS-B only + F(mrar_source, 60, 70); // Comm-B only + F(wind, 60, 70); // Comm-B only + F(temperature, 60, 70); // Comm-B only + F(pressure, 60, 70); // Comm-B only + F(turbulence, 60, 70); // Comm-B only + F(humidity, 60, 70); // Comm-B only #undef F Modes.stats_current.unique_aircraft++; @@ -1237,6 +1243,31 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->sda = mm->accuracy.sda; } + if (mm->mrar_source_valid && accept_data(&a->mrar_source_valid, mm->source)) { + a->mrar_source = mm->mrar_source; + } + + if (mm->wind_valid && accept_data(&a->wind_valid, mm->source)) { + a->wind_speed = mm->wind_speed; + a->wind_dir = mm->wind_dir; + } + + if (mm->temperature_valid && accept_data(&a->temperature_valid, mm->source)) { + a->temperature = mm->temperature; + } + + if (mm->pressure_valid && accept_data(&a->pressure_valid, mm->source)) { + a->pressure = mm->pressure; + } + + if (mm->turbulence_valid && accept_data(&a->turbulence_valid, mm->source)) { + a->turbulence = mm->turbulence; + } + + if (mm->humidity_valid && accept_data(&a->humidity_valid, mm->source)) { + a->humidity = mm->humidity; + } + // Now handle derived data // derive geometric altitude if we have baro + delta @@ -1400,6 +1431,12 @@ static void trackRemoveStaleAircraft(uint64_t now) EXPIRE(sil); EXPIRE(gva); EXPIRE(sda); + EXPIRE(mrar_source); + EXPIRE(wind); + EXPIRE(temperature); + EXPIRE(pressure); + EXPIRE(turbulence); + EXPIRE(humidity); #undef EXPIRE prev = a; a = a->next; } diff --git a/track.h b/track.h index ade1267..8968833 100644 --- a/track.h +++ b/track.h @@ -226,6 +226,22 @@ struct aircraft { unsigned gva : 2; // GVA from opstatus unsigned sda : 2; // SDA from opstatus + // data extracted from MRAR + data_validity mrar_source_valid; + data_validity wind_valid; // speed and direction + data_validity pressure_valid; + data_validity temperature_valid; + data_validity turbulence_valid; + data_validity humidity_valid; + + mrar_source_t mrar_source; + float wind_speed; + float wind_dir; + float pressure; + float temperature; + hazard_t turbulence; + float humidity; + 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? @@ -250,7 +266,9 @@ struct aircraft { nav_modes_t fatsv_emitted_nav_modes; // -"- enabled navigation modes float fatsv_emitted_nav_qnh; // -"- altimeter setting unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message + unsigned char fatsv_emitted_bds_17[7]; // -"- BDS 1,7 message unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message + unsigned char fatsv_emitted_unknown_commb[7]; // -"- unrecognized Comm-B 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 char fatsv_emitted_callsign[9]; // -"- callsign