Interactive mode updates

These updates were designed to assist those using interactive
mode to tune antennas and SDR gain.

* Add options to display distance and bearing in interactive mode

  Distance and bearing instead of latitude and longitude can now be
  displayed in interactive mode using the following options to
  dump1090-fa and view1090-fa.

  --interactive-show-distance   Show aircraft distance and bearing
                                instead of aircraft lat/lon
                                (requires --lat and --lon)
  --interactive-distance-units  Distance units ('km', 'sm', 'nm')
                                (default: 'nm')"

  You have to specify a reference --lat and --lon for this to
  work of course.

* A new line now shows at the top of the interactive display that
  has for the current sample:

  Total valid aircraft count
  Vidible aircraft count
     Will be less than total if the screen hasn't enough lines to show
     them all.
  Max RSSI
  Min RSSI
  Mean RSSI
  Max Distance

 Tot:  47 Vis:  47 RSSI: Max 25.4+ Mean -29.5 Min -36.9-  MaxD:  197.3nm+

* Add max distance and min/max RSSI indicators

  A '+' after the distance in a row indicates it's the row with the
  maximum distance.

  A '+' after the RSSI in a row indicates it's the row with the highest
  RSSI.

  A '-' after the RSSI in a row indicates it's the row with the lowest
  RSSI.

  The summary line at the top of the screen always shows the values for
  ALL aircraft, even those not visible.  The row indicators only mark
  visible rows though.

  In this example, the first aircraft is both the farthest away and has
  the weakest RSSI.  The second aircraft has the strongest RSSI.

 Tot:  47 Vis:  47 RSSI: Max 25.4+ Mean -29.5 Min -36.9-  MaxD:  197.3nm+     -
 Hex    Mode  Sqwk  Flight   Alt    Spd  Hdg  Dist(nm) Bearing  RSSI  Msgs  Ti
────────────────────────────────────────────────────────────────────────────────
 A8D5A4 S2                   34000  438  252   197.3+      85  -36.1-   26  2
 A39A13 S2ac  5740  FFT525   30750  439  256    98.8       68  -25.4+  123  0
 A70B23 S2ac  2744  LXJ553   43000  419  258   136.1       39  -33.6   174  0

* Finally, a new option '--interactive-callsign-filter' has been added
  to allow filtering interactive by callsign.  The value can be a
  simple string, in which case aircraft with that string anywhere in its
  callsign will be displayed, or a regular expression should you want a
  more precise match.

  Examples:
    --interactive-callsign-filter UAL
      will match all aircraft with UAL anywhere in its callsign.
    --interactive-callsign-filter "^UAL"
      will match only those callsigns that start with UAL.
    --interactive-callsign-filter "^(UAL|AAL)"
      will match only those callsigns that start with UAL or AAL.
    --interactive-callsign-filter "^N[0-9]" or "^N[[:digit:]]"
      will match only those callsigns that start with "N" and a number

  If you need more info on regular expressions, see this link:
  https://en.wikipedia.org/wiki/Regular_expression
This commit is contained in:
George Joseph 2020-09-21 16:00:44 -06:00
parent 3167459bfc
commit 68e95c06cc
6 changed files with 218 additions and 26 deletions

View File

@ -284,6 +284,10 @@ static void showHelp(void)
"--freq <hz> Set frequency (default: 1090 Mhz)\n"
"--interactive Interactive mode refreshing data on screen. Implies --throttle\n"
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
"--interactive-show-distance Show aircraft distance and bearing instead of lat/lon\n"
" (requires --lat and --lon)\n"
"--interactive-distance-units Distance units ('km', 'sm', 'nm') (default: 'nm')\n"
"--interactive-callsign-filter Only callsigns that match the prefix or regex will be displayed\n"
"--raw Show only messages hex values\n"
"--net Enable networking with default ports unless overridden\n"
"--modeac Enable decoding of SSR Modes 3/A & 3/C\n"
@ -571,7 +575,20 @@ int main(int argc, char **argv) {
Modes.interactive = 1;
} else if (!strcmp(argv[j],"--interactive-ttl") && more) {
Modes.interactive_display_ttl = (uint64_t)(1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--lat") && more) {
} else if (!strcmp(argv[j],"--interactive-show-distance")) {
Modes.interactive_show_distance = 1;
} else if (!strcmp(argv[j], "--interactive-distance-units") && more) {
char *units = argv[++j];
if (!strcmp(units, "km")) {
Modes.interactive_distance_units = UNIT_KILOMETERS;
} else if (!strcmp(units, "sm")) {
Modes.interactive_distance_units = UNIT_STATUTE_MILES;
} else {
Modes.interactive_distance_units = UNIT_NAUTICAL_MILES;
}
} else if (!strcmp(argv[j], "--interactive-callsign-filter") && more) {
Modes.interactive_callsign_filter = strdup(argv[++j]);
} else if (!strcmp(argv[j], "--lat") && more) {
Modes.fUserLat = atof(argv[++j]);
} else if (!strcmp(argv[j],"--lon") && more) {
Modes.fUserLon = atof(argv[++j]);

View File

@ -163,6 +163,12 @@ typedef enum {
UNIT_METERS
} altitude_unit_t;
typedef enum {
UNIT_NAUTICAL_MILES,
UNIT_STATUTE_MILES,
UNIT_KILOMETERS,
} interactive_distance_unit_t;
typedef enum {
ALTITUDE_BARO,
ALTITUDE_GEOM
@ -336,6 +342,9 @@ struct _Modes { // Internal state
uint32_t show_only; // Only show messages from this ICAO
int interactive; // Interactive mode
uint64_t interactive_display_ttl;// Interactive mode: TTL display
int interactive_show_distance; // Show aircraft distance and bearing instead of lat/lon
interactive_distance_unit_t interactive_distance_units; // Units for interactive distance display
char *interactive_callsign_filter; // Filter for interactive display callsigns
uint64_t stats; // Interval (millis) between stats dumps,
int stats_range_histo; // Collect/show a range histogram?
int onlyaddr; // Print only ICAO addresses

View File

@ -50,6 +50,8 @@
#include "dump1090.h"
#include <curses.h>
#include <regex.h>
#include <sys/types.h>
//
//========================= Interactive mode ===============================
@ -77,9 +79,40 @@ static int convert_speed(int kts)
// Show the currently captured interactive data on screen.
//
double distance_units_conversion;
char *distance_units_suffix;
regex_t callsign_filter_regex;
void interactiveInit() {
if (!Modes.interactive)
if (!Modes.interactive) {
return;
}
switch(Modes.interactive_distance_units) {
case UNIT_NAUTICAL_MILES:
distance_units_conversion = 0.53996;
distance_units_suffix = "nm";
break;
case UNIT_STATUTE_MILES:
distance_units_conversion = 0.621371;
distance_units_suffix = "sm";
break;
case UNIT_KILOMETERS:
distance_units_conversion = 1.0;
distance_units_suffix = "km";
break;
}
if (Modes.interactive_callsign_filter) {
int rc = regcomp(&callsign_filter_regex, Modes.interactive_callsign_filter,
REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (rc != 0) {
char msg[256];
regerror(rc, &callsign_filter_regex, msg, sizeof(msg));
fprintf(stderr, "Unable to parse filter '%s': %s\n", Modes.interactive_callsign_filter, msg);
exit(1);
}
}
initscr();
clear();
@ -88,6 +121,7 @@ void interactiveInit() {
void interactiveCleanup(void) {
if (Modes.interactive) {
regfree(&callsign_filter_regex);
endwin();
}
}
@ -106,6 +140,11 @@ void interactiveShowData(void) {
uint64_t now = mstime();
char progress;
char spinner[4] = "|/-\\";
int valid = 0;
double signalMax = -100.0;
double signalMin = +100.0;
double signalMean = 0.0;
double distanceMax = 0.0;
if (!Modes.interactive)
return;
@ -116,39 +155,58 @@ void interactiveShowData(void) {
next_update = now + MODES_INTERACTIVE_REFRESH_TIME;
mvprintw(0, 0, " Hex Mode Sqwk Flight Alt Spd Hdg Lat Long RSSI Msgs Ti");
mvhline(1, 0, ACS_HLINE, 80);
if (Modes.interactive_show_distance) {
mvprintw(1, 0, " Hex Mode Sqwk Flight Alt Spd Hdg Dist(%s) Bearing RSSI Msgs Ti",
distance_units_suffix);
} else {
mvprintw(1, 0, " Hex Mode Sqwk Flight Alt Spd Hdg Lat Long RSSI Msgs Ti");
}
mvhline(2, 0, ACS_HLINE, 80);
progress = spinner[(now/1000)%4];
mvaddch(0, 79, progress);
int rows = getmaxy(stdscr);
int row = 2;
int row = 3;
int rowMaxd = 0;
int rowMaxRSSI = 0;
int rowMinRSSI = 0;
while (a && row < rows) {
if (a->reliable && (now - a->seen) < Modes.interactive_display_ttl) {
while (a) {
if (a->reliable && (now - a->seen) < Modes.interactive_display_ttl
&& (Modes.interactive_callsign_filter == NULL || a->callsign_matched
|| regexec(&callsign_filter_regex, a->callsign, 0, NULL, 0) == 0)) {
char strSquawk[5] = " ";
char strFl[7] = " ";
char strTt[5] = " ";
char strGs[5] = " ";
int msgs = a->messages;
a->callsign_matched = 1;
valid++;
if (trackDataValid(&a->squawk_valid)) {
snprintf(strSquawk,5,"%04x", a->squawk);
snprintf(strSquawk, sizeof(strSquawk), "%04x", a->squawk);
}
if (trackDataValid(&a->gs_valid)) {
snprintf (strGs, 5,"%3d", convert_speed(a->gs));
snprintf (strGs, sizeof(strGs), "%3d", convert_speed(a->gs));
}
if (trackDataValid(&a->track_valid)) {
snprintf (strTt, 5,"%03.0f", a->track);
snprintf (strTt, sizeof(strTt), "%03.0f", a->track);
}
if (msgs > 99999) {
msgs = 99999;
}
double distance = 0.0;
char strDistance[8] = " ";
double bearing = 0.0;
char strBearing[9] = " ";
char strMode[5] = " ";
char strLat[8] = " ";
char strLon[9] = " ";
@ -168,28 +226,70 @@ void interactiveShowData(void) {
}
if (trackDataValid(&a->position_valid)) {
snprintf(strLat, 8,"%7.03f", a->lat);
snprintf(strLon, 9,"%8.03f", a->lon);
snprintf(strLat, sizeof(strLat), "%7.03f", a->lat);
snprintf(strLon, sizeof(strLon), "%8.03f", a->lon);
}
if (trackDataValid(&a->airground_valid) && a->airground == AG_GROUND) {
snprintf(strFl, 7," grnd");
snprintf(strFl, sizeof(strFl), "grnd ");
} else if (Modes.use_gnss && trackDataValid(&a->altitude_geom_valid)) {
snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_geom));
snprintf(strFl, sizeof(strFl), "%5dH", convert_altitude(a->altitude_geom));
} else if (trackDataValid(&a->altitude_baro_valid)) {
snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude_baro));
snprintf(strFl, sizeof(strFl), "%5d ", convert_altitude(a->altitude_baro));
}
double signalDisplay = 10.0 * log10(signalAverage);
if ((Modes.bUserFlags & MODES_USER_LATLON_VALID) && trackDataValid(&a->position_valid)) {
distance = greatcircle(Modes.fUserLat, Modes.fUserLon,
a->lat, a->lon);
distance /= 1000.0;
distance *= distance_units_conversion;
if (distance > distanceMax) {
distanceMax = distance;
}
snprintf(strDistance, sizeof(strDistance), "%5.1f ", distance);
bearing = get_bearing(Modes.fUserLat, Modes.fUserLon, a->lat, a->lon);
snprintf(strBearing, sizeof(strBearing), "%5.0f ", bearing);
}
mvprintw(row, 0, "%s%06X %-4s %-4s %-8s %6s %3s %3s %7s %8s %5.1f %5d %2.0f",
if (signalDisplay > signalMax) {
signalMax = signalDisplay;
}
if (signalDisplay < signalMin) {
signalMin = signalDisplay;
}
signalMean += signalDisplay;
if (row < rows) {
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;
Modes.interactive_show_distance ? strDistance : strLat,
Modes.interactive_show_distance ? strBearing : strLon,
signalDisplay, msgs, (now - a->seen)/1000.0);
if (signalDisplay >= signalMax) {
rowMaxRSSI = row;
}
if (signalDisplay <= signalMin) {
rowMinRSSI = row;
}
if (distance >= distanceMax) {
rowMaxd = row;
}
++row;
}
}
a = a->next;
}
if (Modes.mode_ac) {
if (Modes.mode_ac && !Modes.interactive_callsign_filter) {
for (unsigned i = 1; i < 4096 && row < rows; ++i) {
if (modeAC_match[i] || modeAC_count[i] < 50 || modeAC_age[i] > 5)
continue;
@ -200,8 +300,9 @@ void interactiveShowData(void) {
int modeC = modeAToModeC(modeA);
if (modeC != INVALID_ALTITUDE) {
strMode[3] = 'C';
snprintf(strFl, 7, "%5d ", convert_altitude(modeC * 100));
snprintf(strFl, sizeof(strFl), "%5d ", convert_altitude(modeC * 100));
}
valid++;
mvprintw(row, 0,
"%7s %-4s %04x %-8s %6s %3s %3s %7s %8s %5s %5d %2d\n",
@ -221,8 +322,24 @@ void interactiveShowData(void) {
}
}
move(row, 0);
clrtobot();
if (rowMaxd > 3 && Modes.interactive_show_distance) {
mvprintw(rowMaxd, 52, "^");
}
mvprintw(rowMaxd, 52, "+");
mvprintw(rowMaxRSSI, 68, "+");
mvprintw(rowMinRSSI, 68, "-");
mvprintw(0, 0, " Tot: %3d Vis: %3d RSSI: Max %5.1f+ Mean %5.1f Min %5.1f- MaxD: %6.1f%s+ ",
valid, (rows-3) < valid ? (rows-3) : valid, signalMax, signalMean / valid, signalMin, distanceMax,
distance_units_suffix);
if (row < rows) {
move(row, 0);
clrtobot();
}
move(0, 0);
refresh();
}

26
track.c
View File

@ -207,7 +207,7 @@ static int compare_validity(const data_validity *lhs, const data_validity *rhs)
// Distance between points on a spherical earth.
// This has up to 0.5% error because the earth isn't actually spherical
// (but we don't use it in situations where that matters)
static double greatcircle(double lat0, double lon0, double lat1, double lon1)
double greatcircle(double lat0, double lon0, double lat1, double lon1)
{
double dlat, dlon;
@ -229,6 +229,25 @@ static double greatcircle(double lat0, double lon0, double lat1, double lon1)
return 6371e3 * acos(sin(lat0) * sin(lat1) + cos(lat0) * cos(lat1) * cos(dlon));
}
double get_bearing(double lat0, double lon0, double lat1, double lon1)
{
double dlon;
lat0 = lat0 * M_PI / 180.0;
lon0 = lon0 * M_PI / 180.0;
lat1 = lat1 * M_PI / 180.0;
lon1 = lon1 * M_PI / 180.0;
dlon = (lon1 - lon0);
double x = (cos(lat0) * sin(lat1)) -
(sin(lat0) * cos(lat1) * cos(dlon));
double y = sin(dlon) * cos(lat1);
double degree = atan2(y, x) * 180 / M_PI;
return (degree >= 0)? degree : (degree + 360);
}
static void update_range_histogram(double lat, double lon)
{
if (Modes.stats_range_histo && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) {
@ -1128,6 +1147,11 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
}
if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source)) {
if (strcmp(a->callsign, mm->callsign) != 0) {
// The callsign changed so tell interactive to
// re-evaluate its callsign filter regex if it has one
a->callsign_matched = 0;
}
memcpy(a->callsign, mm->callsign, sizeof(a->callsign));
}

View File

@ -108,6 +108,7 @@ struct aircraft {
data_validity callsign_valid;
char callsign[9]; // Flight number
int callsign_matched; // Interactive callsign filter matched
data_validity altitude_baro_valid;
int altitude_baro; // Altitude (Baro)
@ -321,4 +322,11 @@ static inline unsigned indexToModeA(unsigned index)
return (index & 0007) | ((index & 0070) << 1) | ((index & 0700) << 2) | ((index & 07000) << 3);
}
/* Great Circle distance in m */
double greatcircle(double lat0, double lon0, double lat1, double lon1);
/* Get bearing from 2 points */
double get_bearing(double lat0, double lon0, double lat1, double lon1);
#endif

View File

@ -107,11 +107,15 @@ static void view1090Init(void) {
//
static void showHelp(void) {
printf(
"-----------------------------------------------------------------------------\n"
"-------------------------------------------------------------------------------------\n"
"| view1090 ModeS Viewer %45s |\n"
"-----------------------------------------------------------------------------\n"
"-------------------------------------------------------------------------------------\n"
"--no-interactive Disable interactive mode, print messages to stdout\n"
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
"--interactive-show-distance Show aircraft distance and bearing instead of lat/lon\n"
" (requires --lat and --lon)\n"
"--interactive-distance-units Distance units ('km', 'sm', 'nm') (default: 'nm')\n"
"--interactive-callsign-filter Only callsigns that match the prefix or regex will be displayed\n"
"--modeac Enable decoding of SSR modes 3/A & 3/C\n"
"--net-bo-ipaddr <IPv4> TCP Beast output listen IPv4 (default: 127.0.0.1)\n"
"--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
@ -168,7 +172,20 @@ int main(int argc, char **argv) {
Modes.interactive = 0;
} else if (!strcmp(argv[j],"--interactive-ttl") && more) {
Modes.interactive_display_ttl = (uint64_t)(1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--lat") && more) {
} else if (!strcmp(argv[j], "--interactive-show-distance")) {
Modes.interactive_show_distance = 1;
} else if (!strcmp(argv[j], "--interactive-distance-units") && more) {
char *units = argv[++j];
if (!strcmp(units, "km")) {
Modes.interactive_distance_units = UNIT_KILOMETERS;
} else if (!strcmp(units, "sm")) {
Modes.interactive_distance_units = UNIT_STATUTE_MILES;
} else {
Modes.interactive_distance_units = UNIT_NAUTICAL_MILES;
}
} else if (!strcmp(argv[j], "--interactive-callsign-filter") && more) {
Modes.interactive_callsign_filter = strdup(argv[++j]);
} else if (!strcmp(argv[j], "--lat") && more) {
Modes.fUserLat = atof(argv[++j]);
} else if (!strcmp(argv[j],"--lon") && more) {
Modes.fUserLon = atof(argv[++j]);