diff --git a/.travis.yml b/.travis.yml index da961e2..ec55da6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ matrix: before_install: - if [ `uname` = "Linux" ]; then sudo apt-get update -qq; - sudo apt-get install -y build-essential debhelper librtlsdr-dev libusb-1.0-0-dev pkg-config; + sudo apt-get install -y build-essential debhelper librtlsdr-dev libusb-1.0-0-dev pkg-config fakeroot libbladerf-dev dh-systemd; elif [ `uname` = "Darwin" ]; then brew update; brew install -v librtlsdr; diff --git a/Makefile b/Makefile index b4f6aaa..10388fe 100644 --- a/Makefile +++ b/Makefile @@ -15,16 +15,20 @@ ifeq ($(RTLSDR), yes) ifdef RTLSDR_PREFIX CPPFLAGS += -I$(RTLSDR_PREFIX)/include - LDFLAGS += -L$(RTLSDR_PREFIX)/lib + ifeq ($(STATIC), yes) + LIBS_SDR += -L$(RTLSDR_PREFIX)/lib -Wl,-Bstatic -lrtlsdr -Wl,-Bdynamic -lusb-1.0 + else + LIBS_SDR += -L$(RTLSDR_PREFIX)/lib -lrtlsdr -lusb-1.0 + endif else CFLAGS += $(shell pkg-config --cflags librtlsdr) - LDFLAGS += $(shell pkg-config --libs-only-L librtlsdr) - endif - - ifeq ($(STATIC), yes) - LIBS_SDR += -Wl,-Bstatic -lrtlsdr -Wl,-Bdynamic -lusb-1.0 - else - LIBS_SDR += -lrtlsdr -lusb-1.0 + # some packaged .pc files are massively broken, try to handle it + RTLSDR_LFLAGS := $(shell pkg-config --libs-only-L librtlsdr) + ifeq ($(RTLSDR_LFLAGS),-L) + LIBS_SDR += $(shell pkg-config --libs-only-l --libs-only-other librtlsdr) + else + LIBS_SDR += $(shell pkg-config --libs librtlsdr) + endif endif endif diff --git a/README.md b/README.md index d77133c..696baab 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,14 @@ customized for use within [FlightAware](http://flightaware.com)'s It is designed to build as a Debian package. +## Building under stretch + +```bash +$ sudo apt-get install build-essential debhelper librtlsdr-dev pkg-config dh-systemd libncurses5-dev libbladerf-dev +$ dpkg-buildpackage -b +``` + + ## Building under jessie ### Dependencies - bladeRF @@ -44,8 +52,8 @@ You can probably just run "make" after installing the required dependencies. Binaries are built in the source directory; you will need to arrange to install them (and a method for starting them) yourself. -"make BLADERF=no" will disable bladeRF support and remove the dependency on +``make BLADERF=no`` will disable bladeRF support and remove the dependency on libbladeRF. -"make RTLSDR=no" will disable rtl-sdr support and remove the dependency on +``make RTLSDR=no`` will disable rtl-sdr support and remove the dependency on librtlsdr. diff --git a/debian/changelog b/debian/changelog index f55d4f3..840a683 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,37 @@ +dump1090-fa (3.7.1~dev) UNRELEASED; urgency=medium + + * in development + + -- Eric Tran Mon, 15 Apr 2019 11:23:00 -6000 + +dump1090-fa (3.7.0.1) stable; urgency=medium + + * dump1090: Fix piaware lat/lon variable + + -- Eric Tran Fri, 29 Mar 2019 7:04:00 -6000 + +dump1090-fa (3.7.0) stable; urgency=medium + + * dump1090: Fix Rc decoding errors + * dump1090: Compute ADS-B v0 NACp/SIL + * dump1090: When generating aircraft.json, leave space for the final line; + otherwise the generated json may have trailing garbage + * dump1090: Don't update the known-address-set from DF18 messages + * Skyview: use heading data for icon orientation if track data is unavailable + * Skyview: don't spin forever if there's no history to load / receiver.json + is missing + * dump1090: Bail out if rtlsdr_read_async() returns early; it probably means we lost + the USB device. There was a workaround for this (originally implemented + in dump1090-mutability) that got lost in the refactoring needed to support + different SDRs. librtlsdr can still be flaky under disconnect conditions, so + this won't catch everything. + * dump1090: add ENABLED to /etc/default/dump1090-fa + * dump1090: track FMS and MCP selected altitudes separately + * skyview: use whichever selected altitude is available + * faup1090: updates to support PiAware 3.7.0 + + -- Oliver Jowett Fri, 22 Mar 2019 15:58:04 +0000 + dump1090-fa (3.6.3) stable; urgency=medium * Fix port 30003 output (Basestation) timestamp formatting broken in 3.6.0 diff --git a/debian/dump1090-fa.default b/debian/dump1090-fa.default index d646aaa..9d03380 100644 --- a/debian/dump1090-fa.default +++ b/debian/dump1090-fa.default @@ -1,11 +1,14 @@ # dump1090-fa configuration -# This is read by the systemd service file as an environment file, -# and evaluated by some scripts as a POSIX shell fragment. +# This is sourced by /usr/share/dump1090-fa/start-dump1090-fa as a +# shellscript fragment. # If you are using a PiAware sdcard image, this config file is regenerated # on boot based on the contents of piaware-config.txt; any changes made to this # file will be lost. +# dump1090-fa won't automatically start unless ENABLED=yes +ENABLED=yes + RECEIVER_OPTIONS="--device-index 0 --gain -10 --ppm 0 --net-bo-port 30005" DECODER_OPTIONS="--max-range 360" NET_OPTIONS="--net --net-heartbeat 60 --net-ro-size 1000 --net-ro-interval 1 --net-ri-port 0 --net-ro-port 30002 --net-sbs-port 30003 --net-bi-port 30004,30104 --net-bo-port 30005" diff --git a/debian/dump1090-fa.install b/debian/dump1090-fa.install index ad434ce..67292d1 100644 --- a/debian/dump1090-fa.install +++ b/debian/dump1090-fa.install @@ -1,3 +1,4 @@ public_html/* usr/share/dump1090-fa/html debian/lighttpd/* etc/lighttpd/conf-available -bladerf/* /usr/share/dump1090-fa/bladerf +bladerf/* usr/share/dump1090-fa/bladerf +debian/start-dump1090-fa usr/share/dump1090-fa/ diff --git a/debian/dump1090-fa.postinst b/debian/dump1090-fa.postinst index 8c58aa7..d533ccf 100644 --- a/debian/dump1090-fa.postinst +++ b/debian/dump1090-fa.postinst @@ -49,6 +49,20 @@ case "$1" in fi fi + # on upgrade, add an ENABLED line if it's not already present + if dpkg --compare-versions "$2" lt-nl "3.7.0" + then + if [ -f /etc/default/dump1090-fa ] + then + if ! grep -q -E '^ENABLED=' /etc/default/dump1090-fa + then + echo "Setting ENABLED=yes in /etc/default/dump1090-fa.." >&2 + echo "# Automatically added by upgrade from $2" >>/etc/default/dump1090-fa + echo "ENABLED=yes" >>/etc/default/dump1090-fa + fi + fi + fi + echo "Restarting lighttpd.." >&2 invoke-rc.d lighttpd restart || true ;; diff --git a/debian/dump1090-fa.service b/debian/dump1090-fa.service index 4b8ea7b..f05607d 100644 --- a/debian/dump1090-fa.service +++ b/debian/dump1090-fa.service @@ -7,17 +7,15 @@ Wants=network.target After=network.target [Service] -EnvironmentFile=/etc/default/dump1090-fa -EnvironmentFile=-/var/cache/piaware/location.env User=dump1090 RuntimeDirectory=dump1090-fa RuntimeDirectoryMode=0755 -ExecStart=/usr/bin/dump1090-fa \ - $RECEIVER_OPTIONS $DECODER_OPTIONS $NET_OPTIONS $JSON_OPTIONS $PIAWARE_DUMP1090_LOCATION_OPTIONS \ - --write-json /run/dump1090-fa --quiet +ExecStart=/usr/share/dump1090-fa/start-dump1090-fa --write-json %t/dump1090-fa --quiet +SyslogIdentifier=dump1090-fa Type=simple Restart=on-failure RestartSec=30 +RestartPreventExitStatus=64 Nice=-5 [Install] diff --git a/debian/start-dump1090-fa b/debian/start-dump1090-fa new file mode 100755 index 0000000..37186fe --- /dev/null +++ b/debian/start-dump1090-fa @@ -0,0 +1,33 @@ +#!/bin/sh + +# Helper script that reads /etc/default/dump1090-fa +# and either starts dump1090-fa with the configured +# arguments, or exits with status 64 to tell systemd +# not to auto-restart the service. + +if [ -f /etc/default/dump1090-fa ] +then + . /etc/default/dump1090-fa +fi + +if [ -f /var/cache/piaware/location.env ] +then + . /var/cache/piaware/location.env +fi + +if [ "x$ENABLED" != "xyes" ] +then + echo "dump1090-fa not enabled in /etc/default/dump1090-fa" >&2 + exit 64 +fi + +if [ -n "$PIAWARE_LAT" -a -n "$PIAWARE_LON" ] +then + POSITION="--lat $PIAWARE_LAT --lon $PIAWARE_LON" +fi + +exec /usr/bin/dump1090-fa \ + $RECEIVER_OPTIONS $DECODER_OPTIONS $NET_OPTIONS $JSON_OPTIONS $POSITION \ + "$@" +# exec failed, do not restart +exit 64 diff --git a/dump1090.c b/dump1090.c index 4e55367..b654c01 100644 --- a/dump1090.c +++ b/dump1090.c @@ -224,15 +224,12 @@ void *readerThreadEntryPoint(void *arg) // Wake the main thread (if it's still waiting) pthread_mutex_lock(&Modes.data_mutex); - Modes.exit = 1; // just in case + if (!Modes.exit) + Modes.exit = 2; // unexpected exit pthread_cond_signal(&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); -#ifndef _WIN32 - pthread_exit(NULL); -#else return NULL; -#endif } // // ============================== Snip mode ================================= @@ -760,15 +757,15 @@ int main(int argc, char **argv) { display_total_stats(); } - log_with_timestamp("Normal exit."); - sdrClose(); -#ifndef _WIN32 - pthread_exit(0); -#else - return (0); -#endif + if (Modes.exit == 1) { + log_with_timestamp("Normal exit."); + return 0; + } else { + log_with_timestamp("Abnormal exit."); + return 1; + } } // //========================================================================= diff --git a/dump1090.h b/dump1090.h index 4e0340c..43e6109 100644 --- a/dump1090.h +++ b/dump1090.h @@ -227,6 +227,10 @@ typedef enum { EMERGENCY_RESERVED = 7 } emergency_t; +typedef enum { + NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS +} nav_altitude_source_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -304,7 +308,7 @@ struct { // Internal state double sample_rate; // actual sample rate in use (in hz) uint16_t *log10lut; // Magnitude -> log10 lookup table - int exit; // Exit from the main loop when true + int exit; // Exit from the main loop when true (2 = unclean exit) // Sample conversion int dc_filter; // should we apply a DC filter? @@ -581,7 +585,7 @@ struct modesMessage { unsigned mcp_altitude; // MCP/FCU selected altitude float qnh; // altimeter setting (QFE or QNH/QNE), millibars - enum { NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS } altitude_source; + nav_altitude_source_t altitude_source; nav_modes_t modes; } nav; diff --git a/faup1090.c b/faup1090.c index 425bc92..e476a7e 100644 --- a/faup1090.c +++ b/faup1090.c @@ -204,7 +204,6 @@ int main(int argc, char **argv) { // Set up output connection on stdout fatsv_output = makeFatsvOutputService(); createGenericClient(fatsv_output, STDOUT_FILENO); - writeFATSVHeader(); // Run it until we've lost either connection while (!Modes.exit && beast_input->connections && fatsv_output->connections) { diff --git a/mode_s.c b/mode_s.c index e8387a1..552cf39 100644 --- a/mode_s.c +++ b/mode_s.c @@ -568,9 +568,9 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) mm->CC = getbit(msg, 7); } - // CF (Control field) + // CF (Control field, see Figure 2-2 ADS-B Message BaselineFormat Structure) if (mm->msgtype == 18) { - mm->CF = getbits(msg, 5, 8); + mm->CF = getbits(msg, 6, 8); } // DR (Downlink Request) @@ -1053,7 +1053,7 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) // nothing break; } - // 10: target altitude type (ignored) + // 10: target altitude type (MSL or Baro, ignored) // 11: backward compatibility bit, always 0 // 12-13: target alt capabilities (ignored) // 14-15: vertical mode @@ -1081,7 +1081,7 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) break; } - // 16-25: altitude + // 16-25: target altitude int alt = -1000 + 100 * getbits(me, 16, 25); switch (mm->nav.altitude_source) { case NAV_ALT_MCP: @@ -1109,7 +1109,7 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) mm->nav.heading_type = HEADING_MAGNETIC_OR_TRUE; } } - // 38-39: horiontal mode + // 38-39: horizontal mode switch (getbits(me, 38, 39)) { case 1: // acquiring case 2: // maintaining diff --git a/net_io.c b/net_io.c index 3bc2f85..f98b7c4 100644 --- a/net_io.c +++ b/net_io.c @@ -1155,8 +1155,10 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s p = safe_snprintf(p, end, "\"emergency\","); if (a->nav_qnh_valid.source == source) p = safe_snprintf(p, end, "\"nav_qnh\","); - if (a->nav_altitude_valid.source == source) - p = safe_snprintf(p, end, "\"nav_altitude\","); + if (a->nav_altitude_mcp_valid.source == source) + p = safe_snprintf(p, end, "\"nav_altitude_mcp\","); + if (a->nav_altitude_fms_valid.source == source) + p = safe_snprintf(p, end, "\"nav_altitude_fms\","); if (a->nav_heading_valid.source == source) p = safe_snprintf(p, end, "\"nav_heading\","); if (a->nav_modes_valid.source == source) @@ -1267,6 +1269,18 @@ static const char *sil_type_enum_string(sil_type_t type) } } +static const char *nav_altitude_source_enum_string(nav_altitude_source_t src) +{ + switch (src) { + case NAV_ALT_INVALID: return "invalid"; + case NAV_ALT_UNKNOWN: return "unknown"; + case NAV_ALT_AIRCRAFT: return "aircraft"; + case NAV_ALT_MCP: return "mcp"; + case NAV_ALT_FMS: return "fms"; + default: return "invalid"; + } +} + char *generateAircraftJson(const char *url_path, int *len) { uint64_t now = mstime(); struct aircraft *a; @@ -1341,8 +1355,10 @@ char *generateAircraftJson(const char *url_path, int *len) { 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_valid)) - p = safe_snprintf(p, end, ",\"nav_altitude\":%d", a->nav_altitude); + 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)) { @@ -1534,7 +1550,7 @@ char *generateStatsJson(const char *url_path, int *len) { p = appendStatsJson(p, end, &add, "total"); p = safe_snprintf(p, end, "\n}\n"); - assert(p <= end); + assert(p < end); *len = p-buf; return buf; @@ -1862,27 +1878,8 @@ __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *en return p; } -#define TSV_MAX_PACKET_SIZE 600 -#define TSV_VERSION 3 - -void writeFATSVHeader() -{ - char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); - if (!p) - return; - - char *end = p + TSV_MAX_PACKET_SIZE; - - p = appendFATSV(p, end, "clock", "%" PRIu64, mstime() / 1000); - p = appendFATSV(p, end, "tsv_version", "%u", TSV_VERSION); - --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)); -} +#define TSV_MAX_PACKET_SIZE 800 +#define TSV_VERSION "4E" static void writeFATSVPositionUpdate(float lat, float lon, float alt) { @@ -1901,6 +1898,7 @@ static void writeFATSVPositionUpdate(float lat, float lon, float alt) 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, "type", "%s", "location_update"); p = appendFATSV(p, end, "lat", "%.5f", lat); @@ -1910,7 +1908,7 @@ static void writeFATSVPositionUpdate(float lat, float lon, float alt) --p; // remove last tab p = safe_snprintf(p, end, "\n"); - if (p <= end) + 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)); @@ -1924,6 +1922,7 @@ static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafiel 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, (mm->addr & MODES_NON_ICAO_ADDRESS) ? "otherid" : "hexid", "%06X", mm->addr & 0xFFFFFF); if (mm->addrtype != ADDR_ADSB_ICAO) { @@ -1936,7 +1935,7 @@ static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafiel } p = safe_snprintf(p, end, "\n"); - if (p <= end) + 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)); @@ -2136,7 +2135,9 @@ static void writeFATSV() (trackDataValid(&a->mach_valid) && fabs(a->mach - a->fatsv_emitted_mach) >= 0.02); int immediate = - (trackDataValid(&a->nav_altitude_valid) && unsigned_difference(a->nav_altitude, a->fatsv_emitted_nav_altitude) > 50) || + (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) || (trackDataValid(&a->nav_heading_valid) && heading_difference(a->nav_heading, a->fatsv_emitted_nav_heading) > 2) || (trackDataValid(&a->nav_modes_valid) && a->nav_modes != a->fatsv_emitted_nav_modes) || (trackDataValid(&a->nav_qnh_valid) && fabs(a->nav_qnh - a->fatsv_emitted_nav_qnh) > 0.8) || // 0.8 is the ES message resolution @@ -2174,6 +2175,7 @@ static void writeFATSV() 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); @@ -2236,8 +2238,10 @@ static void writeFATSV() 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", a, &a->nav_altitude_valid, "%u", a->nav_altitude); + 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); @@ -2252,7 +2256,7 @@ static void writeFATSV() --p; // remove last tab p = safe_snprintf(p, end, "\n"); - if (p <= end) + 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)); @@ -2271,7 +2275,9 @@ static void writeFATSV() 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 = a->nav_altitude; + 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; diff --git a/net_io.h b/net_io.h index 0dd3ea8..2c39f6f 100644 --- a/net_io.h +++ b/net_io.h @@ -87,8 +87,6 @@ void modesInitNet(void); void modesQueueOutput(struct modesMessage *mm, struct aircraft *a); void modesNetPeriodicWork(void); -void writeFATSVHeader(); - // TODO: move these somewhere else char *generateAircraftJson(const char *url_path, int *len); char *generateStatsJson(const char *url_path, int *len); diff --git a/prepare-build.sh b/prepare-build.sh index ef0a467..8af69e8 100755 --- a/prepare-build.sh +++ b/prepare-build.sh @@ -11,8 +11,8 @@ then exit 1 fi -export DEBFULLNAME=${DEBFULLNAME:-FlightAware build automation} -export DEBEMAIL=${DEBEMAIL:-adsb-devs@flightaware.com} +export DEBFULLNAME="${DEBFULLNAME:-FlightAware build automation}" +export DEBEMAIL="${DEBEMAIL:-adsb-devs@flightaware.com}" TOP=`dirname $0` DIST=$1 diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 437fc0c..6e81ffd 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -384,7 +384,16 @@ PlaneObject.prototype.updateIcon = function() { var outline = (this.position_from_mlat ? OutlineMlatColor : OutlineADSBColor); var add_stroke = (this.selected && !SelectedAllPlanes) ? ' stroke="black" stroke-width="1px"' : ''; var baseMarker = getBaseMarker(this.category, this.icaotype, this.typeDescription, this.wtc); - var rotation = (this.track === null ? 0 : this.track); + var rotation = this.track; + if (rotation === null) { + rotation = this.true_heading; + } + if (rotation === null) { + rotation = this.mag_heading; + } + if (rotation === null) { + rotation = 0; + } //var transparentBorderWidth = (32 / baseMarker.scale / scaleFactor).toFixed(1); var svgKey = col + '!' + outline + '!' + baseMarker.svg + '!' + add_stroke + "!" + scaleFactor; @@ -445,7 +454,7 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { var fields = ["alt_baro", "alt_geom", "gs", "ias", "tas", "track", "track_rate", "mag_heading", "true_heading", "mach", - "roll", "nav_altitude", "nav_heading", "nav_modes", + "roll", "nav_heading", "nav_modes", "nac_p", "nac_v", "nic_baro", "sil_type", "sil", "nav_qnh", "baro_rate", "geom_rate", "rc", "squawk", "category", "version"]; @@ -498,6 +507,15 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { this.altitude = null; } + // Pick a selected altitude + if ('nav_altitude_fms' in data) { + this.nav_altitude = data.nav_altitude_fms; + } else if ('nav_altitude_mcp' in data) { + this.nav_altitude = data.nav_altitude_mcp; + } else { + this.nav_altitude = null; + } + // Pick vertical rate from either baro or geom rate // geometric rate is generally more reliable (smoothed etc) if ('geom_rate' in data) { diff --git a/public_html/script.js b/public_html/script.js index ae98b09..ad372f1 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -389,6 +389,9 @@ function start_load_history() { for (var i = 0; i < PositionHistorySize; i++) { load_history_item(i); } + } else { + // Nothing to load + end_load_history(); } } diff --git a/sdr_bladerf.c b/sdr_bladerf.c index 8577b9d..65ef2a8 100644 --- a/sdr_bladerf.c +++ b/sdr_bladerf.c @@ -23,6 +23,11 @@ #include #include +// Polyfill for the older bladerf API +#if defined(LIBBLADERF_API_VERSION) && (LIBBLADERF_API_VERSION < 0x02000000) +typedef unsigned int bladerf_frequency; +#endif + static struct { const char *device_str; const char *fpga_path; @@ -102,7 +107,7 @@ static void show_config() int status; unsigned rate; - unsigned freq; + bladerf_frequency freq; bladerf_lpf_mode lpf_mode; unsigned lpf_bw; bladerf_lna_gain lna_gain; diff --git a/sdr_rtlsdr.c b/sdr_rtlsdr.c index d1c1d79..f2fc30b 100644 --- a/sdr_rtlsdr.c +++ b/sdr_rtlsdr.c @@ -362,10 +362,11 @@ void rtlsdrRun() start_cpu_timing(&rtlsdr_thread_cpu); - while (!Modes.exit) { - rtlsdr_read_async(RTLSDR.dev, rtlsdrCallback, NULL, - /* MODES_RTL_BUFFERS */ 4, - MODES_RTL_BUF_SIZE); + rtlsdr_read_async(RTLSDR.dev, rtlsdrCallback, NULL, + /* MODES_RTL_BUFFERS */ 4, + MODES_RTL_BUF_SIZE); + if (!Modes.exit) { + fprintf(stderr, "rtlsdr: rtlsdr_read_async returned unexpectedly, probably lost the USB device, bailing out\n"); } } diff --git a/track.c b/track.c index be6689c..bd34c1a 100644 --- a/track.c +++ b/track.c @@ -116,7 +116,9 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { F(squawk, 15, 70); // ADS-B or Mode S F(airground, 15, 70); // ADS-B or Mode S F(nav_qnh, 60, 70); // Comm-B only - F(nav_altitude, 60, 70); // ADS-B or Comm-B + F(nav_altitude_mcp, 60, 70); // ADS-B or Comm-B + F(nav_altitude_fms, 60, 70); // ADS-B or Comm-B + F(nav_altitude_src, 60, 70); // ADS-B or Comm-B F(nav_heading, 60, 70); // ADS-B or Comm-B F(nav_modes, 60, 70); // ADS-B or Comm-B F(cpr_odd, 60, 70); // ADS-B only @@ -302,6 +304,18 @@ static int speed_check(struct aircraft *a, double lat, double lon, int surface) return inrange; } +// return 1 if left_rc is worse (less accurate) than right_rc +static int rcIsWorse(int left_rc, int right_rc) +{ + if (left_rc == 0 && right_rc == 0) // both unknown + return 0; + if (left_rc == 0) + return 1; // left unknown < right known + if (right_rc == 0) + return 0; // left known > right unknown + return (left_rc > right_rc); +} + static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nic, unsigned *rc) { int result; @@ -309,9 +323,9 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, int surface = (mm->cpr_type == CPR_SURFACE); // derive NIC, Rc from the worse of the two position - // smaller NIC is worse; larger Rc is worse + // smaller NIC is worse *nic = (a->cpr_even_nic < a->cpr_odd_nic ? a->cpr_even_nic : a->cpr_odd_nic); - *rc = (a->cpr_even_rc > a->cpr_odd_rc ? a->cpr_even_rc : a->cpr_odd_rc); + *rc = (rcIsWorse(a->cpr_even_rc, a->cpr_odd_rc) ? a->cpr_even_rc : a->cpr_odd_rc); if (surface) { // surface global CPR @@ -372,7 +386,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, return result; // check speed limit - if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && a->pos_rc <= *rc && !speed_check(a, *lat, *lon, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && !rcIsWorse(a->pos_rc, *rc) && !speed_check(a, *lat, *lon, surface)) { Modes.stats_current.cpr_global_speed_checks++; return -2; } @@ -404,7 +418,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, if (a->pos_nic < *nic) *nic = a->pos_nic; - if (a->pos_rc < *rc) + if (rcIsWorse(a->pos_rc, *rc)) *rc = a->pos_rc; range_limit = 50e3; @@ -454,7 +468,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, } // check speed limit - if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && a->pos_rc <= *rc && !speed_check(a, *lat, *lon, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && !rcIsWorse(a->pos_rc, *rc) && !speed_check(a, *lat, *lon, surface)) { #ifdef DEBUG_CPR_CHECKS fprintf(stderr, "Speed check for %06X with local decoding failed\n", a->addr); #endif @@ -661,6 +675,8 @@ static unsigned compute_nic(unsigned metype, unsigned version, unsigned nic_a, u static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, unsigned nic_b, unsigned nic_c) { + // ED-102 Table 2-14, Table N-4, Table N-11 + switch (metype) { case 5: // surface case 9: // airborne @@ -696,7 +712,7 @@ static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, un } else if (nic_a && !nic_c) { return 556; // 555.6m, 0.3NM } else if (!nic_a && nic_c) { - return 926; // 926m, 0.5NM + return 1111; // 1111m, 0.6NM } else { return RC_UNKNOWN; } @@ -709,16 +725,16 @@ static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, un if (nic_a && nic_b) { return 75; } else { - return 186; // 370.4m, 0.2NM + return 186; // 185.2m, 0.1NM } } else if (version == 1) { if (nic_a) { return 75; } else { - return 186; // 370.4m, 0.2NM + return 186; // 185.2m, 0.1NM } } else { - return 186; // 370.4m, 0.2NM + return 186; // 185.2m, 0.1NM } case 12: // airborne @@ -776,6 +792,81 @@ static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, un } } +// Map ADS-B v0 position message type to NACp value +// returned computed NACp, or -1 if not a suitable message type +static int compute_v0_nacp(struct modesMessage *mm) +{ + if (mm->msgtype != 17 && mm->msgtype != 18) { + return -1; + } + + // ED-102A Table N-7 + switch (mm->metype) { + case 0: return 0; + case 5: return 11; + case 6: return 10; + case 7: return 8; + case 8: return 0; + case 9: return 11; + case 10: return 10; + case 11: return 8; + case 12: return 7; + case 13: return 6; + case 14: return 5; + case 15: return 4; + case 16: return 1; + case 17: return 1; + case 18: return 0; + case 20: return 11; + case 21: return 10; + case 22: return 0; + default: return -1; + } +} + +// Map ADS-B v0 position message type to SIL value +// returned computed SIL, or -1 if not a suitable message type +static int compute_v0_sil(struct modesMessage *mm) +{ + if (mm->msgtype != 17 && mm->msgtype != 18) { + return -1; + } + + // ED-102A Table N-8 + switch (mm->metype) { + case 0: + return 0; + + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + return 2; + + case 18: + return 0; + + case 20: + case 21: + return 2; + + case 22: + return 0; + + default: + return -1; + } +} + static void compute_nic_rc_from_message(struct modesMessage *mm, struct aircraft *a, unsigned *nic, unsigned *rc) { int nic_a = (trackDataValid(&a->nic_a_valid) && a->nic_a); @@ -861,6 +952,23 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) } } + // fill in ADS-B v0 NACp, SIL from position message type + if (a->adsb_version == 0 && !mm->accuracy.nac_p_valid) { + int computed_nacp = compute_v0_nacp(mm); + if (computed_nacp != -1) { + mm->accuracy.nac_p_valid = 1; + mm->accuracy.nac_p = computed_nacp; + } + } + + if (a->adsb_version == 0 && mm->accuracy.sil_type == SIL_INVALID) { + int computed_sil = compute_v0_sil(mm); + if (computed_sil != -1) { + mm->accuracy.sil_type = SIL_UNKNOWN; + mm->accuracy.sil = computed_sil; + } + } + if (mm->altitude_baro_valid && accept_data(&a->altitude_baro_valid, mm->source)) { int alt = altitude_to_feet(mm->altitude_baro, mm->altitude_baro_unit); if (a->modeC_hit) { @@ -985,12 +1093,16 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) memcpy(a->callsign, mm->callsign, sizeof(a->callsign)); } - // prefer MCP over FMS - // unless the source says otherwise - if (mm->nav.mcp_altitude_valid && mm->nav.altitude_source != NAV_ALT_FMS && accept_data(&a->nav_altitude_valid, mm->source)) { - a->nav_altitude = mm->nav.mcp_altitude; - } else if (mm->nav.fms_altitude_valid && accept_data(&a->nav_altitude_valid, mm->source)) { - a->nav_altitude = mm->nav.fms_altitude; + if (mm->nav.mcp_altitude_valid && accept_data(&a->nav_altitude_mcp_valid, mm->source)) { + a->nav_altitude_mcp = mm->nav.mcp_altitude; + } + + if (mm->nav.fms_altitude_valid && accept_data(&a->nav_altitude_fms_valid, mm->source)) { + a->nav_altitude_fms = mm->nav.fms_altitude; + } + + if (mm->nav.altitude_source != NAV_ALT_INVALID && accept_data(&a->nav_altitude_src_valid, mm->source)) { + a->nav_altitude_src = mm->nav.altitude_source; } if (mm->nav.heading_valid && accept_data(&a->nav_heading_valid, mm->source)) { @@ -1205,7 +1317,9 @@ static void trackRemoveStaleAircraft(uint64_t now) EXPIRE(squawk); EXPIRE(airground); EXPIRE(nav_qnh); - EXPIRE(nav_altitude); + EXPIRE(nav_altitude_mcp); + EXPIRE(nav_altitude_fms); + EXPIRE(nav_altitude_src); EXPIRE(nav_heading); EXPIRE(nav_modes); EXPIRE(cpr_odd); diff --git a/track.h b/track.h index daac95a..3aa31e7 100644 --- a/track.h +++ b/track.h @@ -151,8 +151,14 @@ struct aircraft { data_validity nav_qnh_valid; float nav_qnh; // Altimeter setting (QNH/QFE), millibars - data_validity nav_altitude_valid; - unsigned nav_altitude; // FMS or FCU selected altitude + data_validity nav_altitude_mcp_valid; + unsigned nav_altitude_mcp; // FCU/MCP selected altitude + + data_validity nav_altitude_fms_valid; + unsigned nav_altitude_fms; // FMS selected altitude + + data_validity nav_altitude_src_valid; + nav_altitude_source_t nav_altitude_src; // source of altitude used by automation data_validity nav_heading_valid; float nav_heading; // target heading, degrees (0-359) @@ -220,7 +226,9 @@ struct aircraft { unsigned fatsv_emitted_tas; // -"- TAS float fatsv_emitted_mach; // -"- Mach number airground_t fatsv_emitted_airground; // -"- air/ground state - unsigned fatsv_emitted_nav_altitude; // -"- target altitude + unsigned fatsv_emitted_nav_altitude_mcp; // -"- MCP altitude + unsigned fatsv_emitted_nav_altitude_fms; // -"- FMS altitude + unsigned fatsv_emitted_nav_altitude_src; // -"- automation altitude source float fatsv_emitted_nav_heading; // -"- target heading nav_modes_t fatsv_emitted_nav_modes; // -"- enabled navigation modes float fatsv_emitted_nav_qnh; // -"- altimeter setting