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 1f47666d..7210ab37 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) && 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); + } + 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; + } } } @@ -1677,7 +1679,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 +1702,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; @@ -1738,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) { @@ -1774,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 @@ -2263,36 +2246,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) || @@ -2307,7 +2291,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) || @@ -2320,154 +2304,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"); - - 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; + --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; + } } } } diff --git a/readsb.c b/readsb.c index 4c18e3c7..65f06200 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; } } @@ -431,20 +426,19 @@ 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; 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 */ @@ -463,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; } diff --git a/readsb.h b/readsb.h index 95cabdc0..032a6610 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 @@ -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 @@ -389,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; @@ -400,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. 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..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 @@ -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); @@ -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) @@ -409,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; @@ -563,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; @@ -614,7 +629,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); + } } } @@ -869,8 +886,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) { @@ -905,7 +922,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; @@ -915,7 +932,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)) { @@ -1121,7 +1184,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; @@ -1149,43 +1212,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); + } } } } @@ -1224,74 +1289,79 @@ 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; + 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) 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);