diff --git a/debian/config-template b/debian/config-template index a9d4b99..2a9d343 100644 --- a/debian/config-template +++ b/debian/config-template @@ -52,6 +52,11 @@ AGGRESSIVE= LAT= LON= +# If set, provides the absolute maximum receiver range used to +# filter bad position reports, and to determine when local position +# decoding is safe to use. Specify this in nautical miles (NM). +MAX_RANGE= + # # Networking options # diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config index 824e1cc..517f63e 100644 --- a/debian/dump1090-mutability.config +++ b/debian/dump1090-mutability.config @@ -30,6 +30,7 @@ if [ -e $CONFIGFILE ]; then db_set_yn $NAME/decode-aggressive "$AGGRESSIVE" db_set $NAME/decode-lat "$LAT" db_set $NAME/decode-lon "$LON" + db_set $NAME/decode-max-range "$MAX_RANGE" db_set $NAME/net-http-port "$HTTP_PORT" db_set $NAME/net-ri-port "$RAW_INPUT_PORT" @@ -178,6 +179,7 @@ db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then db_input low $NAME/decode-fix-crc || true db_input low $NAME/decode-aggressive || true + db_input_verify medium $NAME/decode-max-range is_number || true db_input_verify medium $NAME/decode-lat is_number_or_empty || true db_go || true; db_get $NAME/decode-lat; if [ -n "$RET" ]; then diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index f28bbd4..dd20631 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -42,6 +42,7 @@ SCRIPTNAME=/etc/init.d/$NAME [ -z "$FATSV_OUTPUT_PORT" ] && FATSV_OUTPUT_PORT=0 [ -z "$NET_BUFFER" ] && NET_BUFFER=0 [ -z "$JSON_INTERVAL" ] && JSON_INTERVAL=0 +[ -z "$MAX_RANGE" ] && MAX_RANGE=300 # receiver: case "x$DEVICE" in @@ -63,6 +64,7 @@ if [ "x$PHASE_ENHANCE" = "xyes" ]; then ARGS="$ARGS --phase-enhance"; fi if [ "x$AGGRESSIVE" = "xyes" ]; then ARGS="$ARGS --aggressive"; fi if [ -n "$LAT" ]; then ARGS="$ARGS --lat $LAT"; fi if [ -n "$LON" ]; then ARGS="$ARGS --lon $LON"; fi +ARGS="$ARGS --max-range $MAX_RANGE"; fi # net: diff --git a/debian/dump1090-mutability.postinst b/debian/dump1090-mutability.postinst index 5306279..bb57a7a 100644 --- a/debian/dump1090-mutability.postinst +++ b/debian/dump1090-mutability.postinst @@ -69,6 +69,7 @@ case "$1" in subvar_yn decode-aggressive AGGRESSIVE subvar decode-lat LAT subvar decode-lon LON + subvar decode-max-range MAX_RANGE subvar net-http-port HTTP_PORT subvar net-ri-port RAW_INPUT_PORT subvar net-ro-port RAW_OUTPUT_PORT diff --git a/debian/dump1090-mutability.templates b/debian/dump1090-mutability.templates index b88ca11..04c12ea 100644 --- a/debian/dump1090-mutability.templates +++ b/debian/dump1090-mutability.templates @@ -102,6 +102,23 @@ Description: Longitude of receiver, in decimal degrees: Type: string Default: +Template: dump1090-mutability/decode-max-range +Description: Absolute maximum range of receiver, in nautical miles: + If the maximum range of the receiver is provided, dump1090 can filter + out impossible position reports that are due to aircraft that transmit + bad data. + . + Additionally, if the maximum range is larger than 180NM, when local + position decoding is used (when insufficient position messages + have been received for global position decoding), it is limited to + only those positions that would unambiguously decode to a single + position within the given receiver range. + . + This range should be the absolute maximum range - any position data + from further away will be entirely discarded! +Type: string +Default: 300 + Template: dump1090-mutability/net-http-port Description: Port for internal webserver (0 disables): dump1090 can provide an internal webserver that serves a basic "virtual diff --git a/dump1090.c b/dump1090.c index c2e7c4e..8d7c8ca 100644 --- a/dump1090.c +++ b/dump1090.c @@ -87,6 +87,7 @@ void modesInitConfig(void) { Modes.fUserLon = MODES_USER_LONGITUDE_DFLT; Modes.json_interval = 1; Modes.json_location_accuracy = 1; + Modes.maxRange = 1852 * 300; // 300NM default max range } // //========================================================================= @@ -460,6 +461,7 @@ void showHelp(void) { "--net-buffer TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n" "--lat Reference/receiver latitude for surface posn (opt)\n" "--lon Reference/receiver longitude for surface posn (opt)\n" +"--max-range Absolute maximum range for position decoding (in nm, default: 300)\n" "--fix Enable single-bits error correction using CRC\n" "--no-fix Disable single-bits error correction using CRC\n" "--no-crc-check Disable messages with broken CRC (discouraged)\n" @@ -822,6 +824,8 @@ int main(int argc, char **argv) { Modes.fUserLat = atof(argv[++j]); } else if (!strcmp(argv[j],"--lon") && more) { Modes.fUserLon = atof(argv[++j]); + } else if (!strcmp(argv[j],"--max-range") && more) { + Modes.maxRange = atof(argv[++j]) * 1852.0; // convert to metres } else if (!strcmp(argv[j],"--debug") && more) { char *f = argv[++j]; while(*f) { diff --git a/dump1090.h b/dump1090.h index a12f46a..00978fd 100644 --- a/dump1090.h +++ b/dump1090.h @@ -367,6 +367,7 @@ struct { // Internal state double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location double fUserLon; // Users receiver/antenna lat/lon needed for initial surface location int bUserFlags; // Flags relating to the user details + double maxRange; // Absolute maximum decoding range, in *metres* // Interactive mode struct aircraft *aircrafts; diff --git a/mode_s.c b/mode_s.c index decd532..40e4111 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2490,6 +2490,19 @@ int cprNFunction(double lat, int fflag) { double cprDlonFunction(double lat, int fflag, int surface) { return (surface ? 90.0 : 360.0) / cprNFunction(lat, fflag); } + +// 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) +{ + lat0 = lat0 * M_PI / 180.0; + lon0 = lon0 * M_PI / 180.0; + lat1 = lat1 * M_PI / 180.0; + lon1 = lon1 * M_PI / 180.0; + return 6371e3 * acos(sin(lat0) * sin(lat1) + cos(lat0) * cos(lat1) * cos(fabs(lon0 - lon1))); +} + // //========================================================================= // @@ -2516,6 +2529,8 @@ int decodeCPR(struct aircraft *a, int fflag, int surface) { double surface_rlat = MODES_USER_LATITUDE_DFLT; double surface_rlon = MODES_USER_LONGITUDE_DFLT; + double rlat, rlon; + if (surface) { // If we're on the ground, make sure we have a (likely) valid Lat/Lon if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && (((int)(now - a->seenLatLon)) < Modes.interactive_display_ttl)) { @@ -2553,26 +2568,35 @@ int decodeCPR(struct aircraft *a, int fflag, int surface) { int ni = cprNFunction(rlat1,1); int m = (int) floor((((lon0 * (cprNLFunction(rlat1)-1)) - (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5); - a->lon = cprDlonFunction(rlat1, 1, surface) * (cprModInt(m, ni)+lon1/131072); - a->lat = rlat1; + rlon = cprDlonFunction(rlat1, 1, surface) * (cprModInt(m, ni)+lon1/131072); + rlat = rlat1; } else { // Use even packet. int ni = cprNFunction(rlat0,0); int m = (int) floor((((lon0 * (cprNLFunction(rlat0)-1)) - (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5); - a->lon = cprDlonFunction(rlat0, 0, surface) * (cprModInt(m, ni)+lon0/131072); - a->lat = rlat0; + rlon = cprDlonFunction(rlat0, 0, surface) * (cprModInt(m, ni)+lon0/131072); + rlat = rlat0; } if (surface) { // Pick the quadrant that's closest to the reference location - // this is not necessarily the same quadrant that contains the // reference location. - a->lon += floor( (surface_rlon - a->lon + 45) / 90 ) * 90; // if we are >45 degrees away, move towards the reference position - a->lon -= floor( (a->lon + 180) / 360 ) * 360; // normalize to (-180..+180) - } else if (a->lon > 180) { - a->lon -= 360; + rlon += floor( (surface_rlon - rlon + 45) / 90 ) * 90; // if we are >45 degrees away, move towards the reference position + rlon -= floor( (rlon + 180) / 360 ) * 360; // normalize to (-180..+180) + } else if (rlon > 180) { + rlon -= 360; } + // check max range + if (Modes.maxRange > 0 && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { + double range = greatcircle(Modes.fUserLat, Modes.fUserLon, rlat, rlon); + if (range > Modes.maxRange) + return (-2); // we consider an out-of-range value to be bad data + } + + a->lat = rlat; + a->lon = rlon; a->seenLatLon = a->seen; a->timestampLatLon = a->timestamp; a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK); @@ -2595,14 +2619,31 @@ int decodeCPRrelative(struct aircraft *a, int fflag, int surface) { double lon; double lonr, latr; double rlon, rlat; + double range_limit = 0; int j,m; if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first + int elapsed = (int)(time(NULL) - a->seenLatLon); + latr = a->lat; lonr = a->lon; + + // impose a range limit based on 2000km/h speed + range_limit = 5e3 + (2000e3 * elapsed / 3600); // 5km + 2000km/h } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { // Try ground station relative next latr = Modes.fUserLat; lonr = Modes.fUserLon; + + // The cell size is at least 360NM, giving a nominal + // max range of 180NM (half a cell). + // + // If the receiver range is more than half a cell + // then we must limit this range further to avoid + // ambiguity. (e.g. if we receive a position report + // at 200NM distance, this may resolve to a position + // at (200-360) = 160NM in the wrong direction) + if (Modes.maxRange > 1852*180) + range_limit = (1852*360) - Modes.maxRange; } else { return (-1); // Exit with error - can't do relative if we don't have ref. } @@ -2648,6 +2689,20 @@ int decodeCPRrelative(struct aircraft *a, int fflag, int surface) { return (-1); // Time to give up - Longitude error } + // check range limit + if (range_limit > 0) { + double range = greatcircle(latr, lonr, rlat, rlon); + if (range > range_limit) + return (-1); + } + + // check max range + if (Modes.maxRange > 0 && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { + double range = greatcircle(Modes.fUserLat, Modes.fUserLon, rlat, rlon); + if (range > Modes.maxRange) + return (-2); // we consider an out-of-range value to be bad data + } + a->lat = rlat; a->lon = rlon;