// Part of readsb, a Mode-S/ADSB/TIS message decoder. // // interactive.c: aircraft tracking and interactive display // // Copyright (c) 2019 Michael Wolf // // This code is based on a detached fork of dump1090-fa. // // Copyright (c) 2014,2015 Oliver Jowett // // This file is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // This file is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // This file incorporates work covered by the following copyright and // license: // // Copyright (C) 2012 by Salvatore Sanfilippo // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "readsb.h" #ifndef DISABLE_INTERACTIVE #include #endif #ifdef DISABLE_INTERACTIVE void interactiveInit() {} void interactiveCleanup(void) {} void interactiveShowData(void) {} #else // //========================= Interactive mode =============================== static int convert_altitude(int ft) { if (Modes.metric) return (ft / 3.2828); else return ft; } static int convert_speed(int kts) { if (Modes.metric) return (kts * 1.852); else return kts; } // //========================================================================= // // Show the currently captured interactive data on screen. // void interactiveInit() { if (!Modes.interactive) return; initscr(); clear(); refresh(); } void interactiveCleanup(void) { if (Modes.interactive) { endwin(); } } static int compareAlt(const void *p1, const void *p2) { struct aircraft *a1 = *(struct aircraft**) p1; struct aircraft *a2 = *(struct aircraft**) p2; if (a1 == NULL) return 1; if (a2 == NULL) return -1; int valid1 = trackDataValid(&a1->baro_alt_valid); int valid2 = trackDataValid(&a2->baro_alt_valid); if (valid1 && valid2) { return (a1->baro_alt > a2->baro_alt) - (a1->baro_alt < a2->baro_alt); } else { return -1 * ((valid1 > valid2) - (valid1 < valid2)); } } void interactiveShowData(void) { static int64_t next_update; static int64_t next_clear; int64_t now = mstime(); char progress; char spinner[4] = "|/-\\"; // Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde if (now < next_update) return; next_update = now + MODES_INTERACTIVE_REFRESH_TIME; // clear potential errors every 2 seconds if (now > next_clear) { next_clear = now + 2 * SECONDS; clear(); // print header mvprintw(0, 0, " Hex Mode Sqwk Flight Alt Spd Hdg Lat Long RSSI Msgs Ti"); mvhline(1, 0, ACS_HLINE, 80); } progress = spinner[(now / 1000) % 4]; mvaddch(0, 79, progress); int rows = getmaxy(stdscr); int row = 2; struct craftArray *ca = &Modes.aircraftActive; // sort active list by altitude pthread_mutex_lock(&ca->change_mutex); pthread_mutex_lock(&ca->write_mutex); qsort(ca->list, ca->len, sizeof(struct aircraft *), compareAlt); pthread_mutex_unlock(&ca->write_mutex); pthread_mutex_unlock(&ca->change_mutex); ca_lock_read(ca); for (int i = 0; i < ca->len; i++) { struct aircraft *a = ca->list[i]; if (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->geom_alt_valid)) { snprintf(strFl, 7, "%5dH", convert_altitude(a->geom_alt)); } else if (trackDataValid(&a->baro_alt_valid)) { snprintf(strFl, 7, "%5d ", convert_altitude(a->baro_alt)); } 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; } } ca_unlock_read(ca); if (Modes.mode_ac) { for (unsigned i = 1; i < 4096 && row < rows; ++i) { if (modeAC_match[i] || modeAC_count[i] < 50 || modeAC_age[i] > 5) continue; char strMode[5] = " A "; char strFl[7] = " "; unsigned modeA = indexToModeA(i); int modeC = modeAToModeC(modeA); if (modeC != INVALID_ALTITUDE) { strMode[3] = 'C'; snprintf(strFl, 7, "%5d ", convert_altitude(modeC * 100)); } mvprintw(row, 0, "%7s %-4s %04x %-8s %6s %3s %3s %7s %8s %5s %5d %2d\n", "", /* address */ strMode, /* mode */ modeA, /* squawk */ "", /* callsign */ strFl, /* altitude */ "", /* gs */ "", /* heading */ "", /* lat */ "", /* lon */ "", /* signal */ modeAC_count[i], /* messages */ modeAC_age[i]); /* age */ ++row; } } move(row, 0); clrtobot(); refresh(); } #endif // //========================================================================= //