From 5a22d6115e415a167b827f07f4ba58cbcfd88401 Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 00:40:44 +0100 Subject: [PATCH 1/8] Add max_distance to stats.json --- net_io.c | 8 ++++++-- stats.c | 6 ++++++ stats.h | 1 + track.c | 19 ++++++++++++++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/net_io.c b/net_io.c index 1f47666d..2893c0e4 100644 --- a/net_io.c +++ b/net_io.c @@ -1677,7 +1677,9 @@ static char * appendStatsJson(char *p, ",\"cpu\":{\"demod\":%llu,\"reader\":%llu,\"background\":%llu}" ",\"tracks\":{\"all\":%u" ",\"single_message\":%u}" - ",\"messages\":%u}", + ",\"messages\":%u" + ",\"max_distance_in_metres\":%ld" + ",\"max_distance_in_nautical_miles\":%.1lf}", st->cpr_surface, st->cpr_airborne, st->cpr_global_ok, @@ -1698,7 +1700,9 @@ static char * appendStatsJson(char *p, (unsigned long long) background_cpu_millis, st->unique_aircraft, st->single_message_aircraft, - st->messages_total); + st->messages_total, + (long) st->longest_distance, + st->longest_distance / 1852.0); } return p; diff --git a/stats.c b/stats.c index dc863279..9a003872 100644 --- a/stats.c +++ b/stats.c @@ -331,4 +331,10 @@ void add_stats(const struct stats *st1, const struct stats *st2, struct stats *t // range histogram for (i = 0; i < RANGE_BUCKET_COUNT; ++i) target->range_histogram[i] = st1->range_histogram[i] + st2->range_histogram[i]; + + // Longest Distance observed + if (st1->longest_distance > st2->longest_distance) + target->longest_distance = st1->longest_distance; + else + target->longest_distance = st2->longest_distance; } diff --git a/stats.h b/stats.h index 6f5f818e..3cf2586c 100644 --- a/stats.h +++ b/stats.h @@ -115,6 +115,7 @@ struct stats // range histogram #define RANGE_BUCKET_COUNT 76 uint32_t range_histogram[RANGE_BUCKET_COUNT]; + double longest_distance; // Longest range decoded, in *metres* uint32_t padding; }; diff --git a/track.c b/track.c index 2db6d0aa..c802ce42 100644 --- a/track.c +++ b/track.c @@ -240,8 +240,19 @@ static double greatcircle(double lat0, double lon0, double lat1, double lon1) { } static void update_range_histogram(double lat, double lon) { - if (Modes.stats_range_histo && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { - double range = greatcircle(Modes.fUserLat, Modes.fUserLon, lat, lon); + double range = 0; + int valid_latlon = Modes.bUserFlags & MODES_USER_LATLON_VALID; + + if (!valid_latlon) + return; + + range = greatcircle(Modes.fUserLat, Modes.fUserLon, lat, lon); + + if ((range <= Modes.maxRange || Modes.maxRange == 0) && range > Modes.stats_current.longest_distance) { + Modes.stats_current.longest_distance = range; + } + + if (Modes.stats_range_histo) { int bucket = round(range / Modes.maxRange * RANGE_BUCKET_COUNT); if (bucket < 0) @@ -614,7 +625,9 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) { a->pos_nic = new_nic; a->pos_rc = new_rc; - update_range_histogram(new_lat, new_lon); + if (a->pos_reliable_odd >= 2 && a->pos_reliable_even >= 2 && mm->source == SOURCE_ADSB) { + update_range_histogram(new_lat, new_lon); + } } } From 857ebc6b5921d58c1a06b4d8c1f2501b4afd40a9 Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 00:40:50 +0100 Subject: [PATCH 2/8] Use multiple linked lists for the aircraft list This improves performance for receivers tracking a large number of aircraft. --- interactive.c | 124 +++++------ net_io.c | 554 +++++++++++++++++++++++++------------------------- readsb.c | 12 +- readsb.h | 4 +- track.c | 196 +++++++++--------- viewadsb.c | 12 +- 6 files changed, 459 insertions(+), 443 deletions(-) diff --git a/interactive.c b/interactive.c index 08b83f12..d521d2b3 100644 --- a/interactive.c +++ b/interactive.c @@ -97,7 +97,6 @@ void interactiveCleanup(void) { } void interactiveShowData(void) { - struct aircraft *a = Modes.aircrafts; static uint64_t next_update; uint64_t now = mstime(); char progress; @@ -115,69 +114,72 @@ void interactiveShowData(void) { int rows = getmaxy(stdscr); int row = 2; - while (a && row < rows) { - - if ((now - a->seen) < Modes.interactive_display_ttl) { - int msgs = a->messages; - - if (msgs > 1) { - char strSquawk[5] = " "; - char strFl[7] = " "; - char strTt[5] = " "; - char strGs[5] = " "; - - if (trackDataValid(&a->squawk_valid)) { - snprintf(strSquawk, 5, "%04x", a->squawk); - } - - if (trackDataValid(&a->gs_valid)) { - snprintf(strGs, 5, "%3d", convert_speed(a->gs)); - } - - if (trackDataValid(&a->track_valid)) { - snprintf(strTt, 5, "%03.0f", a->track); - } - - if (msgs > 99999) { - msgs = 99999; + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + struct aircraft *a = Modes.aircrafts[j]; + while (a && row < rows) { + + if ((now - a->seen) < Modes.interactive_display_ttl) { + int msgs = a->messages; + + if (msgs > 1) { + char strSquawk[5] = " "; + char strFl[7] = " "; + char strTt[5] = " "; + char strGs[5] = " "; + + if (trackDataValid(&a->squawk_valid)) { + snprintf(strSquawk, 5, "%04x", a->squawk); + } + + if (trackDataValid(&a->gs_valid)) { + snprintf(strGs, 5, "%3d", convert_speed(a->gs)); + } + + if (trackDataValid(&a->track_valid)) { + snprintf(strTt, 5, "%03.0f", a->track); + } + + if (msgs > 99999) { + msgs = 99999; + } + + char strMode[5] = " "; + char strLat[8] = " "; + char strLon[9] = " "; + double * pSig = a->signalLevel; + double signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + + pSig[4] + pSig[5] + pSig[6] + pSig[7]) / 8.0; + + strMode[0] = 'S'; + if (a->modeA_hit) { + strMode[2] = 'a'; + } + if (a->modeC_hit) { + strMode[3] = 'c'; + } + + if (trackDataValid(&a->position_valid)) { + snprintf(strLat, 8, "%7.03f", a->lat); + snprintf(strLon, 9, "%8.03f", a->lon); + } + + if (trackDataValid(&a->airground_valid) && a->airground == AG_GROUND) { + snprintf(strFl, 7, " grnd"); + } else if (Modes.use_gnss && trackDataValid(&a->altitude_geom_valid)) { + snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_geom)); + } else if (trackDataValid(&a->altitude_baro_valid)) { + 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", + (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : " ", (a->addr & 0xffffff), + strMode, strSquawk, a->callsign, strFl, strGs, strTt, + strLat, strLon, 10 * log10(signalAverage), msgs, (now - a->seen) / 1000.0); + ++row; } - - char strMode[5] = " "; - char strLat[8] = " "; - char strLon[9] = " "; - double * pSig = a->signalLevel; - double signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] + - pSig[4] + pSig[5] + pSig[6] + pSig[7]) / 8.0; - - strMode[0] = 'S'; - if (a->modeA_hit) { - strMode[2] = 'a'; - } - if (a->modeC_hit) { - strMode[3] = 'c'; - } - - if (trackDataValid(&a->position_valid)) { - snprintf(strLat, 8, "%7.03f", a->lat); - snprintf(strLon, 9, "%8.03f", a->lon); - } - - if (trackDataValid(&a->airground_valid) && a->airground == AG_GROUND) { - snprintf(strFl, 7, " grnd"); - } else if (Modes.use_gnss && trackDataValid(&a->altitude_geom_valid)) { - snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_geom)); - } else if (trackDataValid(&a->altitude_baro_valid)) { - 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", - (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : " ", (a->addr & 0xffffff), - strMode, strSquawk, a->callsign, strFl, strGs, strTt, - strLat, strLon, 10 * log10(signalAverage), msgs, (now - a->seen) / 1000.0); - ++row; } + a = a->next; } - a = a->next; } if (Modes.mode_ac) { diff --git a/net_io.c b/net_io.c index 2893c0e4..904ec8ba 100644 --- a/net_io.c +++ b/net_io.c @@ -1473,115 +1473,117 @@ char *generateAircraftJson(const char *url_path, int *len) { now / 1000.0, Modes.stats_current.messages_total + Modes.stats_alltime.messages_total); - for (a = Modes.aircrafts; a; a = a->next) { - if (a->messages < 2) { // basic filter for bad decodes - continue; - } - if ((now - a->seen) > 90E3) // don't include stale aircraft in the JSON - continue; + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + for (a = Modes.aircrafts[j]; a; a = a->next) { + if (a->messages < 2) { // basic filter for bad decodes + continue; + } + if ((now - a->seen) > 90E3) // don't include stale aircraft in the JSON + continue; - if (first) - first = 0; - else - *p++ = ','; + if (first) + first = 0; + else + *p++ = ','; retry: - line_start = p; - p = safe_snprintf(p, end, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); - if (a->addrtype != ADDR_ADSB_ICAO) - p = safe_snprintf(p, end, ",\"type\":\"%s\"", addrtype_enum_string(a->addrtype)); - if (trackDataValid(&a->callsign_valid)) - p = safe_snprintf(p, end, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign)); - if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND) - p = safe_snprintf(p, end, ",\"alt_baro\":\"ground\""); - else { - if (trackDataValid(&a->altitude_baro_valid)) - p = safe_snprintf(p, end, ",\"alt_baro\":%d", a->altitude_baro); - if (trackDataValid(&a->altitude_geom_valid)) - p = safe_snprintf(p, end, ",\"alt_geom\":%d", a->altitude_geom); - } - if (trackDataValid(&a->gs_valid)) - p = safe_snprintf(p, end, ",\"gs\":%.1f", a->gs); - if (trackDataValid(&a->ias_valid)) - p = safe_snprintf(p, end, ",\"ias\":%u", a->ias); - if (trackDataValid(&a->tas_valid)) - p = safe_snprintf(p, end, ",\"tas\":%u", a->tas); - if (trackDataValid(&a->mach_valid)) - p = safe_snprintf(p, end, ",\"mach\":%.3f", a->mach); - if (trackDataValid(&a->track_valid)) - p = safe_snprintf(p, end, ",\"track\":%.1f", a->track); - if (trackDataValid(&a->track_rate_valid)) - p = safe_snprintf(p, end, ",\"track_rate\":%.2f", a->track_rate); - if (trackDataValid(&a->roll_valid)) - p = safe_snprintf(p, end, ",\"roll\":%.1f", a->roll); - if (trackDataValid(&a->mag_heading_valid)) - p = safe_snprintf(p, end, ",\"mag_heading\":%.1f", a->mag_heading); - if (trackDataValid(&a->true_heading_valid)) - p = safe_snprintf(p, end, ",\"true_heading\":%.1f", a->true_heading); - if (trackDataValid(&a->baro_rate_valid)) - p = safe_snprintf(p, end, ",\"baro_rate\":%d", a->baro_rate); - if (trackDataValid(&a->geom_rate_valid)) - p = safe_snprintf(p, end, ",\"geom_rate\":%d", a->geom_rate); - if (trackDataValid(&a->squawk_valid)) - p = safe_snprintf(p, end, ",\"squawk\":\"%04x\"", a->squawk); - if (trackDataValid(&a->emergency_valid)) - p = safe_snprintf(p, end, ",\"emergency\":\"%s\"", emergency_enum_string(a->emergency)); - if (a->category != 0) - p = safe_snprintf(p, end, ",\"category\":\"%02X\"", a->category); - if (trackDataValid(&a->nav_qnh_valid)) - p = safe_snprintf(p, end, ",\"nav_qnh\":%.1f", a->nav_qnh); - if (trackDataValid(&a->nav_altitude_mcp_valid)) - p = safe_snprintf(p, end, ",\"nav_altitude_mcp\":%d", a->nav_altitude_mcp); - if (trackDataValid(&a->nav_altitude_fms_valid)) - p = safe_snprintf(p, end, ",\"nav_altitude_fms\":%d", a->nav_altitude_fms); - if (trackDataValid(&a->nav_heading_valid)) - p = safe_snprintf(p, end, ",\"nav_heading\":%.1f", a->nav_heading); - if (trackDataValid(&a->nav_modes_valid)) { - p = safe_snprintf(p, end, ",\"nav_modes\":["); - p = append_nav_modes(p, end, a->nav_modes, "\"", ","); - p = safe_snprintf(p, end, "]"); - } - if (trackDataValid(&a->position_valid)) - p = safe_snprintf(p, end, ",\"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 (a->adsb_version >= 0) - p = safe_snprintf(p, end, ",\"version\":%d", a->adsb_version); - if (trackDataValid(&a->nic_baro_valid)) - p = safe_snprintf(p, end, ",\"nic_baro\":%u", a->nic_baro); - if (trackDataValid(&a->nac_p_valid)) - p = safe_snprintf(p, end, ",\"nac_p\":%u", a->nac_p); - if (trackDataValid(&a->nac_v_valid)) - p = safe_snprintf(p, end, ",\"nac_v\":%u", a->nac_v); - if (trackDataValid(&a->sil_valid)) - p = safe_snprintf(p, end, ",\"sil\":%u", a->sil); - if (a->sil_type != SIL_INVALID) - p = safe_snprintf(p, end, ",\"sil_type\":\"%s\"", sil_type_enum_string(a->sil_type)); - if (trackDataValid(&a->gva_valid)) - 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->alert_valid)) - p = safe_snprintf(p, end, ",\"alert\":%u", a->alert); - if (trackDataValid(&a->spi_valid)) - p = safe_snprintf(p, end, ",\"spi\":%u", a->spi); - - p = safe_snprintf(p, end, ",\"mlat\":"); - p = append_flags(p, end, a, SOURCE_MLAT); - p = safe_snprintf(p, end, ",\"tisb\":"); - p = append_flags(p, end, a, SOURCE_TISB); - - p = safe_snprintf(p, end, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}", - a->messages, (now - a->seen) / 1000.0, - 10 * log10((a->signalLevel[0] + a->signalLevel[1] + a->signalLevel[2] + a->signalLevel[3] + - a->signalLevel[4] + a->signalLevel[5] + a->signalLevel[6] + a->signalLevel[7] + 1e-5) / 8)); - - if ((p + 10) >= end) { // +10 to leave some space for the final line - // overran the buffer - int used = line_start - buf; - buflen *= 2; - buf = (char *) realloc(buf, buflen); - p = buf + used; - end = buf + buflen; - goto retry; + line_start = p; + p = safe_snprintf(p, end, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); + if (a->addrtype != ADDR_ADSB_ICAO) + p = safe_snprintf(p, end, ",\"type\":\"%s\"", addrtype_enum_string(a->addrtype)); + if (trackDataValid(&a->callsign_valid)) + p = safe_snprintf(p, end, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign)); + if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND) + p = safe_snprintf(p, end, ",\"alt_baro\":\"ground\""); + else { + if (trackDataValid(&a->altitude_baro_valid)) + p = safe_snprintf(p, end, ",\"alt_baro\":%d", a->altitude_baro); + if (trackDataValid(&a->altitude_geom_valid)) + p = safe_snprintf(p, end, ",\"alt_geom\":%d", a->altitude_geom); + } + if (trackDataValid(&a->gs_valid)) + p = safe_snprintf(p, end, ",\"gs\":%.1f", a->gs); + if (trackDataValid(&a->ias_valid)) + p = safe_snprintf(p, end, ",\"ias\":%u", a->ias); + if (trackDataValid(&a->tas_valid)) + p = safe_snprintf(p, end, ",\"tas\":%u", a->tas); + if (trackDataValid(&a->mach_valid)) + p = safe_snprintf(p, end, ",\"mach\":%.3f", a->mach); + if (trackDataValid(&a->track_valid)) + p = safe_snprintf(p, end, ",\"track\":%.1f", a->track); + if (trackDataValid(&a->track_rate_valid)) + p = safe_snprintf(p, end, ",\"track_rate\":%.2f", a->track_rate); + if (trackDataValid(&a->roll_valid)) + p = safe_snprintf(p, end, ",\"roll\":%.1f", a->roll); + if (trackDataValid(&a->mag_heading_valid)) + p = safe_snprintf(p, end, ",\"mag_heading\":%.1f", a->mag_heading); + if (trackDataValid(&a->true_heading_valid)) + p = safe_snprintf(p, end, ",\"true_heading\":%.1f", a->true_heading); + if (trackDataValid(&a->baro_rate_valid)) + p = safe_snprintf(p, end, ",\"baro_rate\":%d", a->baro_rate); + if (trackDataValid(&a->geom_rate_valid)) + p = safe_snprintf(p, end, ",\"geom_rate\":%d", a->geom_rate); + if (trackDataValid(&a->squawk_valid)) + p = safe_snprintf(p, end, ",\"squawk\":\"%04x\"", a->squawk); + if (trackDataValid(&a->emergency_valid)) + p = safe_snprintf(p, end, ",\"emergency\":\"%s\"", emergency_enum_string(a->emergency)); + if (a->category != 0) + p = safe_snprintf(p, end, ",\"category\":\"%02X\"", a->category); + if (trackDataValid(&a->nav_qnh_valid)) + p = safe_snprintf(p, end, ",\"nav_qnh\":%.1f", a->nav_qnh); + if (trackDataValid(&a->nav_altitude_mcp_valid)) + p = safe_snprintf(p, end, ",\"nav_altitude_mcp\":%d", a->nav_altitude_mcp); + if (trackDataValid(&a->nav_altitude_fms_valid)) + p = safe_snprintf(p, end, ",\"nav_altitude_fms\":%d", a->nav_altitude_fms); + if (trackDataValid(&a->nav_heading_valid)) + p = safe_snprintf(p, end, ",\"nav_heading\":%.1f", a->nav_heading); + if (trackDataValid(&a->nav_modes_valid)) { + p = safe_snprintf(p, end, ",\"nav_modes\":["); + p = append_nav_modes(p, end, a->nav_modes, "\"", ","); + p = safe_snprintf(p, end, "]"); + } + if (trackDataValid(&a->position_valid)) + p = safe_snprintf(p, end, ",\"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 (a->adsb_version >= 0) + p = safe_snprintf(p, end, ",\"version\":%d", a->adsb_version); + if (trackDataValid(&a->nic_baro_valid)) + p = safe_snprintf(p, end, ",\"nic_baro\":%u", a->nic_baro); + if (trackDataValid(&a->nac_p_valid)) + p = safe_snprintf(p, end, ",\"nac_p\":%u", a->nac_p); + if (trackDataValid(&a->nac_v_valid)) + p = safe_snprintf(p, end, ",\"nac_v\":%u", a->nac_v); + if (trackDataValid(&a->sil_valid)) + p = safe_snprintf(p, end, ",\"sil\":%u", a->sil); + if (a->sil_type != SIL_INVALID) + p = safe_snprintf(p, end, ",\"sil_type\":\"%s\"", sil_type_enum_string(a->sil_type)); + if (trackDataValid(&a->gva_valid)) + 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->alert_valid)) + p = safe_snprintf(p, end, ",\"alert\":%u", a->alert); + if (trackDataValid(&a->spi_valid)) + p = safe_snprintf(p, end, ",\"spi\":%u", a->spi); + + p = safe_snprintf(p, end, ",\"mlat\":"); + p = append_flags(p, end, a, SOURCE_MLAT); + p = safe_snprintf(p, end, ",\"tisb\":"); + p = append_flags(p, end, a, SOURCE_TISB); + + p = safe_snprintf(p, end, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}", + a->messages, (now - a->seen) / 1000.0, + 10 * log10((a->signalLevel[0] + a->signalLevel[1] + a->signalLevel[2] + a->signalLevel[3] + + a->signalLevel[4] + a->signalLevel[5] + a->signalLevel[6] + a->signalLevel[7] + 1e-5) / 8)); + + if ((p + 10) >= end) { // +10 to leave some space for the final line + // overran the buffer + int used = line_start - buf; + buflen *= 2; + buf = (char *) realloc(buf, buflen); + p = buf + used; + end = buf + buflen; + goto retry; + } } } @@ -2267,36 +2269,37 @@ static void writeFATSV() { // scan once a second at most next_update = now + 1000; - for (a = Modes.aircrafts; a; a = a->next) { - if (a->messages < 2) // basic filter for bad decodes - continue; + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + for (a = Modes.aircrafts[j]; a; a = a->next) { + if (a->messages < 2) // basic filter for bad decodes + continue; - // don't emit if it hasn't updated since last time - if (a->seen < a->fatsv_last_emitted) { - continue; - } + // don't emit if it hasn't updated since last time + if (a->seen < a->fatsv_last_emitted) { + continue; + } - // Pretend we are "processing a message" so the validity checks work as expected - _messageNow = a->seen; - - // some special cases: - 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 gsValid = trackDataValid(&a->gs_valid); - int squawkValid = trackDataValid(&a->squawk_valid); - int callsignValid = trackDataValid(&a->callsign_valid) && strcmp(a->callsign, " ") != 0; - int positionValid = trackDataValid(&a->position_valid); - - // If we are definitely on the ground, suppress any unreliable altitude info. - // When on the ground, ADS-B transponders don't emit an ADS-B message that includes - // altitude, so a corrupted Mode S altitude response from some other in-the-air AC - // might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000". - if (airgroundValid && a->airground == AG_GROUND && a->altitude_baro_valid.source < SOURCE_MODE_S_CHECKED) - altValid = 0; - - // if it hasn't changed altitude, heading, or speed much, - // don't update so often - int changed = + // Pretend we are "processing a message" so the validity checks work as expected + _messageNow = a->seen; + + // some special cases: + 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 gsValid = trackDataValid(&a->gs_valid); + int squawkValid = trackDataValid(&a->squawk_valid); + int callsignValid = trackDataValid(&a->callsign_valid) && strcmp(a->callsign, " ") != 0; + int positionValid = trackDataValid(&a->position_valid); + + // If we are definitely on the ground, suppress any unreliable altitude info. + // When on the ground, ADS-B transponders don't emit an ADS-B message that includes + // altitude, so a corrupted Mode S altitude response from some other in-the-air AC + // might be taken as the "best available altitude" and produce e.g. "airGround G+ alt 31000". + if (airgroundValid && a->airground == AG_GROUND && a->altitude_baro_valid.source < SOURCE_MODE_S_CHECKED) + altValid = 0; + + // if it hasn't changed altitude, heading, or speed much, + // don't update so often + int changed = (altValid && abs(a->altitude_baro - a->fatsv_emitted_altitude_baro) >= 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) || @@ -2311,7 +2314,7 @@ static void writeFATSV() { (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); - int immediate = + int immediate = (trackDataValid(&a->nav_altitude_mcp_valid) && unsigned_difference(a->nav_altitude_mcp, a->fatsv_emitted_nav_altitude_mcp) > 50) || (trackDataValid(&a->nav_altitude_fms_valid) && unsigned_difference(a->nav_altitude_fms, a->fatsv_emitted_nav_altitude_fms) > 50) || (trackDataValid(&a->nav_altitude_src_valid) && a->nav_altitude_src != a->fatsv_emitted_nav_altitude_src) || @@ -2324,154 +2327,155 @@ static void writeFATSV() { (squawkValid && a->squawk != a->fatsv_emitted_squawk) || (trackDataValid(&a->emergency_valid) && a->emergency != a->fatsv_emitted_emergency); - uint64_t minAge; - if (immediate) { - // a change we want to emit right away - minAge = 0; - } else if (!positionValid) { - // don't send mode S very often - minAge = 30000; - } else if ((airgroundValid && a->airground == AG_GROUND) || - (altValid && a->altitude_baro < 500 && (!gsValid || a->gs < 200)) || - (gsValid && a->gs < 100 && (!altValid || a->altitude_baro < 1000))) { - // we are probably on the ground, increase the update rate - minAge = 1000; - } else if (!altValid || a->altitude_baro < 10000) { - // Below 10000 feet, emit up to every 5s when changing, 10s otherwise - minAge = (changed ? 5000 : 10000); - } else { - // Above 10000 feet, emit up to every 10s when changing, 30s otherwise - minAge = (changed ? 10000 : 30000); - } + uint64_t minAge; + if (immediate) { + // a change we want to emit right away + minAge = 0; + } else if (!positionValid) { + // don't send mode S very often + minAge = 30000; + } else if ((airgroundValid && a->airground == AG_GROUND) || + (altValid && a->altitude_baro < 500 && (!gsValid || a->gs < 200)) || + (gsValid && a->gs < 100 && (!altValid || a->altitude_baro < 1000))) { + // we are probably on the ground, increase the update rate + minAge = 1000; + } else if (!altValid || a->altitude_baro < 10000) { + // Below 10000 feet, emit up to every 5s when changing, 10s otherwise + minAge = (changed ? 5000 : 10000); + } else { + // Above 10000 feet, emit up to every 10s when changing, 30s otherwise + minAge = (changed ? 10000 : 30000); + } - if ((now - a->fatsv_last_emitted) < minAge) - continue; + if ((now - a->fatsv_last_emitted) < minAge) + continue; - char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); - if (!p) - return; - char *end = p + TSV_MAX_PACKET_SIZE; + char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); + if (!p) + return; + char *end = p + TSV_MAX_PACKET_SIZE; - p = appendFATSV(p, end, "_v", "%s", TSV_VERSION); - 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, "_v", "%s", TSV_VERSION); + p = appendFATSV(p, end, "clock", "%" PRIu64, messageNow() / 1000); + p = appendFATSV(p, end, (a->addr & MODES_NON_ICAO_ADDRESS) ? "otherid" : "hexid", "%06X", a->addr & 0xFFFFFF); - // for fields we only emit on change, - // occasionally re-emit them all - int forceEmit = (now - a->fatsv_last_force_emit) > 600000; + // 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_enum_string(a->addrtype)); - } - if (forceEmit || a->adsb_version != a->fatsv_emitted_adsb_version) { - p = appendFATSV(p, end, "adsb_version", "%d", a->adsb_version); - } - if (forceEmit || a->category != a->fatsv_emitted_category) { - p = appendFATSV(p, end, "category", "%02X", a->category); - } - if (trackDataValid(&a->nac_p_valid) && (forceEmit || a->nac_p != a->fatsv_emitted_nac_p)) { - p = appendFATSVMeta(p, end, "nac_p", a, &a->nac_p_valid, "%u", a->nac_p); - } - if (trackDataValid(&a->nac_v_valid) && (forceEmit || a->nac_v != a->fatsv_emitted_nac_v)) { - p = appendFATSVMeta(p, end, "nac_v", a, &a->nac_v_valid, "%u", a->nac_v); - } - if (trackDataValid(&a->sil_valid) && (forceEmit || a->sil != a->fatsv_emitted_sil)) { - p = appendFATSVMeta(p, end, "sil", a, &a->sil_valid, "%u", a->sil); - } - if (trackDataValid(&a->sil_valid) && (forceEmit || a->sil_type != a->fatsv_emitted_sil_type)) { - p = appendFATSVMeta(p, end, "sil_type", a, &a->sil_valid, "%s", sil_type_enum_string(a->sil_type)); - } - if (trackDataValid(&a->nic_baro_valid) && (forceEmit || a->nic_baro != a->fatsv_emitted_nic_baro)) { - p = appendFATSVMeta(p, end, "nic_baro", a, &a->nic_baro_valid, "%u", a->nic_baro); - } + // 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_enum_string(a->addrtype)); + } + if (forceEmit || a->adsb_version != a->fatsv_emitted_adsb_version) { + p = appendFATSV(p, end, "adsb_version", "%d", a->adsb_version); + } + if (forceEmit || a->category != a->fatsv_emitted_category) { + p = appendFATSV(p, end, "category", "%02X", a->category); + } + if (trackDataValid(&a->nac_p_valid) && (forceEmit || a->nac_p != a->fatsv_emitted_nac_p)) { + p = appendFATSVMeta(p, end, "nac_p", a, &a->nac_p_valid, "%u", a->nac_p); + } + if (trackDataValid(&a->nac_v_valid) && (forceEmit || a->nac_v != a->fatsv_emitted_nac_v)) { + p = appendFATSVMeta(p, end, "nac_v", a, &a->nac_v_valid, "%u", a->nac_v); + } + if (trackDataValid(&a->sil_valid) && (forceEmit || a->sil != a->fatsv_emitted_sil)) { + p = appendFATSVMeta(p, end, "sil", a, &a->sil_valid, "%u", a->sil); + } + if (trackDataValid(&a->sil_valid) && (forceEmit || a->sil_type != a->fatsv_emitted_sil_type)) { + p = appendFATSVMeta(p, end, "sil_type", a, &a->sil_valid, "%s", sil_type_enum_string(a->sil_type)); + } + if (trackDataValid(&a->nic_baro_valid) && (forceEmit || a->nic_baro != a->fatsv_emitted_nic_baro)) { + p = appendFATSVMeta(p, end, "nic_baro", a, &a->nic_baro_valid, "%u", a->nic_baro); + } - // only emit alt, speed, latlon, track etc if they have been received since the last time - // and are not stale - - char *dataStart = p; - - // special cases - if (airgroundValid) - p = appendFATSVMeta(p, end, "airGround", a, &a->airground_valid, "%s", airground_enum_string(a->airground)); - 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); - if (altValid) - 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); - } + // only emit alt, speed, latlon, track etc if they have been received since the last time + // and are not stale + + char *dataStart = p; + + // special cases + if (airgroundValid) + p = appendFATSVMeta(p, end, "airGround", a, &a->airground_valid, "%s", airground_enum_string(a->airground)); + 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); + if (altValid) + 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, "alt_gnss", 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_geom", 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, "speed_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, "mach", a, &a->mach_valid, "%.3f", a->mach); - p = appendFATSVMeta(p, end, "track", a, &a->track_valid, "%.1f", a->track); - p = appendFATSVMeta(p, end, "track_rate", 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, "heading_magnetic", a, &a->mag_heading_valid, "%.1f", a->mag_heading); - p = appendFATSVMeta(p, end, "heading_true", a, &a->true_heading_valid, "%.1f", a->true_heading); - 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)); - - // if we didn't get anything interesting, bail out. - // We don't need to do anything special to unwind prepareWrite(). - if (p == dataStart) { - continue; - } + p = appendFATSVMeta(p, end, "alt_gnss", 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_geom", 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, "speed_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, "mach", a, &a->mach_valid, "%.3f", a->mach); + p = appendFATSVMeta(p, end, "track", a, &a->track_valid, "%.1f", a->track); + p = appendFATSVMeta(p, end, "track_rate", 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, "heading_magnetic", a, &a->mag_heading_valid, "%.1f", a->mag_heading); + p = appendFATSVMeta(p, end, "heading_true", a, &a->true_heading_valid, "%.1f", a->true_heading); + 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)); + + // if we didn't get anything interesting, bail out. + // We don't need to do anything special to unwind prepareWrite(). + if (p == dataStart) { + continue; + } + + --p; // remove last tab + p = safe_snprintf(p, end, "\n"); - --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)); - - a->fatsv_emitted_altitude_baro = a->altitude_baro; - a->fatsv_emitted_altitude_geom = a->altitude_geom; - a->fatsv_emitted_baro_rate = a->baro_rate; - a->fatsv_emitted_geom_rate = a->geom_rate; - a->fatsv_emitted_gs = a->gs; - a->fatsv_emitted_ias = a->ias; - a->fatsv_emitted_tas = a->tas; - a->fatsv_emitted_mach = a->mach; - a->fatsv_emitted_track = a->track; - a->fatsv_emitted_track_rate = a->track_rate; - a->fatsv_emitted_roll = a->roll; - a->fatsv_emitted_mag_heading = a->mag_heading; - a->fatsv_emitted_true_heading = a->true_heading; - a->fatsv_emitted_airground = a->airground; - a->fatsv_emitted_nav_altitude_mcp = a->nav_altitude_mcp; - a->fatsv_emitted_nav_altitude_fms = a->nav_altitude_fms; - a->fatsv_emitted_nav_altitude_src = a->nav_altitude_src; - a->fatsv_emitted_nav_heading = a->nav_heading; - a->fatsv_emitted_nav_modes = a->nav_modes; - a->fatsv_emitted_nav_qnh = a->nav_qnh; - 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_emitted_nac_p = a->nac_p; - a->fatsv_emitted_nac_v = a->nac_v; - a->fatsv_emitted_sil = a->sil; - a->fatsv_emitted_sil_type = a->sil_type; - a->fatsv_emitted_nic_baro = a->nic_baro; - a->fatsv_emitted_emergency = a->emergency; - a->fatsv_last_emitted = now; - if (forceEmit) { - a->fatsv_last_force_emit = now; + 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)); + + a->fatsv_emitted_altitude_baro = a->altitude_baro; + a->fatsv_emitted_altitude_geom = a->altitude_geom; + a->fatsv_emitted_baro_rate = a->baro_rate; + a->fatsv_emitted_geom_rate = a->geom_rate; + a->fatsv_emitted_gs = a->gs; + a->fatsv_emitted_ias = a->ias; + a->fatsv_emitted_tas = a->tas; + a->fatsv_emitted_mach = a->mach; + a->fatsv_emitted_track = a->track; + a->fatsv_emitted_track_rate = a->track_rate; + a->fatsv_emitted_roll = a->roll; + a->fatsv_emitted_mag_heading = a->mag_heading; + a->fatsv_emitted_true_heading = a->true_heading; + a->fatsv_emitted_airground = a->airground; + a->fatsv_emitted_nav_altitude_mcp = a->nav_altitude_mcp; + a->fatsv_emitted_nav_altitude_fms = a->nav_altitude_fms; + a->fatsv_emitted_nav_altitude_src = a->nav_altitude_src; + a->fatsv_emitted_nav_heading = a->nav_heading; + a->fatsv_emitted_nav_modes = a->nav_modes; + a->fatsv_emitted_nav_qnh = a->nav_qnh; + 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_emitted_nac_p = a->nac_p; + a->fatsv_emitted_nac_v = a->nac_v; + a->fatsv_emitted_sil = a->sil; + a->fatsv_emitted_sil_type = a->sil_type; + a->fatsv_emitted_nic_baro = a->nic_baro; + a->fatsv_emitted_emergency = a->emergency; + a->fatsv_last_emitted = now; + if (forceEmit) { + a->fatsv_last_force_emit = now; + } } } } diff --git a/readsb.c b/readsb.c index 4c18e3c7..a90e6b5a 100644 --- a/readsb.c +++ b/readsb.c @@ -431,11 +431,13 @@ static void cleanup_and_exit(int code) { free(Modes.net_push_server_port); free(Modes.beast_serial); /* Go through tracked aircraft chain and free up any used memory */ - struct aircraft *a = Modes.aircrafts, *na; - while (a) { - na = a->next; - if (a) free(a); - a = na; + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + struct aircraft *a = Modes.aircrafts[j], *na; + while (a) { + na = a->next; + if (a) free(a); + a = na; + } } int i; diff --git a/readsb.h b/readsb.h index 95cabdc0..4b3f0ccc 100644 --- a/readsb.h +++ b/readsb.h @@ -268,6 +268,8 @@ typedef enum { #define MODES_NOTUSED(V) ((void) V) +#define AIRCRAFTS_BUCKETS 2048 + // Include subheaders after all the #defines are in place #include "util.h" @@ -331,7 +333,7 @@ struct int beast_fd; // Local Modes-S Beast handler struct net_service *services; // Active services struct client *clients; // Our clients - struct aircraft *aircrafts; + struct aircraft *aircrafts[AIRCRAFTS_BUCKETS]; struct net_writer raw_out; // Raw output struct net_writer beast_out; // Beast-format output struct net_writer sbs_out; // SBS-format output diff --git a/track.c b/track.c index c802ce42..21d45a0e 100644 --- a/track.c +++ b/track.c @@ -152,7 +152,7 @@ static struct aircraft *trackCreateAircraft(struct modesMessage *mm) { // static struct aircraft *trackFindAircraft(uint32_t addr) { - struct aircraft *a = Modes.aircrafts; + struct aircraft *a = Modes.aircrafts[addr % AIRCRAFTS_BUCKETS]; while (a) { if (a->addr == addr) return (a); @@ -882,8 +882,8 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) { a = trackFindAircraft(mm->addr); if (!a) { // If it's a currently unknown aircraft.... a = trackCreateAircraft(mm); // ., create a new record for it, - a->next = Modes.aircrafts; // .. and put it at the head of the list - Modes.aircrafts = a; + a->next = Modes.aircrafts[mm->addr % AIRCRAFTS_BUCKETS]; // .. and put it at the head of the list + Modes.aircrafts[mm->addr % AIRCRAFTS_BUCKETS] = a; } if (mm->signalLevel > 0) { @@ -1162,43 +1162,45 @@ static void trackMatchAC(uint64_t now) { } // scan aircraft list, look for matches - for (struct aircraft *a = Modes.aircrafts; a; a = a->next) { - if ((now - a->seen) > 5000) { - continue; - } + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + for (struct aircraft *a = Modes.aircrafts[j]; a; a = a->next) { + if ((now - a->seen) > 5000) { + continue; + } - // match on Mode A - if (trackDataValid(&a->squawk_valid)) { - unsigned i = modeAToIndex(a->squawk); - if ((modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { - a->modeA_hit = 1; - modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + // match on Mode A + if (trackDataValid(&a->squawk_valid)) { + unsigned i = modeAToIndex(a->squawk); + if ((modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { + a->modeA_hit = 1; + modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + } } - } - // match on Mode C (+/- 100ft) - if (trackDataValid(&a->altitude_baro_valid)) { - int modeC = (a->altitude_baro + 49) / 100; + // match on Mode C (+/- 100ft) + if (trackDataValid(&a->altitude_baro_valid)) { + int modeC = (a->altitude_baro + 49) / 100; - unsigned modeA = modeCToModeA(modeC); - unsigned i = modeAToIndex(modeA); - if (modeA && (modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { - a->modeC_hit = 1; - modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); - } + unsigned modeA = modeCToModeA(modeC); + unsigned i = modeAToIndex(modeA); + if (modeA && (modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { + a->modeC_hit = 1; + modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + } - modeA = modeCToModeA(modeC + 1); - i = modeAToIndex(modeA); - if (modeA && (modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { - a->modeC_hit = 1; - modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); - } + modeA = modeCToModeA(modeC + 1); + i = modeAToIndex(modeA); + if (modeA && (modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { + a->modeC_hit = 1; + modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + } - modeA = modeCToModeA(modeC - 1); - i = modeAToIndex(modeA); - if (modeA && (modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { - a->modeC_hit = 1; - modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + modeA = modeCToModeA(modeC - 1); + i = modeAToIndex(modeA); + if (modeA && (modeAC_count[i] - modeAC_lastcount[i]) >= TRACK_MODEAC_MIN_MESSAGES) { + a->modeC_hit = 1; + modeAC_match[i] = (modeAC_match[i] ? 0xFFFFFFFF : a->addr); + } } } } @@ -1237,74 +1239,76 @@ static void trackMatchAC(uint64_t now) { // static void trackRemoveStaleAircraft(uint64_t now) { - struct aircraft *a = Modes.aircrafts; - struct aircraft *prev = NULL; - - while (a) { - if ((now - a->seen) > TRACK_AIRCRAFT_TTL || - (a->messages == 1 && (now - a->seen) > TRACK_AIRCRAFT_ONEHIT_TTL)) { - // Count aircraft where we saw only one message before reaping them. - // These are likely to be due to messages with bad addresses. - if (a->messages == 1) - Modes.stats_current.single_message_aircraft++; - - // Remove the element from the linked list, with care - // if we are removing the first element - if (!prev) { - Modes.aircrafts = a->next; - free(a); - a = Modes.aircrafts; + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + struct aircraft *a = Modes.aircrafts[j]; + struct aircraft *prev = NULL; + + while (a) { + if ((now - a->seen) > TRACK_AIRCRAFT_TTL || + (a->messages == 1 && (now - a->seen) > TRACK_AIRCRAFT_ONEHIT_TTL)) { + // Count aircraft where we saw only one message before reaping them. + // These are likely to be due to messages with bad addresses. + if (a->messages == 1) + Modes.stats_current.single_message_aircraft++; + + // Remove the element from the linked list, with care + // if we are removing the first element + if (!prev) { + Modes.aircrafts[j] = a->next; + free(a); + a = Modes.aircrafts[j]; + } else { + prev->next = a->next; + free(a); + a = prev->next; + } } else { - prev->next = a->next; - free(a); - a = prev->next; - } - } else { #define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0) - EXPIRE(callsign); - EXPIRE(altitude_baro); - EXPIRE(altitude_geom); - EXPIRE(geom_delta); - EXPIRE(gs); - EXPIRE(ias); - EXPIRE(tas); - EXPIRE(mach); - EXPIRE(track); - EXPIRE(track_rate); - EXPIRE(roll); - EXPIRE(mag_heading); - EXPIRE(true_heading); - EXPIRE(baro_rate); - EXPIRE(geom_rate); - EXPIRE(squawk); - EXPIRE(airground); - EXPIRE(nav_qnh); - EXPIRE(nav_altitude_mcp); - EXPIRE(nav_altitude_fms); - EXPIRE(nav_altitude_src); - EXPIRE(nav_heading); - EXPIRE(nav_modes); - EXPIRE(cpr_odd); - EXPIRE(cpr_even); - EXPIRE(position); - EXPIRE(nic_a); - EXPIRE(nic_c); - EXPIRE(nic_baro); - EXPIRE(nac_p); - EXPIRE(sil); - EXPIRE(gva); - EXPIRE(sda); + EXPIRE(callsign); + EXPIRE(altitude_baro); + EXPIRE(altitude_geom); + EXPIRE(geom_delta); + EXPIRE(gs); + EXPIRE(ias); + EXPIRE(tas); + EXPIRE(mach); + EXPIRE(track); + EXPIRE(track_rate); + EXPIRE(roll); + EXPIRE(mag_heading); + EXPIRE(true_heading); + EXPIRE(baro_rate); + EXPIRE(geom_rate); + EXPIRE(squawk); + EXPIRE(airground); + EXPIRE(nav_qnh); + EXPIRE(nav_altitude_mcp); + EXPIRE(nav_altitude_fms); + EXPIRE(nav_altitude_src); + EXPIRE(nav_heading); + EXPIRE(nav_modes); + EXPIRE(cpr_odd); + EXPIRE(cpr_even); + EXPIRE(position); + EXPIRE(nic_a); + EXPIRE(nic_c); + EXPIRE(nic_baro); + EXPIRE(nac_p); + EXPIRE(sil); + EXPIRE(gva); + EXPIRE(sda); #undef EXPIRE - // reset position reliability when the position has expired - if (a->position_valid.source == SOURCE_INVALID) { - a->pos_reliable_odd = 0; - a->pos_reliable_even = 0; - } + // reset position reliability when the position has expired + if (a->position_valid.source == SOURCE_INVALID) { + a->pos_reliable_odd = 0; + a->pos_reliable_even = 0; + } - prev = a; - a = a->next; + prev = a; + a = a->next; + } } } } diff --git a/viewadsb.c b/viewadsb.c index 7e809f88..60879527 100644 --- a/viewadsb.c +++ b/viewadsb.c @@ -269,11 +269,13 @@ int main(int argc, char **argv) { } /* Go through tracked aircraft chain and free up any used memory */ - struct aircraft *a = Modes.aircrafts, *n; - while (a) { - n = a->next; - if (a) free(a); - a = n; + for (int j = 0; j < AIRCRAFTS_BUCKETS; j++) { + struct aircraft *a = Modes.aircrafts[j], *n; + while (a) { + n = a->next; + if (a) free(a); + a = n; + } } // Free local service and client if (s) free(s); From 77e8a8e882e326b71383b7657f5cf451bc9d800e Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 00:40:54 +0100 Subject: [PATCH 3/8] increase network input read size to 8 kB --- readsb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readsb.h b/readsb.h index 4b3f0ccc..03b49e5f 100644 --- a/readsb.h +++ b/readsb.h @@ -259,8 +259,8 @@ typedef enum { #define MODES_NET_HEARTBEAT_INTERVAL 60000 // milliseconds -#define MODES_CLIENT_BUF_SIZE 1024 -#define MODES_NET_SNDBUF_SIZE (1024*64) +#define MODES_CLIENT_BUF_SIZE (8*1024) +#define MODES_NET_SNDBUF_SIZE (64*1024) #define MODES_NET_SNDBUF_MAX (7) #define HISTORY_SIZE 120 From 8d2fcd12504d70c40adab603f621fa3862cf81ed Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 00:40:55 +0100 Subject: [PATCH 4/8] Filtering for implausible altitudes --- track.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- track.h | 3 +++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/track.c b/track.c index 21d45a0e..5de443a3 100644 --- a/track.c +++ b/track.c @@ -918,7 +918,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) { } } - if (mm->altitude_baro_valid && accept_data(&a->altitude_baro_valid, mm->source)) { + if (mm->altitude_baro_valid) { int alt = altitude_to_feet(mm->altitude_baro, mm->altitude_baro_unit); if (a->modeC_hit) { int new_modeC = (a->altitude_baro + 49) / 100; @@ -928,7 +928,53 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) { } } - a->altitude_baro = alt; + int delta = alt - a->altitude_baro; + int fpm = 0; + + int max_fpm = 12500; + int min_fpm = -12500; + + if (abs(delta) >= 300) { + fpm = delta*60*10/(abs((int)trackDataAge(&a->altitude_baro_valid)/100)+10); + if (trackDataValid(&a->geom_rate_valid) && trackDataAge(&a->geom_rate_valid) < trackDataAge(&a->baro_rate_valid)) { + min_fpm = a->geom_rate - 1500 - min(11000, ((int)trackDataAge(&a->geom_rate_valid)/2)); + max_fpm = a->geom_rate + 1500 + min(11000, ((int)trackDataAge(&a->geom_rate_valid)/2)); + } else if (trackDataValid(&a->baro_rate_valid)) { + min_fpm = a->baro_rate - 1500 - min(11000, ((int)trackDataAge(&a->baro_rate_valid)/2)); + max_fpm = a->baro_rate + 1500 + min(11000, ((int)trackDataAge(&a->baro_rate_valid)/2)); + } + if (trackDataValid(&a->altitude_baro_valid) && trackDataAge(&a->altitude_baro_valid) < 30000) { + a->altitude_baro_reliable = min( + ALTITUDE_BARO_RELIABLE_MAX - (ALTITUDE_BARO_RELIABLE_MAX*trackDataAge(&a->altitude_baro_valid)/30000), + a->altitude_baro_reliable); + } else { + a->altitude_baro_reliable = 0; + } + } + int good_crc = (mm->crc == 0 && mm->source != SOURCE_MLAT) ? (ALTITUDE_BARO_RELIABLE_MAX/2 - 1) : 0; + + if (a->altitude_baro_reliable <= 0 || abs(delta) < 300 + || (fpm < max_fpm && fpm > min_fpm) + || (good_crc && a->altitude_baro_reliable <= (ALTITUDE_BARO_RELIABLE_MAX/2 + 2)) + ) { + if (accept_data(&a->altitude_baro_valid, mm->source)) { + a->altitude_baro_reliable = min(ALTITUDE_BARO_RELIABLE_MAX , a->altitude_baro_reliable + (good_crc+1)); + /*if (abs(delta) > 2000 && delta != alt) { + fprintf(stderr, "Alt change B: %06x: %d %d -> %d, min %.1f kfpm, max %.1f kfpm, actual %.1f kfpm\n", + a->addr, a->altitude_baro_reliable, a->altitude_baro, alt, min_fpm/1000.0, max_fpm/1000.0, fpm/1000.0); + }*/ + a->altitude_baro = alt; + } + } else { + a->altitude_baro_reliable = a->altitude_baro_reliable - (good_crc+1); + //fprintf(stderr, "Alt check F: %06x: %d %d -> %d, min %.1f kfpm, max %.1f kfpm, actual %.1f kfpm\n", + // a->addr, a->altitude_baro_reliable, a->altitude_baro, alt, min_fpm/1000.0, max_fpm/1000.0, fpm/1000.0); + if (a->altitude_baro_reliable <= 0) { + //fprintf(stderr, "Altitude INVALIDATED: %06x\n", a->addr); + a->altitude_baro_reliable = 0; + a->altitude_baro_valid.source = SOURCE_INVALID; + } + } } if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source)) { @@ -1134,7 +1180,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) { // Now handle derived data // derive geometric altitude if we have baro + delta - if (compare_validity(&a->altitude_baro_valid, &a->altitude_geom_valid) > 0 && + if (a->altitude_baro_reliable >= 3 && compare_validity(&a->altitude_baro_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 a->altitude_geom = a->altitude_baro + a->geom_delta; @@ -1306,6 +1352,9 @@ static void trackRemoveStaleAircraft(uint64_t now) { a->pos_reliable_even = 0; } + if (a->altitude_baro_valid.source == SOURCE_INVALID) + a->altitude_baro_reliable = 0; + prev = a; a = a->next; } diff --git a/track.h b/track.h index 3fd5e942..004300e5 100644 --- a/track.h +++ b/track.h @@ -68,6 +68,8 @@ /* Special value for Rc unknown */ #define RC_UNKNOWN 0 +#define ALTITUDE_BARO_RELIABLE_MAX 20 + // data moves through three states: // 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. @@ -96,6 +98,7 @@ struct aircraft long messages; // Number of Mode S messages received int signalNext; // next index of signalLevel to use int altitude_baro; // Altitude (Baro) + int altitude_baro_reliable; int altitude_geom; // Altitude (Geometric) int geom_delta; // Difference between Geometric and Baro altitudes int baro_rate; // Vertical rate (barometric) From 48769e984f4515a6fa5bda862a50d51bfb878719 Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 00:40:57 +0100 Subject: [PATCH 5/8] don't emit unreliable altitudes in JSON --- net_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index 904ec8ba..5d3d264b 100644 --- a/net_io.c +++ b/net_io.c @@ -1496,7 +1496,7 @@ char *generateAircraftJson(const char *url_path, int *len) { if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND) p = safe_snprintf(p, end, ",\"alt_baro\":\"ground\""); else { - if (trackDataValid(&a->altitude_baro_valid)) + if (trackDataValid(&a->altitude_baro_valid) && a->altitude_baro_reliable >= 3) p = safe_snprintf(p, end, ",\"alt_baro\":%d", a->altitude_baro); if (trackDataValid(&a->altitude_geom_valid)) p = safe_snprintf(p, end, ",\"alt_geom\":%d", a->altitude_geom); From c30e670d1e99024e1d117a847def7bc7c5257f4b Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 00:41:00 +0100 Subject: [PATCH 6/8] clean up position_valid expiration time The long expiration time was only required for aircraft relative position decoding. Instead of changing expirationg time, it's better to just check when the position was last updated. Also increment both pos_reliable on first global CPR. --- track.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/track.c b/track.c index 5de443a3..d518bc90 100644 --- a/track.c +++ b/track.c @@ -128,7 +128,7 @@ static struct aircraft *trackCreateAircraft(struct modesMessage *mm) { F(nav_modes, 60, 70); // ADS-B or Comm-B F(cpr_odd, 60, 70); // ADS-B only F(cpr_even, 60, 70); // ADS-B only - F(position, 60, 10*60); // 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 @@ -420,7 +420,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, *rc = a->cpr_even_rc; } - if (trackDataValid(&a->position_valid)) { + if (messageNow() - a->position_valid.updated < (10*60*1000)) { reflat = a->lat; reflon = a->lon; @@ -574,10 +574,14 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) { if (accept_data(&a->position_valid, mm->source)) { Modes.stats_current.cpr_global_ok++; - if (mm->cpr_odd) + if (a->pos_reliable_odd <= 0 || a->pos_reliable_even <=0) { + a->pos_reliable_odd = 1; + a->pos_reliable_even = 1; + } else if (mm->cpr_odd) { a->pos_reliable_odd = min(a->pos_reliable_odd + 1, Modes.filter_persistence); - else + } else { a->pos_reliable_even = min(a->pos_reliable_even + 1, Modes.filter_persistence); + } if (trackDataValid(&a->gs_valid)) a->gs_last_pos = a->gs; From abfa278d24ea7e636f684a458b49fdfcbb10a957 Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 01:10:18 +0100 Subject: [PATCH 7/8] Remove redundant history code Probably was once there for internal webserver. --- net_io.c | 25 +------------------------ readsb.c | 24 ++++++++---------------- readsb.h | 7 +------ 3 files changed, 10 insertions(+), 46 deletions(-) diff --git a/net_io.c b/net_io.c index 5d3d264b..7210ab37 100644 --- a/net_io.c +++ b/net_io.c @@ -1744,21 +1744,14 @@ char *generateStatsJson(const char *url_path, int *len) { // char *generateReceiverJson(const char *url_path, int *len) { char *buf = (char *) malloc(1024), *p = buf; - int history_size; MODES_NOTUSED(url_path); - // work out number of valid history entries - if (Modes.json_aircraft_history[HISTORY_SIZE - 1].content == NULL) - history_size = Modes.json_aircraft_history_next; - else - history_size = HISTORY_SIZE; - p += snprintf(p, 1024, "{ " \ "\"version\" : \"%s\", " "\"refresh\" : %.0f, " "\"history\" : %d", - MODES_READSB_VERSION, 1.0 * Modes.json_interval, history_size); + MODES_READSB_VERSION, 1.0 * Modes.json_interval, Modes.json_aircraft_history_next + 1 ); if (Modes.json_location_accuracy && (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0)) { if (Modes.json_location_accuracy == 1) { @@ -1780,22 +1773,6 @@ char *generateReceiverJson(const char *url_path, int *len) { return buf; } -char *generateHistoryJson(const char *url_path, int *len) { - int history_index = -1; - - if (sscanf(url_path, "/data/history_%d.json", &history_index) != 1) - return NULL; - - if (history_index < 0 || history_index >= HISTORY_SIZE) - return NULL; - - if (!Modes.json_aircraft_history[history_index].content) - return NULL; - - *len = Modes.json_aircraft_history[history_index].clen; - return strdup(Modes.json_aircraft_history[history_index].content); -} - // Write JSON to file void writeJsonToFile(const char *file, char * (*generator) (const char *, int*)) { #ifndef _WIN32 diff --git a/readsb.c b/readsb.c index a90e6b5a..caa25205 100644 --- a/readsb.c +++ b/readsb.c @@ -388,24 +388,19 @@ static void backgroundTasks(void) { next_json = now + Modes.json_interval; } - if (now >= next_history) { - int rewrite_receiver_json = (Modes.json_dir && Modes.json_aircraft_history[HISTORY_SIZE - 1].content == NULL); + if (Modes.json_dir && now >= next_history) { - free(Modes.json_aircraft_history[Modes.json_aircraft_history_next].content); // might be NULL, that's OK. - Modes.json_aircraft_history[Modes.json_aircraft_history_next].content = - generateAircraftJson("/data/aircraft.json", (int *) &Modes.json_aircraft_history[Modes.json_aircraft_history_next].clen); + char filebuf[PATH_MAX]; + snprintf(filebuf, PATH_MAX, "history_%d.json", Modes.json_aircraft_history_next); + writeJsonToFile(filebuf, generateAircraftJson); - if (Modes.json_dir) { - char filebuf[PATH_MAX]; - snprintf(filebuf, PATH_MAX, "history_%d.json", Modes.json_aircraft_history_next); - writeJsonToFile(filebuf, generateHistoryJson); + if (!Modes.json_aircraft_history_full) { + writeJsonToFile("receiver.json", generateReceiverJson); // number of history entries changed + if (Modes.json_aircraft_history_next == HISTORY_SIZE - 1) + Modes.json_aircraft_history_full = 1; } Modes.json_aircraft_history_next = (Modes.json_aircraft_history_next + 1) % HISTORY_SIZE; - - if (rewrite_receiver_json) - writeJsonToFile("receiver.json", generateReceiverJson); // number of history entries changed - next_history = now + HISTORY_INTERVAL; } } @@ -444,9 +439,6 @@ static void cleanup_and_exit(int code) { for (i = 0; i < MODES_MAG_BUFFERS; ++i) { free(Modes.mag_buffers[i].data); } - for (i = 0; i < HISTORY_SIZE; ++i) { - free(Modes.json_aircraft_history[i].content); - } crcCleanupTables(); /* Cleanup network setup */ diff --git a/readsb.h b/readsb.h index 03b49e5f..032a6610 100644 --- a/readsb.h +++ b/readsb.h @@ -391,6 +391,7 @@ struct int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact int json_aircraft_history_next; + int json_aircraft_history_full; int stats_latest_1min; int bUserFlags; // Flags relating to the user details int biastee; @@ -402,12 +403,6 @@ struct struct stats stats_15min; struct timespec reader_cpu_accumulator; // CPU time used by the reader thread, copied out and reset by the main thread under the mutex struct mag_buf mag_buffers[MODES_MAG_BUFFERS]; // Converted magnitude buffers from RTL or file input - - struct - { - long clen; - char *content; - } json_aircraft_history[HISTORY_SIZE]; } Modes; // The struct we use to store information about a decoded message. From 7d0567d2cc36e9ef2739a438906c36920f054061 Mon Sep 17 00:00:00 2001 From: Matthias Wirth Date: Sat, 7 Dec 2019 16:54:54 +0100 Subject: [PATCH 8/8] make valgrind a bit happier --- readsb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readsb.c b/readsb.c index caa25205..65f06200 100644 --- a/readsb.c +++ b/readsb.c @@ -457,6 +457,8 @@ static void cleanup_and_exit(int code) { while (s) { ns = s->next; free(s->listener_fds); + if (s->writer && s->writer->data) + free(s->writer->data); if (s) free(s); s = ns; }