From 98d64483d6739365c6b5af18d89059096bb42584 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 18:07:40 +0100 Subject: [PATCH 01/52] WIP: More Comm-B & ADS-B decoding. --- Makefile | 6 +- comm_b.c | 735 ++++++++++++++++++++++++++++++++++++++++++++++++++ comm_b.h | 26 ++ dump1090.h | 123 +++++---- interactive.c | 12 +- mode_s.c | 376 ++++++++++++-------------- mode_s.h | 100 +++++++ net_io.c | 319 ++++++++++++++-------- track.c | 114 +++++--- track.h | 73 +++-- 10 files changed, 1446 insertions(+), 438 deletions(-) create mode 100644 comm_b.c create mode 100644 comm_b.h create mode 100644 mode_s.h diff --git a/Makefile b/Makefile index a7d4c04..93db17a 100644 --- a/Makefile +++ b/Makefile @@ -45,13 +45,13 @@ all: dump1090 view1090 %.o: %.c *.h $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o sdr_ifile.o sdr.o $(SDR_OBJ) $(COMPAT) +dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o sdr_ifile.o sdr.o $(SDR_OBJ) $(COMPAT) $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_SDR) -lncurses -view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(COMPAT) +view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(COMPAT) $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) -lncurses -faup1090: faup1090.o anet.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(COMPAT) +faup1090: faup1090.o anet.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(COMPAT) $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) clean: diff --git a/comm_b.c b/comm_b.c new file mode 100644 index 0000000..b03ea43 --- /dev/null +++ b/comm_b.c @@ -0,0 +1,735 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// comm_b.c: Comm-B message decoding +// +// Copyright (c) 2017 FlightAware, LLC +// Copyright (c) 2017 Oliver Jowett +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 . + +#include "dump1090.h" + +typedef int (*CommBDecoderFn)(struct modesMessage *,bool); + +static int decodeEmptyResponse(struct modesMessage *mm, bool store); +static int decodeBDS10(struct modesMessage *mm, bool store); +static int decodeBDS17(struct modesMessage *mm, bool store); +static int decodeBDS20(struct modesMessage *mm, bool store); +static int decodeBDS30(struct modesMessage *mm, bool store); +static int decodeBDS40(struct modesMessage *mm, bool store); +static int decodeBDS50(struct modesMessage *mm, bool store); +static int decodeBDS60(struct modesMessage *mm, bool store); + +static CommBDecoderFn comm_b_decoders[] = { + &decodeEmptyResponse, + &decodeBDS10, + &decodeBDS20, + &decodeBDS30, + &decodeBDS17, + &decodeBDS40, + &decodeBDS50, + &decodeBDS60 +}; + +void decodeCommB(struct modesMessage *mm) +{ + mm->commb_format = COMMB_UNKNOWN; + + // If DR or UM are set, this message is _probably_ noise + // as nothing really seems to use the multisite broadcast stuff? + // Also skip anything that had errors corrected + if (mm->DR != 0 || mm->UM != 0 || mm->correctedbits > 0) { + return; + } + + // This is a bit hairy as we don't know what the requested register was + int bestScore = 0; + CommBDecoderFn bestDecoder = NULL; + + for (unsigned i = 0; i < (sizeof(comm_b_decoders) / sizeof(comm_b_decoders[0])); ++i) { + int score = comm_b_decoders[i](mm, false); + if (score > bestScore) { + bestScore = score; + bestDecoder = comm_b_decoders[i]; + } + } + + if (bestDecoder) { + // decode it + bestDecoder(mm, true); + } +} + +static int decodeEmptyResponse(struct modesMessage *mm, bool store) +{ + for (unsigned i = 0; i < 7; ++i) { + if (mm->MB[i] != 0) { + return 0; + } + } + + if (store) { + mm->commb_format = COMMB_EMPTY_RESPONSE; + } + + return 56; +} + +// BDS1,0 Datalink capabilities +static int decodeBDS10(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + // BDS identifier + if (msg[0] != 0x10) { + return 0; + } + + // Reserved bits + if (getbits(msg, 10, 15) != 0) { + return 0; + } + + // Looks plausible. + + if (store) { + mm->commb_format = COMMB_DATALINK_CAPS; + } + + return 56; +} + +// BDS1,7 Common usage GICB capability report +static int decodeBDS17(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + // reserved bits + if (getbits(msg, 25, 56) != 0) { + return 0; + } + + int score = 0; + if (getbit(msg, 7)) { + score += 1; // 2,0 aircraft identification + } else { + // BDS2,0 is on almost everything + score -= 2; + } + + // unlikely bits + if (getbit(msg, 10)) { // 4,1 next waypoint identifier + score -= 2; + } + if (getbit(msg, 11)) { // 4,2 next waypoint position + score -= 2; + } + if (getbit(msg, 12)) { // 4,3 next waypoint information + score -= 2; + } + if (getbit(msg, 13)) { // 4,4 meterological routine report + score -= 2; + } + if (getbit(msg, 14)) { // 4,4 meterological hazard report + score -= 2; + } + if (getbit(msg, 20)) { // 5,4 waypoint 1 + score -= 2; + } + if (getbit(msg, 21)) { // 5,5 waypoint 2 + score -= 2; + } + if (getbit(msg, 22)) { // 5,6 waypoint 3 + score -= 2; + } + + if (getbit(msg, 1) && getbit(msg, 2) && getbit(msg, 3) && getbit(msg, 4) && getbit(msg, 5)) { + // looks like ES capable + score += 5; + if (getbit(msg, 6)) { + // ES EDI + score += 1; + } + } else if (!getbit(msg, 1) && !getbit(msg, 2) && !getbit(msg, 3) && !getbit(msg, 4) && !getbit(msg, 5) && !getbit(msg, 6)) { + // not ES capable + score += 1; + } else { + // partial ES support, unlikely + score -= 12; + } + + if (getbit(msg, 16) && getbit(msg, 24)) { + // track/turn, heading/speed + score += 2; + if (getbit(msg, 9)) { + // vertical intent + score += 1; + } + } else if (!getbit(msg, 16) && !getbit(msg, 24) && !getbit(msg, 9)) { + // neither + score += 1; + } else { + // unlikely + score -= 6; + } + + if (store) { + mm->commb_format = COMMB_GICB_CAPS; + } + + return score; +} + +// BDS2,0 Aircraft identification +static int decodeBDS20(struct modesMessage *mm, bool store) +{ + char callsign[9]; + unsigned char *msg = mm->MB; + + // BDS identifier + if (msg[0] != 0x20) { + return 0; + } + + callsign[0] = ais_charset[getbits(msg, 9, 14)]; + callsign[1] = ais_charset[getbits(msg, 15, 20)]; + callsign[2] = ais_charset[getbits(msg, 21, 26)]; + callsign[3] = ais_charset[getbits(msg, 27, 32)]; + callsign[4] = ais_charset[getbits(msg, 33, 38)]; + callsign[5] = ais_charset[getbits(msg, 39, 44)]; + callsign[6] = ais_charset[getbits(msg, 45, 50)]; + callsign[7] = ais_charset[getbits(msg, 51, 56)]; + callsign[8] = 0; + + // score based on number of valid non-space characters + int score = 8; + for (unsigned i = 0; i < 8; ++i) { + if ((callsign[i] >= 'A' && callsign[i] <= 'Z') || (callsign[i] >= '0' && callsign[i] <= '9')) { + score += 6; + } else if (callsign[i] == ' ') { + // Valid, but not informative + } else { + // Invalid + return 0; + } + } + + if (store) { + mm->commb_format = COMMB_AIRCRAFT_IDENT; + memcpy(mm->callsign, callsign, sizeof(mm->callsign)); + mm->callsign_valid = 1; + } + + return score; +} + +// BDS3,0 ACAS RA +static int decodeBDS30(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + // BDS identifier + if (msg[0] != 0x30) { + return 0; + } + + if (store) { + mm->commb_format = COMMB_ACAS_RA; + } + + // just accept it. + return 56; +} + +// BDS4,0 Selected vertical intention +static int decodeBDS40(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + unsigned mcp_valid = getbit(msg, 1); + unsigned mcp_raw = getbits(msg, 2, 13); + unsigned fms_valid = getbit(msg, 14); + unsigned fms_raw = getbits(msg, 15, 26); + unsigned baro_valid = getbit(msg, 27); + unsigned baro_raw = getbits(msg, 28, 39); + unsigned reserved_1 = getbits(msg, 40, 47); + unsigned mode_valid = getbit(msg, 48); + unsigned mode_raw = getbits(msg, 49, 51); + unsigned reserved_2 = getbits(msg, 52, 53); + unsigned source_valid = getbit(msg, 54); + unsigned source_raw = getbits(msg, 55, 56); + + if (!mcp_valid && !fms_valid && !baro_valid && !mode_valid && !source_valid) { + return 0; + } + + int score = 0; + + unsigned mcp_alt = 0; + if (mcp_valid && mcp_raw != 0) { + mcp_alt = mcp_raw * 16; + if (mcp_alt >= 1000 && mcp_alt <= 50000) { + score += 13; + } + } else if (!mcp_valid && mcp_raw == 0) { + score += 1; + } else { + score -= 130; + } + + unsigned fms_alt = 0; + if (fms_valid && fms_raw != 0) { + fms_alt = fms_raw * 16; + if (fms_alt >= 1000 && fms_alt <= 50000) { + score += 13; + } + } else if (!fms_valid && fms_raw == 0) { + score += 1; + } else { + score -= 130; + } + + float baro_setting = 0; + if (baro_valid && baro_raw != 0) { + baro_setting = 800 + baro_raw * 0.1; + if (baro_setting >= 900 && baro_setting <= 1100) { + score += 13; + } + } else if (!baro_valid && baro_raw == 0) { + score += 1; + } else { + score -= 130; + } + + if (reserved_1 != 0) { + score -= 80; + } + + if (mode_valid) { + score += 4; + } else if (!mode_valid && mode_raw == 0) { + score += 1; + } else { + score -= 40; + } + + if (reserved_2 != 0) { + score -= 20; + } + + if (source_valid) { + score += 3; + } else if (!source_valid && source_raw == 0) { + score += 1; + } else { + score -= 30; + } + + // small bonuses for consistent data + if (mcp_valid && fms_valid && mcp_alt == fms_alt) { + score += 2; + } + + if (baro_valid && baro_raw == 2132) { + // 1013.2mb, standard pressure + score += 2; + } + + if (mcp_valid) { + unsigned remainder = mcp_alt % 500; + if (remainder < 16 || remainder > 484) { + // mcp altitude is a multiple of 500 + score += 2; + } + } + + if (fms_valid) { + unsigned remainder = fms_alt % 500; + if (remainder < 16 || remainder > 484) { + // fms altitude is a multiple of 500 + score += 2; + } + } + + if (store) { + mm->commb_format = COMMB_VERTICAL_INTENT; + + mm->intent.valid = 1; + + if (mcp_valid) { + mm->intent.mcp_altitude_valid = 1; + mm->intent.mcp_altitude = mcp_alt; + } + + if (fms_valid) { + mm->intent.fms_altitude_valid = 1; + mm->intent.fms_altitude = fms_alt; + } + + if (baro_valid) { + mm->intent.alt_setting_valid = 1; + mm->intent.alt_setting = baro_setting; + } + + if (mode_valid) { + mm->intent.mode_vnav = (mode_raw & 4) ? 1 : 0; + mm->intent.mode_alt_hold = (mode_raw & 2) ? 1 : 0; + mm->intent.mode_approach = (mode_raw & 1) ? 1 : 0; + } + + if (source_valid) { + switch (source_raw) { + case 0: + mm->intent.altitude_source = TARGET_UNKNOWN; + break; + case 1: + mm->intent.altitude_source = TARGET_AIRCRAFT; + break; + case 2: + mm->intent.altitude_source = TARGET_MCP; + break; + case 3: + mm->intent.altitude_source = TARGET_FMS; + break; + default: + mm->intent.altitude_source = TARGET_INVALID; + break; + } + } else { + mm->intent.altitude_source = TARGET_INVALID; + } + } + + return score; +} + +// BDS5,0 Track and turn report +static int decodeBDS50(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + unsigned roll_valid = getbit(msg, 1); + unsigned roll_sign = getbit(msg, 2); + unsigned roll_raw = getbits(msg, 3, 11); + + unsigned track_valid = getbit(msg, 12); + unsigned track_sign = getbit(msg, 13); + unsigned track_raw = getbits(msg, 14, 23); + + unsigned gs_valid = getbit(msg, 24); + unsigned gs_raw = getbits(msg, 25, 34); + + unsigned track_rate_valid = getbit(msg, 35); + unsigned track_rate_sign = getbit(msg, 36); + unsigned track_rate_raw = getbits(msg, 37, 45); + + unsigned tas_valid = getbit(msg, 46); + unsigned tas_raw = getbits(msg, 47, 56); + + if (!roll_valid && !track_valid && !gs_valid && !track_rate_valid && !tas_valid) { + return 0; + } + + int score = 0; + + float roll = 0; + if (roll_valid) { + roll = roll_raw * 45.0 / 256.0; + if (roll_sign) { + roll -= 90.0; + } + + if (roll >= -40 && roll < 40) { + score += 11; + } else { + score -= 5; + } + } else if (!roll_valid && roll_raw == 0 && !roll_sign) { + score += 1; + } else { + score -= 110; + } + + float track = 0; + if (track_valid) { + score += 12; + track = track_raw * 90.0 / 512.0; + if (track_sign) { + track += 180.0; + } + } else if (!track_valid && track_raw == 0 && !track_sign) { + score += 1; + } else { + score -= 120; + } + + unsigned gs = 0; + if (gs_valid && gs_raw != 0) { + gs = gs_raw * 2; + + if (gs >= 50 && gs <= 700) { + score += 11; + } else { + score -= 5; + } + } else if (!gs_valid && gs_raw == 0) { + score += 1; + } else { + score -= 110; + } + + float track_rate = 0; + if (track_rate_valid) { + track_rate = track_rate_raw * 8.0 / 256.0; + if (track_rate_sign) { + track_rate -= 16; + } + + if (track_rate >= -10.0 && track_rate <= 10.0) { + score += 11; + } else { + score -= 5; + } + } else if (!track_rate_valid && track_rate_raw == 0 && !track_rate_sign) { + score += 1; + } else { + score -= 110; + } + + unsigned tas = 0; + if (tas_valid && tas_raw != 0) { + tas = tas_raw * 2; + + if (tas >= 50 && tas <= 700) { + score += 11; + } else { + score -= 5; + } + } else if (!tas_valid && tas_raw == 0) { + score += 1; + } else { + score -= 110; + } + + // small bonuses for consistent data + if (gs_valid && tas_valid) { + int delta = abs((int)gs_valid - (int)tas_valid); + if (delta < 50) { + score += 5; + } else if (delta > 150) { + score -= 5; + } + } + + // compute the theoretical turn rate and compare to track angle rate + if (roll_valid && tas_valid && tas > 0 && track_rate_valid) { + double turn_rate = 68625 * tan(roll * M_PI / 180.0) / (tas * 20 * M_PI); + double delta = fabs(turn_rate - track_rate); + if (delta < 0.5) { + score += 5; + } else if (delta > 2.0) { + score -= 5; + } + } + + if (store) { + mm->commb_format = COMMB_TRACK_TURN; + + if (roll_valid) { + mm->roll_valid = 1; + mm->roll = roll; + } + + if (track_valid) { + mm->track_valid = 1; + mm->track = track; + } + + if (gs_valid) { + mm->gs_valid = 1; + mm->gs = gs; + } + + if (track_rate_valid) { + mm->track_rate_valid = 1; + mm->track_rate = track_rate; + } + + if (tas_valid) { + mm->tas_valid = 1; + mm->tas = tas; + } + } + + return score; +} + +// BDS6,0 Heading and speed report +static int decodeBDS60(struct modesMessage *mm, bool store) +{ + unsigned char *msg = mm->MB; + + unsigned heading_valid = getbit(msg, 1); + unsigned heading_sign = getbit(msg, 2); + unsigned heading_raw = getbits(msg, 3, 12); + + unsigned ias_valid = getbit(msg, 13); + unsigned ias_raw = getbits(msg, 14, 23); + + unsigned mach_valid = getbit(msg, 24); + unsigned mach_raw = getbits(msg, 25, 34); + + unsigned baro_rate_valid = getbit(msg, 35); + unsigned baro_rate_sign = getbit(msg, 36); + unsigned baro_rate_raw = getbits(msg, 37, 45); + + unsigned inertial_rate_valid = getbit(msg, 46); + unsigned inertial_rate_sign = getbit(msg, 47); + unsigned inertial_rate_raw = getbits(msg, 48, 56); + + if (!heading_valid && !ias_valid && !mach_valid && !baro_rate_valid && !inertial_rate_valid) { + return 0; + } + + int score = 0; + + float heading = 0; + if (heading_valid) { + heading = heading_raw * 90.0 / 512.0; + if (heading_sign) { + heading += 180.0; + } + score += 12; + } else if (!heading_valid && heading_raw == 0 && !heading_sign) { + score += 1; + } else { + score -= 120; + } + + unsigned ias = 0; + if (ias_valid && ias_raw != 0) { + ias = ias_raw; + if (ias >= 50 && ias <= 700) { + score += 11; + } else { + score -= 5; + } + } else if (!ias_valid && ias_raw == 0) { + score += 1; + } else { + score -= 110; + } + + float mach = 0; + if (mach_valid && mach_raw != 0) { + mach = mach_raw * 2.048 / 512; + if (mach >= 0.1 && mach <= 0.9) { + score += 11; + } else { + score -= 5; + } + } else if (!mach_valid && mach_raw == 0) { + score += 1; + } else { + score -= 110; + } + + int baro_rate = 0; + if (baro_rate_valid) { + baro_rate = baro_rate_raw * 32; + if (baro_rate_sign) { + baro_rate -= 16384; + } + + if (baro_rate >= -6000 && baro_rate <= 6000) { + score += 11; + } else { + score -= 5; + } + } else if (!baro_rate_valid && baro_rate_raw == 0) { + score += 1; + } else { + score -= 110; + } + + int inertial_rate = 0; + if (inertial_rate_valid) { + inertial_rate = inertial_rate_raw * 32; + if (inertial_rate_sign) { + inertial_rate -= 16384; + } + + if (inertial_rate >= -6000 && inertial_rate <= 6000) { + score += 11; + } else { + score -= 5; + } + } else if (!inertial_rate_valid && inertial_rate_raw == 0) { + score += 1; + } else { + score -= 110; + } + + // small bonuses for consistent data + if (ias_valid && mach_valid) { + double delta = fabs(ias / 666.0 - mach); + if (delta < 0.1) { + score += 5; + } else if (delta > 0.25) { + score -= 5; + } + } + + if (baro_rate_valid && inertial_rate_valid) { + int delta = abs(baro_rate - inertial_rate); + if (delta < 500) { + score += 5; + } else if (delta > 200) { + score -= 5; + } + } + + if (store) { + mm->commb_format = COMMB_HEADING_SPEED; + + if (heading_valid) { + mm->mag_heading_valid = 1; + mm->mag_heading = heading; + } + + if (ias_valid) { + mm->ias_valid = 1; + mm->ias = ias; + } + + if (mach_valid) { + mm->mach_valid = 1; + mm->mach = mach; + } + + if (baro_rate_valid) { + mm->baro_rate_valid = 1; + mm->baro_rate = baro_rate; + } + + if (inertial_rate_valid) { + // INS-derived data is treated as a "geometric rate" / "geometric altitude" + // elsewhere, so do the same here. + mm->geom_rate_valid = 1; + mm->geom_rate = inertial_rate; + } + } + + return score; +} diff --git a/comm_b.h b/comm_b.h new file mode 100644 index 0000000..e7cc888 --- /dev/null +++ b/comm_b.h @@ -0,0 +1,26 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// comm_b.h: Comm-B message decoding (prototypes) +// +// Copyright (c) 2017 FlightAware, LLC +// Copyright (c) 2017 Oliver Jowett +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 . + +#ifndef COMM_B_H +#define COMM_B_H + +void decodeCommB(struct modesMessage *mm); + +#endif diff --git a/dump1090.h b/dump1090.h index 07c2247..d74c547 100644 --- a/dump1090.h +++ b/dump1090.h @@ -167,7 +167,7 @@ typedef enum { typedef enum { ALTITUDE_BARO, - ALTITUDE_GNSS + ALTITUDE_GEOM } altitude_source_t; typedef enum { @@ -177,12 +177,6 @@ typedef enum { AG_UNCERTAIN } airground_t; -typedef enum { - SPEED_GROUNDSPEED, - SPEED_IAS, - SPEED_TAS -} speed_source_t; - typedef enum { HEADING_TRUE, HEADING_MAGNETIC @@ -196,6 +190,18 @@ typedef enum { CPR_SURFACE, CPR_AIRBORNE, CPR_COARSE } cpr_type_t; +typedef enum { + COMMB_UNKNOWN, + COMMB_EMPTY_RESPONSE, + COMMB_DATALINK_CAPS, + COMMB_GICB_CAPS, + COMMB_AIRCRAFT_IDENT, + COMMB_ACAS_RA, + COMMB_VERTICAL_INTENT, + COMMB_TRACK_TURN, + COMMB_HEADING_SPEED +} commb_format_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -406,19 +412,24 @@ struct modesMessage { // Decoded data unsigned altitude_valid : 1; - unsigned heading_valid : 1; - unsigned speed_valid : 1; - unsigned vert_rate_valid : 1; + unsigned track_valid : 1; + unsigned track_rate_valid : 1; + unsigned mag_heading_valid : 1; + unsigned roll_valid : 1; + unsigned gs_valid : 1; + unsigned ias_valid : 1; + unsigned tas_valid : 1; + unsigned mach_valid : 1; + unsigned baro_rate_valid : 1; + unsigned geom_rate_valid : 1; unsigned squawk_valid : 1; unsigned callsign_valid : 1; - unsigned ew_velocity_valid : 1; - unsigned ns_velocity_valid : 1; unsigned cpr_valid : 1; unsigned cpr_odd : 1; unsigned cpr_decoded : 1; unsigned cpr_relative : 1; unsigned category_valid : 1; - unsigned gnss_delta_valid : 1; + unsigned geom_delta_valid : 1; unsigned from_mlat : 1; unsigned from_tisb : 1; unsigned spi_valid : 1; @@ -429,26 +440,27 @@ struct modesMessage { unsigned metype; // DF17/18 ME type unsigned mesub; // DF17/18 ME subtype + commb_format_t commb_format; // Inferred format of a comm-b message + // valid if altitude_valid: int altitude; // Altitude in either feet or meters altitude_unit_t altitude_unit; // the unit used for altitude - altitude_source_t altitude_source; // whether the altitude is a barometric altude or a GNSS height - // valid if gnss_delta_valid: - int gnss_delta; // difference between GNSS and baro alt - // valid if heading_valid: - unsigned heading; // Reported by aircraft, or computed from from EW and NS velocity - heading_source_t heading_source; // what "heading" is measuring (true or magnetic heading) - // valid if speed_valid: - unsigned speed; // in kts, reported by aircraft, or computed from from EW and NS velocity - speed_source_t speed_source; // what "speed" is measuring (groundspeed / IAS / TAS) - // valid if vert_rate_valid: - int vert_rate; // vertical rate in feet/minute - altitude_source_t vert_rate_source; // the altitude source used for vert_rate - // valid if squawk_valid: - unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits - // valid if callsign_valid - char callsign[9]; // 8 chars flight number - // valid if category_valid + altitude_source_t altitude_source; // whether the altitude is a barometric altitude or a geometric height + + // following fields are valid if the corresponding _valid field is set: + int geom_delta; // Difference between geometric and baro alt + float track; // True ground track, degrees (0-359). Reported directly or computed from from EW and NS velocity + float track_rate; // Rate of change of track, degrees/second + float mag_heading; // Magnetic heading, degrees (0-359) + float roll; // Roll, degrees, negative is left roll + unsigned gs; // Groundspeed, kts, reported directly or computed from from EW and NS velocity + unsigned ias; // Indicated airspeed, kts + unsigned tas; // True airspeed, kts + double mach; // Mach number + int baro_rate; // Rate of change of barometric altitude, feet/minute + int geom_rate; // Rate of change of geometric (GNSS / INS) altitude, feet/minute + unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits + char callsign[9]; // 8 chars flight number, NUL-terminated unsigned category; // A0 - D7 encoded as a single hex byte // valid if cpr_valid cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B) @@ -500,32 +512,37 @@ struct modesMessage { unsigned cc_antenna_offset; } opstatus; - // Target State & Status (ADS-B V2 only) + // combined: + // Target State & Status (ADS-B V2 only) + // Comm-B BDS4,0 Vertical Intent struct { unsigned valid : 1; - unsigned altitude_valid : 1; - unsigned baro_valid : 1; - unsigned heading_valid : 1; - unsigned mode_valid : 1; - unsigned mode_autopilot : 1; - unsigned mode_vnav : 1; - unsigned mode_alt_hold : 1; - unsigned mode_approach : 1; - unsigned acas_operational : 1; - unsigned nac_p : 4; - unsigned nic_baro : 1; - unsigned sil : 2; - sil_type_t sil_type; - enum { TSS_ALTITUDE_MCP, TSS_ALTITUDE_FMS } altitude_type; - unsigned altitude; - float baro; - unsigned heading; - } tss; + unsigned heading_valid : 1; + unsigned fms_altitude_valid : 1; + unsigned mcp_altitude_valid : 1; + unsigned alt_setting_valid : 1; + + float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended) + unsigned fms_altitude; // FMS selected altitude + unsigned mcp_altitude; // MCP/FCU selected altitude + float alt_setting; // altimeter setting (QFE or QNH/QNE), millibars + + enum { TARGET_INVALID, TARGET_UNKNOWN, TARGET_AIRCRAFT, TARGET_MCP, TARGET_FMS } altitude_source; + + unsigned mode_autopilot : 1; // Autopilot engaged + unsigned mode_vnav : 1; // Vertical Navigation Mode active + unsigned mode_alt_hold : 1; // Altitude Hold Mode active + unsigned mode_approach : 1; // Approach Mode active + unsigned mode_lnav : 1; // Lateral Navigation Mode active + + } intent; }; // This one needs modesMessage: #include "track.h" +#include "mode_s.h" +#include "comm_b.h" // ======================== function declarations ========================= @@ -542,14 +559,6 @@ void modeACInit(); int modeAToModeC (unsigned int modeA); unsigned modeCToModeA (int modeC); -// -// Functions exported from mode_s.c -// -int modesMessageLenByType(int type); -int scoreModesMessage(unsigned char *msg, int validbits); -int decodeModesMessage (struct modesMessage *mm, unsigned char *msg); -void displayModesMessage(struct modesMessage *mm); -void useModesMessage (struct modesMessage *mm); // // Functions exported from interactive.c // diff --git a/interactive.c b/interactive.c index 85554b2..09fcd1b 100644 --- a/interactive.c +++ b/interactive.c @@ -130,12 +130,12 @@ void interactiveShowData(void) { snprintf(strSquawk,5,"%04x", a->squawk); } - if (trackDataValid(&a->speed_valid)) { - snprintf (strGs, 5,"%3d", convert_speed(a->speed)); + if (trackDataValid(&a->gs_valid)) { + snprintf (strGs, 5,"%3d", convert_speed(a->gs)); } - if (trackDataValid(&a->heading_valid)) { - snprintf (strTt, 5,"%03d", a->heading); + if (trackDataValid(&a->track_valid)) { + snprintf (strTt, 5,"%03.0f", a->track); } if (msgs > 99999) { @@ -160,8 +160,8 @@ void interactiveShowData(void) { if (trackDataValid(&a->airground_valid) && a->airground == AG_GROUND) { snprintf(strFl, 7," grnd"); - } else if (Modes.use_gnss && trackDataValid(&a->altitude_gnss_valid)) { - snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_gnss)); + } else if (Modes.use_gnss && trackDataValid(&a->altitude_geom_valid)) { + snprintf(strFl, 7, "%5dH", convert_altitude(a->altitude_geom)); } else if (trackDataValid(&a->altitude_valid)) { snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude)); } diff --git a/mode_s.c b/mode_s.c index be41840..f14f0f1 100644 --- a/mode_s.c +++ b/mode_s.c @@ -53,8 +53,6 @@ /* for PRIX64 */ #include -#include - // // ===================== Mode S detection and decoding =================== // @@ -220,67 +218,6 @@ static int correct_aa_field(uint32_t *addr, struct errorinfo *ei) return addr_errors; } -// The first bit (MSB of the first byte) is numbered 1, for consistency -// with how the specs number them. - -// Extract one bit from a message. -static inline __attribute__((always_inline)) unsigned getbit(unsigned char *data, unsigned bitnum) -{ - unsigned bi = bitnum - 1; - unsigned by = bi >> 3; - unsigned mask = 1 << (7 - (bi & 7)); - - return (data[by] & mask) != 0; -} - -// Extract some bits (firstbit .. lastbit inclusive) from a message. -static inline __attribute__((always_inline)) unsigned getbits(unsigned char *data, unsigned firstbit, unsigned lastbit) -{ - unsigned fbi = firstbit - 1; - unsigned lbi = lastbit - 1; - unsigned nbi = (lastbit - firstbit + 1); - - unsigned fby = fbi >> 3; - unsigned lby = lbi >> 3; - unsigned nby = (lby - fby) + 1; - - unsigned shift = 7 - (lbi & 7); - unsigned topmask = 0xFF >> (fbi & 7); - - assert (fbi <= lbi); - assert (nbi <= 32); - assert (nby <= 5); - - if (nby == 5) { - return - ((data[fby] & topmask) << (32 - shift)) | - (data[fby + 1] << (24 - shift)) | - (data[fby + 2] << (16 - shift)) | - (data[fby + 3] << (8 - shift)) | - (data[fby + 4] >> shift); - } else if (nby == 4) { - return - ((data[fby] & topmask) << (24 - shift)) | - (data[fby + 1] << (16 - shift)) | - (data[fby + 2] << (8 - shift)) | - (data[fby + 3] >> shift); - } else if (nby == 3) { - return - ((data[fby] & topmask) << (16 - shift)) | - (data[fby + 1] << (8 - shift)) | - (data[fby + 2] >> shift); - } else if (nby == 2) { - return - ((data[fby] & topmask) << (8 - shift)) | - (data[fby + 1] >> shift); - } else if (nby == 1) { - return - (data[fby] & topmask) >> shift; - } else { - return 0; - } -} - // Score how plausible this ModeS message looks. // The more positive, the more reliable the message is @@ -417,8 +354,8 @@ int scoreModesMessage(unsigned char *msg, int validbits) // static void decodeExtendedSquitter(struct modesMessage *mm); -static void decodeCommB(struct modesMessage *mm); -static char *ais_charset = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?"; + +char ais_charset[64] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?"; // return 0 if all OK // -1: message might be valid, but we couldn't validate the CRC against a known ICAO @@ -737,33 +674,6 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) return 0; } -// Decode BDS2,0 carried in Comm-B or ES -static void decodeBDS20(struct modesMessage *mm) -{ - unsigned char *msg = mm->msg; - - mm->callsign[0] = ais_charset[getbits(msg, 41, 46)]; - mm->callsign[1] = ais_charset[getbits(msg, 47, 52)]; - mm->callsign[2] = ais_charset[getbits(msg, 53, 58)]; - mm->callsign[3] = ais_charset[getbits(msg, 59, 64)]; - mm->callsign[4] = ais_charset[getbits(msg, 65, 70)]; - mm->callsign[5] = ais_charset[getbits(msg, 71, 76)]; - mm->callsign[6] = ais_charset[getbits(msg, 77, 82)]; - mm->callsign[7] = ais_charset[getbits(msg, 83, 88)]; - mm->callsign[8] = 0; - - // Catch possible bad decodings since BDS2,0 is not - // 100% reliable: accept only alphanumeric data - mm->callsign_valid = 1; - for (int i = 0; i < 8; ++i) { - if (! ((mm->callsign[i] >= 'A' && mm->callsign[i] <= 'Z') || - (mm->callsign[i] >= '0' && mm->callsign[i] <= '9') || - mm->callsign[i] == ' ') ) { - mm->callsign_valid = 0; - break; - } - } -} static void decodeESIdentAndCategory(struct modesMessage *mm) { @@ -828,13 +738,18 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) return; unsigned vert_rate = getbits(me, 38, 46); + unsigned vert_rate_is_geom = getbit(me, 36); if (vert_rate) { - mm->vert_rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64); - mm->vert_rate_valid = 1; + int rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64); + if (vert_rate_is_geom) { + mm->geom_rate = rate; + mm->geom_rate_valid = 1; + } else { + mm->baro_rate = rate; + mm->baro_rate_valid = 1; + } } - mm->vert_rate_source = (getbit(me, 36) ? ALTITUDE_GNSS : ALTITUDE_BARO); - switch (mm->mesub) { case 1: case 2: { @@ -846,20 +761,17 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mm->mesub == 2) ? 4 : 1); // Compute velocity and angle from the two speed components - mm->speed = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); - mm->speed_valid = 1; + mm->gs = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); + mm->gs_valid = 1; - if (mm->speed) { - int heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI + 0.5); + if (mm->gs) { + float heading = atan2(ew_vel, ns_vel) * 180.0 / M_PI; // We don't want negative values but a 0-360 scale if (heading < 0) heading += 360; - mm->heading = (unsigned) heading; - mm->heading_source = HEADING_TRUE; - mm->heading_valid = 1; + mm->track = heading; + mm->track_valid = 1; } - - mm->speed_source = SPEED_GROUNDSPEED; } break; } @@ -868,15 +780,19 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) { unsigned airspeed = getbits(me, 26, 35); if (airspeed) { - mm->speed = (airspeed - 1) * (mm->mesub == 4 ? 4 : 1); - mm->speed_source = getbit(me, 25) ? SPEED_TAS : SPEED_IAS; - mm->speed_valid = 1; + unsigned speed = (airspeed - 1) * (mm->mesub == 4 ? 4 : 1); + if (getbit(me, 25)) { + mm->tas_valid = 1; + mm->tas = speed; + } else { + mm->ias_valid = 1; + mm->ias = speed; + } } if (getbit(me, 14)) { - mm->heading = getbits(me, 15, 24); - mm->heading_source = HEADING_MAGNETIC; - mm->heading_valid = 1; + mm->mag_heading_valid = 1; + mm->mag_heading = getbits(me, 15, 24) * 360.0 / 1024.0; } break; } @@ -884,8 +800,8 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) unsigned raw_delta = getbits(me, 50, 56); if (raw_delta) { - mm->gnss_delta_valid = 1; - mm->gnss_delta = (raw_delta - 1) * (getbit(me, 49) ? -25 : 25); + mm->geom_delta_valid = 1; + mm->geom_delta = (raw_delta - 1) * (getbit(me, 49) ? -25 : 25); } } @@ -907,15 +823,13 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf) unsigned movement = getbits(me, 6, 12); if (movement > 0 && movement < 125) { - mm->speed_valid = 1; - mm->speed = decodeMovementField(movement); - mm->speed_source = SPEED_GROUNDSPEED; + mm->gs_valid = 1; + mm->gs = decodeMovementField(movement); } if (getbit(me, 13)) { - mm->heading_valid = 1; - mm->heading_source = HEADING_TRUE; - mm->heading = getbits(me, 14, 20) * 360 / 128; + mm->track_valid = 1; + mm->track = getbits(me, 14, 20) * 360.0 / 128.0; } } @@ -967,7 +881,7 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) mm->altitude_valid = 1; } - mm->altitude_source = (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) ? ALTITUDE_GNSS : ALTITUDE_BARO; + mm->altitude_source = (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) ? ALTITUDE_GEOM : ALTITUDE_BARO; } } @@ -1017,45 +931,50 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) if (mm->mesub == 0) { // Target state and status, V1 // TODO: need RTCA/DO-260A } else if (mm->mesub == 1) { // Target state and status, V2 - mm->tss.valid = 1; - mm->tss.sil_type = getbit(me, 8) ? SIL_PER_SAMPLE : SIL_PER_HOUR; - mm->tss.altitude_type = getbit(me, 9) ? TSS_ALTITUDE_FMS : TSS_ALTITUDE_MCP; + mm->intent.valid = 1; + + // 8: SIL + unsigned is_fms = getbit(me, 9); unsigned alt_bits = getbits(me, 10, 20); - if (alt_bits == 0) { - mm->tss.altitude_valid = 0; - } else { - mm->tss.altitude_valid = 1; - mm->tss.altitude = (alt_bits - 1) * 32; + if (alt_bits != 0) { + if (is_fms) { + mm->intent.fms_altitude_valid = 1; + mm->intent.fms_altitude = (alt_bits - 1) * 32; + } else { + mm->intent.mcp_altitude_valid = 1; + mm->intent.mcp_altitude = (alt_bits - 1) * 32; + } } unsigned baro_bits = getbits(me, 21, 29); - if (baro_bits == 0) { - mm->tss.baro_valid = 0; - } else { - mm->tss.baro_valid = 1; - mm->tss.baro = 800.0 + (baro_bits - 1) * 0.8; + if (baro_bits != 0) { + mm->intent.alt_setting_valid = 1; + mm->intent.alt_setting = 800.0 + (baro_bits - 1) * 0.8; } - mm->tss.heading_valid = getbit(me, 30); - if (mm->tss.heading_valid) { + if (getbit(me, 30)) { + mm->intent.heading_valid = 1; // two's complement -180..+180, which is conveniently // also the same as unsigned 0..360 - mm->tss.heading = getbits(me, 31, 39) * 180 / 256; + mm->intent.heading = getbits(me, 31, 39) * 180.0 / 256.0; } - mm->tss.nac_p = getbits(me, 40, 43); - mm->tss.nic_baro = getbit(me, 44); - mm->tss.sil = getbits(me, 45, 46); - mm->tss.mode_valid = getbit(me, 47); - if (mm->tss.mode_valid) { - mm->tss.mode_autopilot = getbit(me, 48); - mm->tss.mode_vnav = getbit(me, 49); - mm->tss.mode_alt_hold = getbit(me, 50); - mm->tss.mode_approach = getbit(me, 52); + // 40-43: NACp + // 44: NICbaro + // 45-46: SIL + + if (getbit(me, 47)) { + mm->intent.mode_autopilot = getbit(me, 48); + mm->intent.mode_vnav = getbit(me, 49); + mm->intent.mode_alt_hold = getbit(me, 50); + // 51: IMF + mm->intent.mode_approach = getbit(me, 52); + // 53: TCAS operational + mm->intent.mode_lnav = getbit(me, 54); } - mm->tss.acas_operational = getbit(me, 53); + // 55-56 reserved } } @@ -1230,7 +1149,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) case 0: // Airborne position, baro altitude only case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: // Airborne position, baro - case 20: case 21: case 22: // Airborne position, GNSS altitude (HAE or MSL) + case 20: case 21: case 22: // Airborne position, geometric altitude (HAE or MSL) decodeESAirbornePosition(mm, check_imf); break; @@ -1261,16 +1180,6 @@ static void decodeExtendedSquitter(struct modesMessage *mm) } } -static void decodeCommB(struct modesMessage *mm) -{ - unsigned char *msg = mm->msg; - - // This is a bit hairy as we don't know what the requested register was - if (getbits(msg, 33, 40) == 0x20) { // BDS 2,0 Aircraft Identification - decodeBDS20(mm); - } -} - static const char *df_names[33] = { /* 0 */ "Short Air-Air Surveillance", /* 1 */ NULL, @@ -1330,8 +1239,8 @@ static const char *altitude_source_to_string(altitude_source_t source) { switch (source) { case ALTITUDE_BARO: return "barometric"; - case ALTITUDE_GNSS: - return "GNSS"; + case ALTITUDE_GEOM: + return "geometric"; default: return "(unknown altitude source)"; } @@ -1352,19 +1261,6 @@ static const char *airground_to_string(airground_t airground) { } } -static const char *speed_source_to_string(speed_source_t speed) { - switch (speed) { - case SPEED_GROUNDSPEED: - return "groundspeed"; - case SPEED_IAS: - return "IAS"; - case SPEED_TAS: - return "TAS"; - default: - return "(unknown speed type)"; - } -} - static const char *addrtype_to_string(addrtype_t type) { switch (type) { case ADDR_ADSB_ICAO: @@ -1403,6 +1299,29 @@ static const char *cpr_type_to_string(cpr_type_t type) { } } +static const char *commb_format_to_string(commb_format_t format) { + switch (format) { + case COMMB_EMPTY_RESPONSE: + return "empty response"; + case COMMB_DATALINK_CAPS: + return "BDS1,0 Datalink capabilities"; + case COMMB_GICB_CAPS: + return "BDS1,7 Common usage GICB capabilities"; + case COMMB_AIRCRAFT_IDENT: + return "BDS2,0 Aircraft identification"; + case COMMB_ACAS_RA: + return "BDS3,0 ACAS resolution advisory"; + case COMMB_VERTICAL_INTENT: + return "BDS4,0 Selected vertical intention"; + case COMMB_TRACK_TURN: + return "BDS5,0 Track and turn report"; + case COMMB_HEADING_SPEED: + return "BDS6,0 Heading and speed report"; + default: + return "unknown format"; + } +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -1455,7 +1374,7 @@ static const char *esTypeName(unsigned metype, unsigned mesub) } case 20: case 21: case 22: - return "Airborne position (GNSS altitude)"; + return "Airborne position (geometric altitude)"; case 23: switch (mesub) { @@ -1639,6 +1558,10 @@ void displayModesMessage(struct modesMessage *mm) { } printf("\n"); + if (mm->msgtype == 20 || mm->msgtype == 21) { + printf(" Comm-B format: %s\n", commb_format_to_string(mm->commb_format)); + } + if (mm->addr & MODES_NON_ICAO_ADDRESS) { printf(" Other Address: %06X (%s)\n", mm->addr & 0xFFFFFF, addrtype_to_string(mm->addrtype)); } else { @@ -1657,25 +1580,49 @@ void displayModesMessage(struct modesMessage *mm) { altitude_source_to_string(mm->altitude_source)); } - if (mm->gnss_delta_valid) { - printf(" GNSS delta: %d ft\n", - mm->gnss_delta); + if (mm->geom_delta_valid) { + printf(" Geom - baro: %d ft\n", + mm->geom_delta); } - if (mm->heading_valid) { - printf(" Heading: %u\n", mm->heading); + if (mm->track_valid) { + printf(" Track: %.1f\n", mm->track); } - if (mm->speed_valid) { - printf(" Speed: %u kt %s\n", - mm->speed, - speed_source_to_string(mm->speed_source)); + if (mm->mag_heading_valid) { + printf(" Mag heading: %.1f\n", mm->mag_heading); } - if (mm->vert_rate_valid) { - printf(" Vertical rate: %d ft/min %s\n", - mm->vert_rate, - altitude_source_to_string(mm->vert_rate_source)); + if (mm->track_rate_valid) { + printf(" Track rate: %.2f deg/sec %s\n", mm->track_rate, mm->track_rate < 0 ? "left" : mm->track_rate > 0 ? "right" : ""); + } + + if (mm->roll_valid) { + printf(" Roll: %.1f degrees %s\n", mm->roll, mm->roll < -0.05 ? "left" : mm->roll > 0.05 ? "right" : ""); + } + + if (mm->gs_valid) { + printf(" Groundspeed: %u kt\n", mm->gs); + } + + if (mm->ias_valid) { + printf(" IAS: %u kt\n", mm->ias); + } + + if (mm->tas_valid) { + printf(" TAS: %u kt\n", mm->tas); + } + + if (mm->mach_valid) { + printf(" Mach number: %.3f\n", mm->mach); + } + + if (mm->baro_rate_valid) { + printf(" Baro rate: %d ft/min\n", mm->baro_rate); + } + + if (mm->geom_rate_valid) { + printf(" Geom rate: %d ft/min\n", mm->geom_rate); } if (mm->squawk_valid) { @@ -1758,26 +1705,47 @@ void displayModesMessage(struct modesMessage *mm) { printf(" Heading reference: %s\n", (mm->opstatus.hrd == HEADING_TRUE ? "true north" : "magnetic north")); } - if (mm->tss.valid) { - printf(" Target State and Status:\n"); - if (mm->tss.altitude_valid) - printf(" Target altitude: %s, %d ft\n", (mm->tss.altitude_type == TSS_ALTITUDE_MCP ? "MCP" : "FMS"), mm->tss.altitude); - if (mm->tss.baro_valid) - printf(" Altimeter setting: %.1f millibars\n", mm->tss.baro); - if (mm->tss.heading_valid) - printf(" Target heading: %d\n", mm->tss.heading); - if (mm->tss.mode_valid) { - printf(" Active modes: "); - if (mm->tss.mode_autopilot) printf("autopilot "); - if (mm->tss.mode_vnav) printf("VNAV "); - if (mm->tss.mode_alt_hold) printf("altitude-hold "); - if (mm->tss.mode_approach) printf("approach "); + if (mm->intent.valid) { + printf(" Intent:\n"); + if (mm->intent.heading_valid) + printf(" Selected heading: %.1f\n", mm->intent.heading); + if (mm->intent.fms_altitude_valid) + printf(" FMS selected altitude: %u ft\n", mm->intent.fms_altitude); + if (mm->intent.mcp_altitude_valid) + printf(" MCP selected altitude: %u ft\n", mm->intent.mcp_altitude); + if (mm->intent.alt_setting_valid) + printf(" Altimeter setting: %.1f millibars\n", mm->intent.alt_setting); + + if (mm->intent.altitude_source != TARGET_INVALID) { + printf(" Target altitude source: "); + switch (mm->intent.altitude_source) { + case TARGET_AIRCRAFT: + printf("aircraft altitude\n"); + break; + case TARGET_MCP: + printf("MCP selected altitude\n"); + break; + case TARGET_FMS: + printf("FMS selected altitude\n"); + break; + default: + printf("unknown\n"); + } + } + + if (mm->intent.mode_autopilot || + mm->intent.mode_vnav || + mm->intent.mode_alt_hold || + mm->intent.mode_approach || + mm->intent.mode_lnav) { + printf(" Active modes: "); + if (mm->intent.mode_autopilot) printf("autopilot "); + if (mm->intent.mode_vnav) printf("VNAV "); + if (mm->intent.mode_alt_hold) printf("altitude-hold "); + if (mm->intent.mode_approach) printf("approach "); + if (mm->intent.mode_lnav) printf("LNAV "); printf("\n"); } - printf(" ACAS: %s\n", mm->tss.acas_operational ? "operational" : "NOT operational"); - printf(" NACp: %d\n", mm->tss.nac_p); - printf(" NICbaro: %d\n", mm->tss.nic_baro); - printf(" SIL: %d (%s)\n", mm->tss.sil, (mm->opstatus.sil_type == SIL_PER_HOUR ? "per hour" : "per sample")); } printf("\n"); diff --git a/mode_s.h b/mode_s.h new file mode 100644 index 0000000..4fbc983 --- /dev/null +++ b/mode_s.h @@ -0,0 +1,100 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// mode_s.h: Mode S message decoding (prototypes) +// +// Copyright (c) 2017 FlightAware, LLC +// Copyright (c) 2017 Oliver Jowett +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 . + +#ifndef MODE_S_H +#define MODE_S_H + +#include + +// +// Functions exported from mode_s.c +// +int modesMessageLenByType(int type); +int scoreModesMessage(unsigned char *msg, int validbits); +int decodeModesMessage (struct modesMessage *mm, unsigned char *msg); +void displayModesMessage(struct modesMessage *mm); +void useModesMessage (struct modesMessage *mm); + +// datafield extraction helpers + +// The first bit (MSB of the first byte) is numbered 1, for consistency +// with how the specs number them. + +// Extract one bit from a message. +static inline __attribute__((always_inline)) unsigned getbit(unsigned char *data, unsigned bitnum) +{ + unsigned bi = bitnum - 1; + unsigned by = bi >> 3; + unsigned mask = 1 << (7 - (bi & 7)); + + return (data[by] & mask) != 0; +} + +// Extract some bits (firstbit .. lastbit inclusive) from a message. +static inline __attribute__((always_inline)) unsigned getbits(unsigned char *data, unsigned firstbit, unsigned lastbit) +{ + unsigned fbi = firstbit - 1; + unsigned lbi = lastbit - 1; + unsigned nbi = (lastbit - firstbit + 1); + + unsigned fby = fbi >> 3; + unsigned lby = lbi >> 3; + unsigned nby = (lby - fby) + 1; + + unsigned shift = 7 - (lbi & 7); + unsigned topmask = 0xFF >> (fbi & 7); + + assert (fbi <= lbi); + assert (nbi <= 32); + assert (nby <= 5); + + if (nby == 5) { + return + ((data[fby] & topmask) << (32 - shift)) | + (data[fby + 1] << (24 - shift)) | + (data[fby + 2] << (16 - shift)) | + (data[fby + 3] << (8 - shift)) | + (data[fby + 4] >> shift); + } else if (nby == 4) { + return + ((data[fby] & topmask) << (24 - shift)) | + (data[fby + 1] << (16 - shift)) | + (data[fby + 2] << (8 - shift)) | + (data[fby + 3] >> shift); + } else if (nby == 3) { + return + ((data[fby] & topmask) << (16 - shift)) | + (data[fby + 1] << (8 - shift)) | + (data[fby + 2] >> shift); + } else if (nby == 2) { + return + ((data[fby] & topmask) << (8 - shift)) | + (data[fby + 1] >> shift); + } else if (nby == 1) { + return + (data[fby] & topmask) >> shift; + } else { + return 0; + } +} + +extern char ais_charset[64]; + +#endif diff --git a/net_io.c b/net_io.c index 35e1e27..66fce2b 100644 --- a/net_io.c +++ b/net_io.c @@ -582,18 +582,18 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { // Field 12 is the altitude (if we have it) if (mm->altitude_valid) { if (Modes.use_gnss) { - if (mm->altitude_source == ALTITUDE_GNSS) { + if (mm->altitude_source == ALTITUDE_GEOM) { p += sprintf(p, ",%dH", mm->altitude); - } else if (trackDataValid(&a->gnss_delta_valid)) { - p += sprintf(p, ",%dH", mm->altitude + a->gnss_delta); + } else if (trackDataValid(&a->geom_delta_valid)) { + p += sprintf(p, ",%dH", mm->altitude + a->geom_delta); } else { p += sprintf(p, ",%d", mm->altitude); } } else { if (mm->altitude_source == ALTITUDE_BARO) { p += sprintf(p, ",%d", mm->altitude); - } else if (trackDataValid(&a->gnss_delta_valid)) { - p += sprintf(p, ",%d", mm->altitude - a->gnss_delta); + } else if (trackDataValid(&a->geom_delta_valid)) { + p += sprintf(p, ",%d", mm->altitude - a->geom_delta); } else { p += sprintf(p, ","); } @@ -603,15 +603,15 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { } // Field 13 is the ground Speed (if we have it) - if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED) { - p += sprintf(p, ",%d", mm->speed); + if (mm->gs_valid) { + p += sprintf(p, ",%d", mm->gs); } else { p += sprintf(p, ","); } - // Field 14 is the ground Heading (if we have it) - if (mm->heading_valid && mm->heading_source == HEADING_TRUE) { - p += sprintf(p, ",%d", mm->heading); + // Field 14 is the ground Heading (if we have it) + if (mm->track_valid) { + p += sprintf(p, ",%.0f", mm->track); } else { p += sprintf(p, ","); } @@ -624,10 +624,22 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { } // Field 17 is the VerticalRate (if we have it) - if (mm->vert_rate_valid) { - p += sprintf(p, ",%d", mm->vert_rate); + if (Modes.use_gnss) { + if (mm->geom_rate_valid) { + p += sprintf(p, ",%dH", mm->geom_rate); + } else if (mm->baro_rate_valid) { + p += sprintf(p, ",%d", mm->baro_rate); + } else { + p += sprintf(p, ","); + } } else { - p += sprintf(p, ","); + if (mm->baro_rate_valid) { + p += sprintf(p, ",%d", mm->baro_rate); + } else if (mm->geom_rate_valid) { + p += sprintf(p, ",%d", mm->geom_rate); + } else { + p += sprintf(p, ","); + } } // Field 18 is the Squawk (if we have it) @@ -1099,12 +1111,14 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s p += snprintf(p, end-p, "\"lat\",\"lon\","); if (a->altitude_valid.source == source) p += snprintf(p, end-p, "\"altitude\","); - if (a->heading_valid.source == source) + if (a->track_valid.source == source) p += snprintf(p, end-p, "\"track\","); - if (a->speed_valid.source == source) + if (a->gs_valid.source == source) p += snprintf(p, end-p, "\"speed\","); - if (a->vert_rate_valid.source == source) - p += snprintf(p, end-p, "\"vert_rate\","); + if (a->baro_rate_valid.source == source) + p += snprintf(p, end-p, "\"baro_rate\","); + if (a->geom_rate_valid.source == source) + p += snprintf(p, end-p, "\"geom_rate\","); if (a->category_valid.source == source) p += snprintf(p, end-p, "\"category\","); if (p[-1] != '[') @@ -1173,14 +1187,30 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->position_valid.updated)/1000.0); if (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND) p += snprintf(p, end-p, ",\"altitude\":\"ground\""); - else if (trackDataValid(&a->altitude_valid)) - p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude); - if (trackDataValid(&a->vert_rate_valid)) - p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate); - if (trackDataValid(&a->heading_valid)) - p += snprintf(p, end-p, ",\"track\":%d", a->heading); - if (trackDataValid(&a->speed_valid)) - p += snprintf(p, end-p, ",\"speed\":%d", a->speed); + else { + if (trackDataValid(&a->altitude_valid)) + p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude); + if (trackDataValid(&a->altitude_geom_valid)) + p += snprintf(p, end-p, ",\"altitude_geom\":%d", a->altitude_geom); + } + if (trackDataValid(&a->baro_rate_valid)) + p += snprintf(p, end-p, ",\"baro_rate\":%d", a->baro_rate); + if (trackDataValid(&a->geom_rate_valid)) + p += snprintf(p, end-p, ",\"geom_rate\":%d", a->geom_rate); + if (trackDataValid(&a->track_valid)) + p += snprintf(p, end-p, ",\"track\":%.1f", a->track); + if (trackDataValid(&a->track_rate_valid)) + p += snprintf(p, end-p, ",\"track_rate\":%.2f", a->track_rate); + if (trackDataValid(&a->gs_valid)) + p += snprintf(p, end-p, ",\"gs\":%u", a->gs); + if (trackDataValid(&a->ias_valid)) + p += snprintf(p, end-p, ",\"ias\":%u", a->ias); + if (trackDataValid(&a->tas_valid)) + p += snprintf(p, end-p, ",\"tas\":%u", a->tas); + if (trackDataValid(&a->mach_valid)) + p += snprintf(p, end-p, ",\"mach\":%.3f", a->mach); + if (trackDataValid(&a->roll_valid)) + p += snprintf(p, end-p, ",\"roll\":%.1f", a->roll); if (trackDataValid(&a->category_valid)) p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category); @@ -1662,7 +1692,7 @@ static void modesReadFromClient(struct client *c) { } } -#define TSV_MAX_PACKET_SIZE 275 +#define TSV_MAX_PACKET_SIZE 400 static void writeFATSVPositionUpdate(float lat, float lon, float alt) { @@ -1747,23 +1777,28 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) switch (mm->msgtype) { case 20: case 21: - if (mm->correctedbits > 0) - break; // only messages we trust a little more - // DF 20/21: Comm-B: emit if they've changed since we last sent them - // - // BDS 1,0: data link capability report - // BDS 3,0: ACAS RA report - if (mm->MB[0] == 0x10 && memcmp(mm->MB, a->fatsv_emitted_bds_10, 7) != 0) { - memcpy(a->fatsv_emitted_bds_10, mm->MB, 7); - writeFATSVEventMessage(mm, "datalink_caps", mm->MB, 7); - } + switch (mm->commb_format) { + case COMMB_DATALINK_CAPS: + // BDS 1,0: data link capability report + if (memcmp(mm->MB, a->fatsv_emitted_bds_10, 7) != 0) { + memcpy(a->fatsv_emitted_bds_10, mm->MB, 7); + writeFATSVEventMessage(mm, "datalink_caps", mm->MB, 7); + } + break; - else if (mm->MB[0] == 0x30 && memcmp(mm->MB, a->fatsv_emitted_bds_30, 7) != 0) { - memcpy(a->fatsv_emitted_bds_30, mm->MB, 7); - writeFATSVEventMessage(mm, "commb_acas_ra", mm->MB, 7); - } + case COMMB_ACAS_RA: + // BDS 3,0: ACAS RA report + if (memcmp(mm->MB, a->fatsv_emitted_bds_30, 7) != 0) { + memcpy(a->fatsv_emitted_bds_30, mm->MB, 7); + writeFATSVEventMessage(mm, "commb_acas_ra", mm->MB, 7); + } + break; + default: + // nothing + break; + } break; case 17: @@ -1778,10 +1813,6 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) // aircraft operational status memcpy(a->fatsv_emitted_es_status, mm->ME, 7); writeFATSVEventMessage(mm, "es_op_status", mm->ME, 7); - } else if (mm->metype == 29 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_target, 7) != 0) { - // target state and status - memcpy(a->fatsv_emitted_es_target, mm->ME, 7); - writeFATSVEventMessage(mm, "es_target", mm->ME, 7); } break; } @@ -1791,13 +1822,13 @@ typedef enum { TISB_IDENT = 1, TISB_SQUAWK = 2, TISB_ALTITUDE = 4, - TISB_ALTITUDE_GNSS = 8, - TISB_SPEED = 16, - TISB_SPEED_IAS = 32, - TISB_SPEED_TAS = 64, + TISB_ALTITUDE_GEOM = 8, + TISB_GS = 16, + TISB_IAS = 32, + TISB_TAS = 64, TISB_POSITION = 128, - TISB_HEADING = 256, - TISB_HEADING_MAGNETIC = 512, + TISB_TRACK = 256, + TISB_MAG_HEADING = 512, TISB_AIRGROUND = 1024, TISB_CATEGORY = 2048 } tisb_flags; @@ -1807,9 +1838,9 @@ static inline unsigned unsigned_difference(unsigned v1, unsigned v2) return (v1 > v2) ? (v1 - v2) : (v2 - v1); } -static inline unsigned heading_difference(unsigned h1, unsigned h2) +static inline float heading_difference(float h1, float h2) { - unsigned d = unsigned_difference(h1, h2); + float d = fabs(h1 - h2); return (d < 180) ? d : (360 - d); } @@ -1832,17 +1863,6 @@ static void writeFATSV() next_update = now + 1000; for (a = Modes.aircrafts; a; a = a->next) { - int altValid = 0; - int altGNSSValid = 0; - int positionValid = 0; - int speedValid = 0; - int speedIASValid = 0; - int speedTASValid = 0; - int headingValid = 0; - int headingMagValid = 0; - int airgroundValid = 0; - int categoryValid = 0; - uint64_t minAge; int useful = 0; @@ -1859,16 +1879,25 @@ static void writeFATSV() continue; } - altValid = trackDataValidEx(&a->altitude_valid, now, 15000, SOURCE_MODE_S); // for non-ADS-B transponders, DF0/4/16/20 are the only sources of altitude data - altGNSSValid = trackDataValidEx(&a->altitude_gnss_valid, now, 15000, SOURCE_MODE_S_CHECKED); - airgroundValid = trackDataValidEx(&a->airground_valid, now, 15000, SOURCE_MODE_S_CHECKED); // for non-ADS-B transponders, only trust DF11 CA field - positionValid = trackDataValidEx(&a->position_valid, now, 15000, SOURCE_MODE_S_CHECKED); - headingValid = trackDataValidEx(&a->heading_valid, now, 15000, SOURCE_MODE_S_CHECKED); - headingMagValid = trackDataValidEx(&a->heading_magnetic_valid, now, 15000, SOURCE_MODE_S_CHECKED); - speedValid = trackDataValidEx(&a->speed_valid, now, 15000, SOURCE_MODE_S_CHECKED); - speedIASValid = trackDataValidEx(&a->speed_ias_valid, now, 15000, SOURCE_MODE_S_CHECKED); - speedTASValid = trackDataValidEx(&a->speed_tas_valid, now, 15000, SOURCE_MODE_S_CHECKED); - categoryValid = trackDataValidEx(&a->category_valid, now, 15000, SOURCE_MODE_S_CHECKED); + int altValid = trackDataValidEx(&a->altitude_valid, now, 15000, SOURCE_MODE_S); // for non-ADS-B transponders, DF0/4/16/20 are the only sources of altitude data + int altGeomValid = trackDataValidEx(&a->altitude_geom_valid, now, 15000, SOURCE_MODE_S_CHECKED); + int airgroundValid = trackDataValidEx(&a->airground_valid, now, 15000, SOURCE_MODE_S_CHECKED); // for non-ADS-B transponders, only trust DF11 CA field + int baroRateValid = trackDataValidEx(&a->baro_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int geomRateValid = trackDataValidEx(&a->geom_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int positionValid = trackDataValidEx(&a->position_valid, now, 15000, SOURCE_MODE_S_CHECKED); + int trackValid = trackDataValidEx(&a->track_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int trackRateValid = trackDataValidEx(&a->track_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B + int rollValid = trackDataValidEx(&a->roll_valid, now, 15000, SOURCE_MODE_S); // Comm-B + int magHeadingValid = trackDataValidEx(&a->mag_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int gsValid = trackDataValidEx(&a->gs_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int iasValid = trackDataValidEx(&a->ias_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int tasValid = trackDataValidEx(&a->tas_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int machValid = trackDataValidEx(&a->mach_valid, now, 15000, SOURCE_MODE_S); // Comm-B + int categoryValid = trackDataValidEx(&a->category_valid, now, 15000, SOURCE_MODE_S_CHECKED); + int intentAltValid = trackDataValidEx(&a->intent_altitude_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int intentHeadingValid = trackDataValidEx(&a->intent_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int altSettingValid = trackDataValidEx(&a->alt_setting_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int callsignValid = trackDataValidEx(&a->callsign, now, 15000, SOURCE_MODE_S); // Comm-B or ES // 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 @@ -1883,22 +1912,49 @@ static void writeFATSV() if (altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) { changed = 1; } - if (altGNSSValid && abs(a->altitude_gnss - a->fatsv_emitted_altitude_gnss) >= 50) { + if (altGeomValid && abs(a->altitude_geom - a->fatsv_emitted_altitude_gnss) >= 50) { changed = 1; } - if (headingValid && heading_difference(a->heading, a->fatsv_emitted_heading) >= 2) { + if (baroRateValid && abs(a->baro_rate - a->fatsv_emitted_baro_rate) > 500) { changed = 1; } - if (headingMagValid && heading_difference(a->heading_magnetic, a->fatsv_emitted_heading_magnetic) >= 2) { + if (geomRateValid && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) { changed = 1; } - if (speedValid && unsigned_difference(a->speed, a->fatsv_emitted_speed) >= 25) { + if (trackValid && heading_difference(a->track, a->fatsv_emitted_heading) >= 2) { changed = 1; } - if (speedIASValid && unsigned_difference(a->speed_ias, a->fatsv_emitted_speed_ias) >= 25) { + if (trackRateValid && fabs(a->track_rate - a->fatsv_emitted_track_rate) >= 0.5) { changed = 1; } - if (speedTASValid && unsigned_difference(a->speed_tas, a->fatsv_emitted_speed_tas) >= 25) { + if (rollValid && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) { + changed = 1; + } + if (magHeadingValid && heading_difference(a->mag_heading, a->fatsv_emitted_heading_magnetic) >= 2) { + changed = 1; + } + if (gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) { + changed = 1; + } + if (iasValid && unsigned_difference(a->ias, a->fatsv_emitted_speed_ias) >= 25) { + changed = 1; + } + if (tasValid && unsigned_difference(a->tas, a->fatsv_emitted_speed_tas) >= 25) { + changed = 1; + } + if (machValid && fabs(a->mach - a->fatsv_emitted_mach) >= 0.02) { + changed = 1; + } + if (intentAltValid && unsigned_difference(a->intent_altitude, a->fatsv_emitted_intent_altitude) > 50) { + changed = 1; + } + if (intentHeadingValid && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) { + changed = 1; + } + if (altSettingValid && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) { // 0.8 is the ES message resolution + changed = 1; + } + if (callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) { changed = 1; } @@ -1910,8 +1966,8 @@ static void writeFATSV() // don't send mode S very often minAge = 30000; } else if ((airgroundValid && a->airground == AG_GROUND) || - (altValid && a->altitude < 500 && (!speedValid || a->speed < 200)) || - (speedValid && a->speed < 100 && (!altValid || a->altitude < 1000))) { + (altValid && a->altitude < 500 && (!gsValid || a->gs < 200)) || + (gsValid && a->gs < 100 && (!altValid || a->altitude < 1000))) { // we are probably on the ground, increase the update rate minAge = 1000; } else if (!altValid || a->altitude < 10000) { @@ -1944,7 +2000,7 @@ static void writeFATSV() p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(a->addrtype)); } - if (trackDataValidEx(&a->callsign_valid, now, 35000, SOURCE_MODE_S_CHECKED) && strcmp(a->callsign, " ") != 0 && a->callsign_valid.updated > a->fatsv_last_emitted) { + if (trackDataValidEx(&a->callsign_valid, now, 35000, SOURCE_MODE_S) && strcmp(a->callsign, " ") != 0 && a->callsign_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tident\t%s", a->callsign); switch (a->callsign_valid.source) { case SOURCE_MODE_S: @@ -1971,7 +2027,7 @@ static void writeFATSV() tisb |= (a->squawk_valid.source == SOURCE_TISB) ? TISB_SQUAWK : 0; } - // only emit alt, speed, latlon, track if they have been received since the last time + // only emit alt, speed, latlon, track etc if they have been received since the last time // and are not stale if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) { @@ -1981,32 +2037,50 @@ static void writeFATSV() tisb |= (a->altitude_valid.source == SOURCE_TISB) ? TISB_ALTITUDE : 0; } - if (altGNSSValid && a->altitude_gnss_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\talt_gnss\t%d", a->altitude_gnss); - a->fatsv_emitted_altitude_gnss = a->altitude_gnss; + if (altGeomValid && a->altitude_geom_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\talt_geom\t%d", a->altitude_geom); + a->fatsv_emitted_altitude_gnss = a->altitude_geom; useful = 1; - tisb |= (a->altitude_gnss_valid.source == SOURCE_TISB) ? TISB_ALTITUDE_GNSS : 0; + tisb |= (a->altitude_geom_valid.source == SOURCE_TISB) ? TISB_ALTITUDE_GEOM : 0; } - if (speedValid && a->speed_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed); - a->fatsv_emitted_speed = a->speed; + if (baroRateValid && a->baro_rate_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tbaro_rate\t%d", a->baro_rate); + a->fatsv_emitted_baro_rate = a->baro_rate; useful = 1; - tisb |= (a->speed_valid.source == SOURCE_TISB) ? TISB_SPEED : 0; } - if (speedIASValid && a->speed_ias_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tspeed_ias\t%d", a->speed_ias); - a->fatsv_emitted_speed_ias = a->speed_ias; + if (geomRateValid && a->geom_rate_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tgeom_rate\t%d", a->geom_rate); + a->fatsv_emitted_geom_rate = a->geom_rate; useful = 1; - tisb |= (a->speed_ias_valid.source == SOURCE_TISB) ? TISB_SPEED_IAS : 0; } - if (speedTASValid && a->speed_tas_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tspeed_tas\t%d", a->speed_tas); - a->fatsv_emitted_speed_tas = a->speed_tas; + if (gsValid && a->gs_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tgs\t%u", a->gs); + a->fatsv_emitted_speed = a->gs; + useful = 1; + tisb |= (a->gs_valid.source == SOURCE_TISB) ? TISB_GS : 0; + } + + if (iasValid && a->ias_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tias\t%u", a->ias); + a->fatsv_emitted_speed_ias = a->ias; + useful = 1; + tisb |= (a->ias_valid.source == SOURCE_TISB) ? TISB_IAS : 0; + } + + if (tasValid && a->tas_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\ttas\t%u", a->tas); + a->fatsv_emitted_speed_tas = a->tas; + useful = 1; + tisb |= (a->tas_valid.source == SOURCE_TISB) ? TISB_TAS : 0; + } + + if (machValid && a->mach_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tmach\t%.3f", a->mach); + a->fatsv_emitted_mach = a->mach; useful = 1; - tisb |= (a->speed_tas_valid.source == SOURCE_TISB) ? TISB_SPEED_TAS : 0; } if (positionValid && a->position_valid.updated > a->fatsv_last_emitted) { @@ -2015,18 +2089,30 @@ static void writeFATSV() tisb |= (a->position_valid.source == SOURCE_TISB) ? TISB_POSITION : 0; } - if (headingValid && a->heading_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\theading\t%d", a->heading); - a->fatsv_emitted_heading = a->heading; + if (trackValid && a->track_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\ttrack\t%.0f", a->track); + a->fatsv_emitted_heading = a->track; useful = 1; - tisb |= (a->heading_valid.source == SOURCE_TISB) ? TISB_HEADING : 0; + tisb |= (a->track_valid.source == SOURCE_TISB) ? TISB_TRACK : 0; } - if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\theading_magnetic\t%d", a->heading_magnetic); - a->fatsv_emitted_heading_magnetic = a->heading_magnetic; + if (trackRateValid && a->track_rate_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\ttrack_rate\t%.2f", a->track_rate); + a->fatsv_emitted_track_rate = a->track_rate; useful = 1; - tisb |= (a->heading_magnetic_valid.source == SOURCE_TISB) ? TISB_HEADING_MAGNETIC : 0; + } + + if (rollValid && a->roll_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\troll\t%.1f", a->roll); + a->fatsv_emitted_roll = a->roll; + useful = 1; + } + + if (magHeadingValid && a->mag_heading_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tmag_heading\t%.0f", a->mag_heading); + a->fatsv_emitted_heading_magnetic = a->mag_heading; + useful = 1; + tisb |= (a->mag_heading_valid.source == SOURCE_TISB) ? TISB_MAG_HEADING : 0; } if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE) && a->airground_valid.updated > a->fatsv_last_emitted) { @@ -2043,6 +2129,25 @@ static void writeFATSV() tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_CATEGORY : 0; } + if (intentAltValid && a->intent_altitude_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tintent_alt\t%u", a->intent_altitude); + a->fatsv_emitted_intent_altitude = a->intent_altitude; + useful = 1; + } + + if (intentHeadingValid && a->intent_heading_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\tintent_heading\t%.0f", a->intent_heading); + a->fatsv_emitted_intent_heading = a->intent_heading; + useful = 1; + } + + if (altSettingValid && a->alt_setting_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\talt_setting\t%.1f", a->alt_setting); + a->fatsv_emitted_alt_setting = a->alt_setting; + useful = 1; + } + + // if we didn't get anything interesting, bail out. // We don't need to do anything special to unwind prepareWrite(). if (!useful) { diff --git a/track.c b/track.c index 24e6348..ea98ab1 100644 --- a/track.c +++ b/track.c @@ -209,12 +209,12 @@ static int speed_check(struct aircraft *a, double lat, double lon, uint64_t now, elapsed = trackDataAge(&a->position_valid, now); - if (trackDataValid(&a->speed_valid)) - speed = a->speed; - else if (trackDataValid(&a->speed_ias_valid)) - speed = a->speed_ias * 4 / 3; - else if (trackDataValid(&a->speed_tas_valid)) - speed = a->speed_tas * 4 / 3; + if (trackDataValid(&a->gs_valid)) + speed = a->gs; + else if (trackDataValid(&a->tas_valid)) + speed = a->tas * 4 / 3; + else if (trackDataValid(&a->ias_valid)) + speed = a->ias * 2; else speed = surface ? 100 : 600; // guess @@ -427,7 +427,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t ++Modes.stats_current.cpr_surface; // Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise - if (mm->speed_valid && mm->speed <= 25) + if (mm->gs_valid && mm->gs <= 25) max_elapsed = 50000; else max_elapsed = 25000; @@ -562,37 +562,52 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->squawk = mm->squawk; } - if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GNSS && accept_data(&a->altitude_gnss_valid, mm->source, now)) { - a->altitude_gnss = mm->altitude; + if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GEOM && accept_data(&a->altitude_geom_valid, mm->source, now)) { + a->altitude_geom = mm->altitude; } - if (mm->gnss_delta_valid && accept_data(&a->gnss_delta_valid, mm->source, now)) { - a->gnss_delta = mm->gnss_delta; + if (mm->geom_delta_valid && accept_data(&a->geom_delta_valid, mm->source, now)) { + a->geom_delta = mm->geom_delta; } - if (mm->heading_valid && mm->heading_source == HEADING_TRUE && accept_data(&a->heading_valid, mm->source, now)) { - a->heading = mm->heading; + if (mm->track_valid && accept_data(&a->track_valid, mm->source, now)) { + a->track = mm->track; } - if (mm->heading_valid && mm->heading_source == HEADING_MAGNETIC && accept_data(&a->heading_magnetic_valid, mm->source, now)) { - a->heading_magnetic = mm->heading; + if (mm->track_rate_valid && accept_data(&a->track_rate_valid, mm->source, now)) { + a->track_rate = mm->track_rate; } - if (mm->speed_valid && mm->speed_source == SPEED_GROUNDSPEED && accept_data(&a->speed_valid, mm->source, now)) { - a->speed = mm->speed; + if (mm->roll_valid && accept_data(&a->roll_valid, mm->source, now)) { + a->roll = mm->roll; } - if (mm->speed_valid && mm->speed_source == SPEED_IAS && accept_data(&a->speed_ias_valid, mm->source, now)) { - a->speed_ias = mm->speed; + if (mm->mag_heading_valid && accept_data(&a->mag_heading_valid, mm->source, now)) { + a->mag_heading = mm->mag_heading; } - if (mm->speed_valid && mm->speed_source == SPEED_TAS && accept_data(&a->speed_tas_valid, mm->source, now)) { - a->speed_tas = mm->speed; + if (mm->gs_valid && accept_data(&a->gs_valid, mm->source, now)) { + a->gs = mm->gs; } - if (mm->vert_rate_valid && accept_data(&a->vert_rate_valid, mm->source, now)) { - a->vert_rate = mm->vert_rate; - a->vert_rate_source = mm->vert_rate_source; + if (mm->ias_valid && accept_data(&a->ias_valid, mm->source, now)) { + a->ias = mm->ias; + } + + if (mm->tas_valid && accept_data(&a->tas_valid, mm->source, now)) { + a->tas = mm->tas; + } + + if (mm->mach_valid && accept_data(&a->mach_valid, mm->source, now)) { + a->mach = mm->mach; + } + + if (mm->baro_rate_valid && accept_data(&a->baro_rate_valid, mm->source, now)) { + a->baro_rate = mm->baro_rate; + } + + if (mm->geom_rate_valid && accept_data(&a->geom_rate_valid, mm->source, now)) { + a->geom_rate = mm->geom_rate; } if (mm->category_valid && accept_data(&a->category_valid, mm->source, now)) { @@ -607,6 +622,22 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) memcpy(a->callsign, mm->callsign, sizeof(a->callsign)); } + if (mm->intent.valid) { + if (mm->intent.mcp_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source, now)) { + a->intent_altitude = mm->intent.mcp_altitude; + } else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source, now)) { + a->intent_altitude = mm->intent.fms_altitude; + } + + if (mm->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source, now)) { + a->intent_heading = mm->intent.heading; + } + + if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source, now)) { + a->alt_setting = mm->intent.alt_setting; + } + } + // CPR, even if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) { a->cpr_even_type = mm->cpr_type; @@ -625,12 +656,12 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) // Now handle derived data - // derive GNSS if we have baro + delta - if (compare_validity(&a->altitude_valid, &a->altitude_gnss_valid, now) > 0 && - compare_validity(&a->gnss_delta_valid, &a->altitude_gnss_valid, now) > 0) { - // Baro and delta are both more recent than GNSS, derive GNSS from baro + delta - a->altitude_gnss = a->altitude + a->gnss_delta; - combine_validity(&a->altitude_gnss_valid, &a->altitude_valid, &a->gnss_delta_valid); + // derive geometric altitude if we have baro + delta + if (compare_validity(&a->altitude_valid, &a->altitude_geom_valid, now) > 0 && + compare_validity(&a->geom_delta_valid, &a->altitude_geom_valid, now) > 0) { + // Baro and delta are both more recent than geometric, derive geometric from baro + delta + a->altitude_geom = a->altitude + a->geom_delta; + combine_validity(&a->altitude_geom_valid, &a->altitude_valid, &a->geom_delta_valid); } // If we've got a new cprlat or cprlon @@ -752,17 +783,24 @@ static void trackRemoveStaleAircraft(uint64_t now) #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); - EXPIRE(altitude_gnss); - EXPIRE(gnss_delta); - EXPIRE(speed); - EXPIRE(speed_ias); - EXPIRE(speed_tas); - EXPIRE(heading); - EXPIRE(heading_magnetic); - EXPIRE(vert_rate); + 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(baro_rate); + EXPIRE(geom_rate); EXPIRE(squawk); EXPIRE(category); EXPIRE(airground); + EXPIRE(alt_setting); + EXPIRE(intent_altitude); + EXPIRE(intent_heading); EXPIRE(cpr_odd); EXPIRE(cpr_even); EXPIRE(position); diff --git a/track.h b/track.h index 57be9f9..dc441bd 100644 --- a/track.h +++ b/track.h @@ -88,30 +88,41 @@ struct aircraft { data_validity altitude_valid; int altitude; // Altitude (Baro) - data_validity altitude_gnss_valid; - int altitude_gnss; // Altitude (GNSS) + data_validity altitude_geom_valid; + int altitude_geom; // Altitude (Geometric) - data_validity gnss_delta_valid; - int gnss_delta; // Difference between GNSS and Baro altitudes + data_validity geom_delta_valid; + int geom_delta; // Difference between Geometric and Baro altitudes - data_validity speed_valid; - unsigned speed; + data_validity gs_valid; + unsigned gs; - data_validity speed_ias_valid; - unsigned speed_ias; + data_validity ias_valid; + unsigned ias; - data_validity speed_tas_valid; - unsigned speed_tas; + data_validity tas_valid; + unsigned tas; - data_validity heading_valid; - unsigned heading; // Heading (OK it's really the track) + data_validity mach_valid; + float mach; - data_validity heading_magnetic_valid; - unsigned heading_magnetic; // Heading + data_validity track_valid; + float track; // Ground track - data_validity vert_rate_valid; - int vert_rate; // Vertical rate - altitude_source_t vert_rate_source; + data_validity track_rate_valid; + float track_rate; // Rate of change of ground track, degrees/second + + data_validity roll_valid; + float roll; // Roll angle, degrees right + + data_validity mag_heading_valid; + float mag_heading; // Magnetic heading + + data_validity baro_rate_valid; + int baro_rate; // Vertical rate (barometric) + + data_validity geom_rate_valid; + int geom_rate; // Vertical rate (geometric) data_validity squawk_valid; unsigned squawk; // Squawk @@ -122,6 +133,15 @@ struct aircraft { data_validity airground_valid; airground_t airground; // air/ground status + data_validity alt_setting_valid; + float alt_setting; // Altimeter setting (QNH/QFE), millibars + + data_validity intent_altitude_valid; + unsigned intent_altitude; // intent altitude (FMS or FCU selected altitude) + + data_validity intent_heading_valid; + float intent_heading; // intent heading, degrees (0-359) + data_validity cpr_odd_valid; // Last seen even CPR message cpr_type_t cpr_odd_type; unsigned cpr_odd_lat; @@ -143,16 +163,23 @@ struct aircraft { int fatsv_emitted_altitude; // last FA emitted altitude int fatsv_emitted_altitude_gnss; // -"- GNSS altitude - int fatsv_emitted_heading; // -"- true track - int fatsv_emitted_heading_magnetic; // -"- magnetic heading - int fatsv_emitted_speed; // -"- groundspeed - int fatsv_emitted_speed_ias; // -"- IAS - int fatsv_emitted_speed_tas; // -"- TAS + int fatsv_emitted_baro_rate; // -"- barometric rate + int fatsv_emitted_geom_rate; // -"- geometric rate + float fatsv_emitted_heading; // -"- true track + float fatsv_emitted_heading_magnetic; // -"- magnetic heading + float fatsv_emitted_track_rate; // -"- track rate of change + float fatsv_emitted_roll; // -"- roll angle + unsigned fatsv_emitted_speed; // -"- groundspeed + unsigned fatsv_emitted_speed_ias; // -"- IAS + unsigned fatsv_emitted_speed_tas; // -"- TAS + float fatsv_emitted_mach; // -"- Mach number airground_t fatsv_emitted_airground; // -"- air/ground state + unsigned fatsv_emitted_intent_altitude; // -"- intent altitude + float fatsv_emitted_intent_heading; // -"- intent heading + float fatsv_emitted_alt_setting; // -"- altimeter setting unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message - unsigned char fatsv_emitted_es_target[7]; // -"- ES target status message unsigned char fatsv_emitted_es_acas_ra[7]; // -"- ES ACAS RA report message uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted From 2142d2edf50bba06ba21c4369925b7d9a126f124 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 18:16:51 +0100 Subject: [PATCH 02/52] Whitespace changes only: cleaning up trailing whitespace at EOL --- convert.c | 14 ++++++------- convert.h | 14 ++++++------- convert_benchmark.c | 14 ++++++------- cpr.c | 20 +++++++++--------- cpr.h | 14 ++++++------- cprtests.c | 14 ++++++------- crc.c | 40 ++++++++++++++++++------------------ crc.h | 14 ++++++------- demod_2400.c | 38 +++++++++++++++++----------------- demod_2400.h | 14 ++++++------- dump1090.c | 40 ++++++++++++++++++------------------ dump1090.h | 33 +++++++++++++++--------------- faup1090.c | 26 +++++++++++------------ icao_filter.c | 16 +++++++-------- icao_filter.h | 14 ++++++------- interactive.c | 16 +++++++-------- mode_ac.c | 14 ++++++------- mode_s.c | 50 ++++++++++++++++++++++----------------------- net_io.c | 44 +++++++++++++++++++-------------------- net_io.h | 14 ++++++------- sdr.c | 14 ++++++------- sdr.h | 14 ++++++------- sdr_bladerf.c | 14 ++++++------- sdr_bladerf.h | 14 ++++++------- sdr_ifile.c | 16 +++++++-------- sdr_ifile.h | 14 ++++++------- sdr_rtlsdr.c | 18 ++++++++-------- sdr_rtlsdr.h | 14 ++++++------- stats.c | 20 +++++++++--------- stats.h | 18 ++++++++-------- track.c | 20 +++++++++--------- track.h | 16 +++++++-------- util.c | 16 +++++++-------- util.h | 14 ++++++------- view1090.c | 14 ++++++------- 35 files changed, 350 insertions(+), 349 deletions(-) diff --git a/convert.c b/convert.c index fc2a7ae..3f34a38 100644 --- a/convert.c +++ b/convert.c @@ -4,17 +4,17 @@ // // Copyright (c) 2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" diff --git a/convert.h b/convert.h index 6412fc0..6b861b1 100644 --- a/convert.h +++ b/convert.h @@ -4,17 +4,17 @@ // // Copyright (c) 2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_CONVERT_H diff --git a/convert_benchmark.c b/convert_benchmark.c index 24b1bc1..96b691f 100644 --- a/convert_benchmark.c +++ b/convert_benchmark.c @@ -5,17 +5,17 @@ // Copyright (c) 2016-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" diff --git a/cpr.c b/cpr.c index 3f8187b..f2e35c5 100644 --- a/cpr.c +++ b/cpr.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -345,9 +345,9 @@ int decodeCPRrelative(double reflat, double reflon, return (-1); // Time to give up - Latitude error } - // Check to see that answer is reasonable - ie no more than 1/2 cell away + // Check to see that answer is reasonable - ie no more than 1/2 cell away if (fabs(rlat - reflat) > (AirDlat/2)) { - return (-1); // Time to give up - Latitude error + return (-1); // Time to give up - Latitude error } // Compute the Longitude Index "m" diff --git a/cpr.h b/cpr.h index 1cb413b..65f4fa4 100644 --- a/cpr.h +++ b/cpr.h @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_CPR_H diff --git a/cprtests.c b/cprtests.c index abcdac8..875c959 100644 --- a/cprtests.c +++ b/cprtests.c @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . diff --git a/crc.c b/crc.c index ad7d020..0f366ee 100644 --- a/crc.c +++ b/crc.c @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" @@ -40,7 +40,7 @@ static void initLookupTables() { int i; uint8_t msg[112/8]; - + for (i = 0; i < 256; ++i) { uint32_t c = i << 16; int j; @@ -141,7 +141,7 @@ static int prepareSubtable(struct errorinfo *table, int n, int maxsize, int offs table[n].syndrome ^= single_bit_syndrome[i + offset]; table[n].errors = error_bit+1; table[n].bit[error_bit] = i; - + ++n; n = prepareSubtable(table, n, maxsize, offset, i + 1, endbit, &table[n-1], error_bit + 1, max_errors); } @@ -200,7 +200,7 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de maxsize += combinations(bits - 5, i); // space needed for all i-bit errors } -#ifdef CRCDEBUG +#ifdef CRCDEBUG fprintf(stderr, "Preparing syndrome table to correct up to %d-bit errors (detecting %d-bit errors) in a %d-bit message (max %d entries)\n", max_correct, max_detect, bits, maxsize); #endif @@ -212,7 +212,7 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de // ignore the first 5 bits (DF type) usedsize = prepareSubtable(table, 0, maxsize, 112 - bits, 5, bits, &base_entry, 0, max_correct); - + #ifdef CRCDEBUG fprintf(stderr, "%d syndromes (expected %d).\n", usedsize, maxsize); fprintf(stderr, "Sorting syndromes..\n"); @@ -220,15 +220,15 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de qsort(table, usedsize, sizeof(struct errorinfo), syndrome_compare); -#ifdef CRCDEBUG +#ifdef CRCDEBUG { // Show the table stats fprintf(stderr, "Undetectable errors:\n"); for (i = 1; i <= max_correct; ++i) { int j, count; - + count = 0; - for (j = 0; j < usedsize; ++j) + for (j = 0; j < usedsize; ++j) if (table[j].errors == i && table[j].syndrome == 0) ++count; @@ -240,7 +240,7 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de // Handle ambiguous cases, where there is more than one possible error pattern // that produces a given syndrome (this happens with >2 bit errors). -#ifdef CRCDEBUG +#ifdef CRCDEBUG fprintf(stderr, "Finding collisions..\n"); #endif for (i = 0, j = 0; i < usedsize; ++i) { @@ -264,7 +264,7 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de #endif usedsize = j; } - + // Flag collisions we want to detect but not correct if (max_detect > max_correct) { int flagged; @@ -302,9 +302,9 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de table = realloc(table, usedsize * sizeof(struct errorinfo)); #endif } - + *size_out = usedsize; - + #ifdef CRCDEBUG { // Check the table. @@ -335,9 +335,9 @@ static struct errorinfo *prepareErrorTable(int bits, int max_correct, int max_de fprintf(stderr, "Syndrome table summary:\n"); for (i = 1; i <= max_correct; ++i) { int j, count, possible; - + count = 0; - for (j = 0; j < usedsize; ++j) + for (j = 0; j < usedsize; ++j) if (table[j].errors == i) ++count; diff --git a/crc.h b/crc.h index 96abe35..c6d2902 100644 --- a/crc.h +++ b/crc.h @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_CRC_H diff --git a/demod_2400.c b/demod_2400.c index 9d350b0..006cd2e 100644 --- a/demod_2400.c +++ b/demod_2400.c @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" @@ -98,7 +98,7 @@ void demodulate2400(struct mag_buf *mag) // phase 6: 0/4\2 2/4\0 0 0 0 2/4\0/5\1 0 0 0 0 0 0 X2 // phase 7: 0/3 3\1/5\0 0 0 0 1/5\0/4\2 0 0 0 0 0 0 X3 // - + // quick check: we must have a rising edge 0->1 and a falling edge 12->13 if (! (preamble[0] < preamble[1] && preamble[12] > preamble[13]) ) continue; @@ -174,7 +174,7 @@ void demodulate2400(struct mag_buf *mag) // Decode all the next 112 bits, regardless of the actual message // size. We'll check the actual message type later - + pPtr = &m[j+19] + (try_phase/5); phase = try_phase % 5; @@ -184,7 +184,7 @@ void demodulate2400(struct mag_buf *mag) switch (phase) { case 0: - theByte = + theByte = (slice_phase0(pPtr) > 0 ? 0x80 : 0) | (slice_phase2(pPtr+2) > 0 ? 0x40 : 0) | (slice_phase4(pPtr+4) > 0 ? 0x20 : 0) | @@ -198,7 +198,7 @@ void demodulate2400(struct mag_buf *mag) phase = 1; pPtr += 19; break; - + case 1: theByte = (slice_phase1(pPtr) > 0 ? 0x80 : 0) | @@ -213,7 +213,7 @@ void demodulate2400(struct mag_buf *mag) phase = 2; pPtr += 19; break; - + case 2: theByte = (slice_phase2(pPtr) > 0 ? 0x80 : 0) | @@ -228,9 +228,9 @@ void demodulate2400(struct mag_buf *mag) phase = 3; pPtr += 19; break; - + case 3: - theByte = + theByte = (slice_phase3(pPtr) > 0 ? 0x80 : 0) | (slice_phase0(pPtr+3) > 0 ? 0x40 : 0) | (slice_phase2(pPtr+5) > 0 ? 0x20 : 0) | @@ -243,9 +243,9 @@ void demodulate2400(struct mag_buf *mag) phase = 4; pPtr += 19; break; - + case 4: - theByte = + theByte = (slice_phase4(pPtr) > 0 ? 0x80 : 0) | (slice_phase1(pPtr+3) > 0 ? 0x40 : 0) | (slice_phase3(pPtr+5) > 0 ? 0x20 : 0) | @@ -265,7 +265,7 @@ void demodulate2400(struct mag_buf *mag) switch (msg[0] >> 3) { case 0: case 4: case 5: case 11: bytelen = MODES_SHORT_MSG_BYTES; break; - + case 16: case 17: case 18: case 20: case 21: case 24: break; @@ -283,7 +283,7 @@ void demodulate2400(struct mag_buf *mag) bestmsg = msg; bestscore = score; bestphase = try_phase; - + // swap to using the other buffer so we don't clobber our demodulated data // (if we find a better result then we'll swap back, but that's OK because // we no longer need this copy if we found a better one) @@ -362,7 +362,7 @@ void demodulate2400(struct mag_buf *mag) // few bits of the first message, but the message bits didn't // overlap) j += msglen*12/5; - + // Pass data to the next layer useModesMessage(&mm); } diff --git a/demod_2400.h b/demod_2400.h index 907294f..c514ba4 100644 --- a/demod_2400.h +++ b/demod_2400.h @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_DEMOD_2400_H diff --git a/dump1090.c b/dump1090.c index 82eda50..314b85a 100644 --- a/dump1090.c +++ b/dump1090.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014-2016 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -158,24 +158,24 @@ void modesInit(void) { // Validate the users Lat/Lon home location inputs if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90 - || (Modes.fUserLat < -90.0) // and + || (Modes.fUserLat < -90.0) // and || (Modes.fUserLon > 360.0) // Longitude must be -180 to +360 || (Modes.fUserLon < -180.0) ) { Modes.fUserLat = Modes.fUserLon = 0.0; } else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0 Modes.fUserLon -= 360.0; } - // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the - // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. - // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian - // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. + // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the + // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. + // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian + // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. // Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0)) Modes.bUserFlags &= ~MODES_USER_LATLON_VALID; if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) { Modes.bUserFlags |= MODES_USER_LATLON_VALID; } - // Limit the maximum requested raw output size to less than one Ethernet Block + // Limit the maximum requested raw output size to less than one Ethernet Block if (Modes.net_output_flush_size > (MODES_OUT_FLUSH_SIZE)) {Modes.net_output_flush_size = MODES_OUT_FLUSH_SIZE;} if (Modes.net_output_flush_interval > (MODES_OUT_FLUSH_INTERVAL)) @@ -372,7 +372,7 @@ void backgroundTasks(void) { if (Modes.net) { modesNetPeriodicWork(); - } + } // Refresh screen when in interactive mode @@ -391,21 +391,21 @@ void backgroundTasks(void) { } else { Modes.stats_latest_1min = (Modes.stats_latest_1min + 1) % 15; Modes.stats_1min[Modes.stats_latest_1min] = Modes.stats_current; - + add_stats(&Modes.stats_current, &Modes.stats_alltime, &Modes.stats_alltime); add_stats(&Modes.stats_current, &Modes.stats_periodic, &Modes.stats_periodic); - + reset_stats(&Modes.stats_5min); for (i = 0; i < 5; ++i) add_stats(&Modes.stats_1min[(Modes.stats_latest_1min - i + 15) % 15], &Modes.stats_5min, &Modes.stats_5min); - + reset_stats(&Modes.stats_15min); for (i = 0; i < 15; ++i) add_stats(&Modes.stats_1min[i], &Modes.stats_15min, &Modes.stats_15min); - + reset_stats(&Modes.stats_current); Modes.stats_current.start = Modes.stats_current.end = now; - + if (Modes.json_dir) writeJsonToFile("stats.json", generateStatsJson); diff --git a/dump1090.h b/dump1090.h index d74c547..ffc6c27 100644 --- a/dump1090.h +++ b/dump1090.h @@ -4,20 +4,20 @@ // // Copyright (c) 2014-2016 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -374,10 +374,10 @@ struct modesMessage { // Generic fields unsigned char msg[MODES_LONG_MSG_BYTES]; // Binary message. unsigned char verbatim[MODES_LONG_MSG_BYTES]; // Binary message, as originally received before correction - int msgbits; // Number of bits in message + int msgbits; // Number of bits in message int msgtype; // Downlink format # uint32_t crc; // Message CRC - int correctedbits; // No. of bits corrected + int correctedbits; // No. of bits corrected uint32_t addr; // Address Announced addrtype_t addrtype; // address format / source uint64_t timestampMsg; // Timestamp of the message (12MHz clock) @@ -462,13 +462,14 @@ struct modesMessage { unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits char callsign[9]; // 8 chars flight number, NUL-terminated unsigned category; // A0 - D7 encoded as a single hex byte - // valid if cpr_valid - cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B) - unsigned cpr_lat; // Non decoded latitude. - unsigned cpr_lon; // Non decoded longitude. - unsigned cpr_nucp; // NUCp/NIC value implied by message type - airground_t airground; // air/ground state + // valid if cpr_valid + cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B) + unsigned cpr_lat; // Non decoded latitude. + unsigned cpr_lon; // Non decoded longitude. + unsigned cpr_nucp; // NUCp/NIC value implied by message type + + airground_t airground; // air/ground state // valid if cpr_decoded: double decoded_lat; diff --git a/faup1090.c b/faup1090.c index 31eb0d8..e476a7e 100644 --- a/faup1090.c +++ b/faup1090.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -83,17 +83,17 @@ static void faupInitConfig(void) { static void faupInit(void) { // Validate the users Lat/Lon home location inputs if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90 - || (Modes.fUserLat < -90.0) // and + || (Modes.fUserLat < -90.0) // and || (Modes.fUserLon > 360.0) // Longitude must be -180 to +360 || (Modes.fUserLon < -180.0) ) { Modes.fUserLat = Modes.fUserLon = 0.0; } else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0 Modes.fUserLon -= 360.0; } - // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the - // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. - // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian - // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. + // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the + // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. + // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian + // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. // Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0)) Modes.bUserFlags &= ~MODES_USER_LATLON_VALID; if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) { diff --git a/icao_filter.c b/icao_filter.c index 0284de1..38f7b5c 100644 --- a/icao_filter.c +++ b/icao_filter.c @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" @@ -51,7 +51,7 @@ static uint32_t icaoHash(uint32_t a) hash += (a >> 16) & 0xff; hash += (hash << 10); hash ^= (hash >> 6); - + hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); diff --git a/icao_filter.h b/icao_filter.h index 2c30f8d..397f7f3 100644 --- a/icao_filter.h +++ b/icao_filter.h @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_ICAO_FILTER_H diff --git a/interactive.c b/interactive.c index 09fcd1b..66fdfb9 100644 --- a/interactive.c +++ b/interactive.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo diff --git a/mode_ac.c b/mode_ac.c index c482fd7..9af1243 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -92,10 +92,10 @@ static int internalModeAToModeC(unsigned int ModeA) if (ModeA & 0x0020) {OneHundreds ^= 0x003;} // C2 if (ModeA & 0x0040) {OneHundreds ^= 0x001;} // C4 - // Remove 7s from OneHundreds (Make 7->5, snd 5->7). + // Remove 7s from OneHundreds (Make 7->5, snd 5->7). if ((OneHundreds & 5) == 5) {OneHundreds ^= 2;} - // Check for invalid codes, only 1 to 5 are valid + // Check for invalid codes, only 1 to 5 are valid if (OneHundreds > 5) { return INVALID_ALTITUDE; } @@ -108,14 +108,14 @@ static int internalModeAToModeC(unsigned int ModeA) if (ModeA & 0x2000) {FiveHundreds ^= 0x01F;} // A2 if (ModeA & 0x4000) {FiveHundreds ^= 0x00F;} // A4 - if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1 + if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1 if (ModeA & 0x0200) {FiveHundreds ^= 0x003;} // B2 if (ModeA & 0x0400) {FiveHundreds ^= 0x001;} // B4 - - // Correct order of OneHundreds. - if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;} - return ((FiveHundreds * 5) + OneHundreds - 13); + // Correct order of OneHundreds. + if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;} + + return ((FiveHundreds * 5) + OneHundreds - 13); } // //========================================================================= diff --git a/mode_s.c b/mode_s.c index f14f0f1..16aec58 100644 --- a/mode_s.c +++ b/mode_s.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014-2016 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -68,8 +68,8 @@ // // Given the Downlink Format (DF) of the message, return the message length in bits. // -// All known DF's 16 or greater are long. All known DF's 15 or less are short. -// There are lots of unused codes in both category, so we can assume ICAO will stick to +// All known DF's 16 or greater are long. All known DF's 15 or less are short. +// There are lots of unused codes in both category, so we can assume ICAO will stick to // these rules, meaning that the most significant bit of the DF indicates the length. // int modesMessageLenByType(int type) { @@ -86,7 +86,7 @@ int modesMessageLenByType(int type) { // // So every group of three bits A, B, C, D represent an integer from 0 to 7. // -// The actual meaning is just 4 octal numbers, but we convert it into a hex +// The actual meaning is just 4 octal numbers, but we convert it into a hex // number tha happens to represent the four octal numbers. // // For more info: http://en.wikipedia.org/wiki/Gillham_code @@ -100,8 +100,8 @@ static int decodeID13Field(int ID13Field) { if (ID13Field & 0x0200) {hexGillham |= 0x2000;} // Bit 9 = A2 if (ID13Field & 0x0100) {hexGillham |= 0x0040;} // Bit 8 = C4 if (ID13Field & 0x0080) {hexGillham |= 0x4000;} // Bit 7 = A4 - //if (ID13Field & 0x0040) {hexGillham |= 0x0800;} // Bit 6 = X or M - if (ID13Field & 0x0020) {hexGillham |= 0x0100;} // Bit 5 = B1 + //if (ID13Field & 0x0040) {hexGillham |= 0x0800;} // Bit 6 = X or M + if (ID13Field & 0x0020) {hexGillham |= 0x0100;} // Bit 5 = B1 if (ID13Field & 0x0010) {hexGillham |= 0x0001;} // Bit 4 = D1 or Q if (ID13Field & 0x0008) {hexGillham |= 0x0200;} // Bit 3 = B2 if (ID13Field & 0x0004) {hexGillham |= 0x0002;} // Bit 2 = D2 @@ -157,13 +157,13 @@ static int decodeAC12Field(int AC12Field, altitude_unit_t *unit) { *unit = UNIT_FEET; if (q_bit) { /// N is the 11 bit integer resulting from the removal of bit Q at bit 4 - int n = ((AC12Field & 0x0FE0) >> 1) | + int n = ((AC12Field & 0x0FE0) >> 1) | (AC12Field & 0x000F); // The final altitude is the resulting number multiplied by 25, minus 1000. return ((n * 25) - 1000); } else { // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6 - int n = ((AC12Field & 0x0FC0) << 1) | + int n = ((AC12Field & 0x0FC0) << 1) | (AC12Field & 0x003F); n = modeAToModeC(decodeID13Field(n)); if (n < -12) { @@ -182,7 +182,7 @@ static int decodeAC12Field(int AC12Field, altitude_unit_t *unit) { static unsigned decodeMovementField(unsigned movement) { int gspeed; - // Note : movement codes 0,125,126,127 are all invalid, but they are + // Note : movement codes 0,125,126,127 are all invalid, but they are // trapped for before this function is called. if (movement > 123) gspeed = 199; // > 175kt @@ -200,7 +200,7 @@ static unsigned decodeMovementField(unsigned movement) { // (from bits 8-31) if it is affected by the given error // syndrome. Updates *addr and returns >0 if changed, 0 if // it was unaffected. -static int correct_aa_field(uint32_t *addr, struct errorinfo *ei) +static int correct_aa_field(uint32_t *addr, struct errorinfo *ei) { int i; int addr_errors = 0; @@ -310,7 +310,7 @@ int scoreModesMessage(unsigned char *msg, int validbits) else return -1; } - + case 17: // Extended squitter case 18: // Extended squitter/non-transponder ei = modesChecksumDiagnose(crc, msgbits); @@ -319,7 +319,7 @@ int scoreModesMessage(unsigned char *msg, int validbits) // fix any errors in the address field addr = getbits(msg, 9, 32); - correct_aa_field(&addr, ei); + correct_aa_field(&addr, ei); if (icaoFilterTest(addr)) return 1800 / (ei->errors+1); @@ -349,7 +349,7 @@ int scoreModesMessage(unsigned char *msg, int validbits) // //========================================================================= // -// Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(), +// Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(), // and split it into fields populating a modesMessage structure. // @@ -458,7 +458,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) mm->correctedbits = ei->errors; modesChecksumFix(msg, ei); addr2 = getbits(msg, 9, 32); - + // we are conservative here: only accept corrected messages that // match an existing aircraft. if (addr1 != addr2 && !icaoFilterTest(addr2)) { @@ -492,7 +492,7 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) default: // All other message types, we don't know how to handle their CRCs, give up return -2; - } + } // decode the bulk of the message @@ -1175,7 +1175,7 @@ static void decodeExtendedSquitter(struct modesMessage *mm) decodeESOperationalStatus(mm, check_imf); break; - default: + default: break; } } @@ -1755,8 +1755,8 @@ void displayModesMessage(struct modesMessage *mm) { // //========================================================================= // -// When a new message is available, because it was decoded from the RTL device, -// file, or received in the TCP input port, or any other way we can receive a +// When a new message is available, because it was decoded from the RTL device, +// file, or received in the TCP input port, or any other way we can receive a // decoded message, we call this function in order to use the message. // // Basically this function passes a raw message to the upper layers for further diff --git a/net_io.c b/net_io.c index 66fce2b..bc7c7fe 100644 --- a/net_io.c +++ b/net_io.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014-2016 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -606,7 +606,7 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { if (mm->gs_valid) { p += sprintf(p, ",%d", mm->gs); } else { - p += sprintf(p, ","); + p += sprintf(p, ","); } // Field 14 is the ground Heading (if we have it) @@ -972,13 +972,13 @@ static int hexDigitVal(int c) { // // This function decodes a string representing message in raw hex format // like: *8D4B969699155600E87406F5B69F; The string is null-terminated. -// +// // The message is passed to the higher level layers, so it feeds // the selected screen output, the network output and so forth. -// +// // If the message looks invalid it is silently discarded. // -// The function always returns 0 (success) to the caller as there is no +// The function always returns 0 (success) to the caller as there is no // case where we want broken messages here to close the client connection. // static int decodeHexMessage(struct client *c, char *hex) { @@ -1030,13 +1030,13 @@ static int decodeHexMessage(struct client *c, char *hex) { break;} } - if ( (l != (MODEAC_MSG_BYTES * 2)) - && (l != (MODES_SHORT_MSG_BYTES * 2)) + if ( (l != (MODEAC_MSG_BYTES * 2)) + && (l != (MODES_SHORT_MSG_BYTES * 2)) && (l != (MODES_LONG_MSG_BYTES * 2)) ) {return (0);} // Too short or long message... broken - if ( (0 == Modes.mode_ac) - && (l == (MODEAC_MSG_BYTES * 2)) ) + if ( (0 == Modes.mode_ac) + && (l == (MODEAC_MSG_BYTES * 2)) ) {return (0);} // Right length for ModeA/C, but not enabled for (j = 0; j < l; j += 2) { @@ -1171,11 +1171,11 @@ char *generateAircraftJson(const char *url_path, int *len) { continue; } - if (first) + if (first) first = 0; else *p++ = ','; - + p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); if (a->addrtype != ADDR_ADSB_ICAO) p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_short_string(a->addrtype)); @@ -1223,7 +1223,7 @@ char *generateAircraftJson(const char *url_path, int *len) { 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 we're getting near the end of the buffer, expand it. if ((end - p) < 512) { int used = p - buf; @@ -1353,7 +1353,7 @@ static char * appendStatsJson(char *p, return p; } - + char *generateStatsJson(const char *url_path, int *len) { struct stats add; char *buf = (char *) malloc(4096), *p = buf, *end = buf + 4096; @@ -1375,7 +1375,7 @@ char *generateStatsJson(const char *url_path, int *len) { add_stats(&Modes.stats_alltime, &Modes.stats_current, &add); p = appendStatsJson(p, end, &add, "total"); - p += snprintf(p, end-p, "\n}\n"); + p += snprintf(p, end-p, "\n}\n"); assert(p <= end); @@ -1461,7 +1461,7 @@ void writeJsonToFile(const char *file, char * (*generator) (const char *,int*)) fd = mkstemp(tmppath); if (fd < 0) return; - + mask = umask(0); umask(mask); fchmod(fd, 0644 & ~mask); diff --git a/net_io.h b/net_io.h index 194792b..2c39f6f 100644 --- a/net_io.h +++ b/net_io.h @@ -4,17 +4,17 @@ // // Copyright (c) 2014,2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_NETIO_H diff --git a/sdr.c b/sdr.c index f37d3e8..89fe8f4 100644 --- a/sdr.c +++ b/sdr.c @@ -5,17 +5,17 @@ // Copyright (c) 2016-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" diff --git a/sdr.h b/sdr.h index 072da66..4bd20a6 100644 --- a/sdr.h +++ b/sdr.h @@ -5,17 +5,17 @@ // Copyright (c) 2016-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef SDR_H diff --git a/sdr_bladerf.c b/sdr_bladerf.c index 068da48..c849b14 100644 --- a/sdr_bladerf.c +++ b/sdr_bladerf.c @@ -4,17 +4,17 @@ // // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "dump1090.h" diff --git a/sdr_bladerf.h b/sdr_bladerf.h index 39de012..9b8bdab 100644 --- a/sdr_bladerf.h +++ b/sdr_bladerf.h @@ -4,17 +4,17 @@ // // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef BLADERF_H diff --git a/sdr_ifile.c b/sdr_ifile.c index 6f7750c..771c36e 100644 --- a/sdr_ifile.c +++ b/sdr_ifile.c @@ -5,20 +5,20 @@ // Copyright (c) 2014-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo diff --git a/sdr_ifile.h b/sdr_ifile.h index 6652426..41b9e4b 100644 --- a/sdr_ifile.h +++ b/sdr_ifile.h @@ -5,17 +5,17 @@ // Copyright (c) 2016-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef SDR_IFILE_H diff --git a/sdr_rtlsdr.c b/sdr_rtlsdr.c index 4f71c17..0feb992 100644 --- a/sdr_rtlsdr.c +++ b/sdr_rtlsdr.c @@ -5,20 +5,20 @@ // Copyright (c) 2014-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -223,7 +223,7 @@ bool rtlsdrOpen(void) { if (closest == -1 || abs(gains[i] - target) < abs(gains[closest] - target)) closest = i; } - + rtlsdr_set_tuner_gain(RTLSDR.dev, gains[closest]); free(gains); diff --git a/sdr_rtlsdr.h b/sdr_rtlsdr.h index 317b2e4..54999e7 100644 --- a/sdr_rtlsdr.h +++ b/sdr_rtlsdr.h @@ -5,17 +5,17 @@ // Copyright (c) 2016-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef SDR_RTLSDR_H diff --git a/stats.c b/stats.c index 69cfcf5..62522f7 100644 --- a/stats.c +++ b/stats.c @@ -4,20 +4,20 @@ // // Copyright (c) 2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -261,7 +261,7 @@ void add_stats(const struct stats *st1, const struct stats *st2, struct stats *t target->start = st2->start; target->end = st1->end > st2->end ? st1->end : st2->end; - + target->demod_preambles = st1->demod_preambles + st2->demod_preambles; target->demod_rejected_bad = st1->demod_rejected_bad + st2->demod_rejected_bad; target->demod_rejected_unknown_icao = st1->demod_rejected_unknown_icao + st2->demod_rejected_unknown_icao; @@ -275,7 +275,7 @@ void add_stats(const struct stats *st1, const struct stats *st2, struct stats *t add_timespecs(&st1->demod_cpu, &st2->demod_cpu, &target->demod_cpu); add_timespecs(&st1->reader_cpu, &st2->reader_cpu, &target->reader_cpu); add_timespecs(&st1->background_cpu, &st2->background_cpu, &target->background_cpu); - + // noise power: target->noise_power_sum = st1->noise_power_sum + st2->noise_power_sum; target->noise_power_count = st1->noise_power_count + st2->noise_power_count; diff --git a/stats.h b/stats.h index 0e65167..36ab59e 100644 --- a/stats.h +++ b/stats.h @@ -4,20 +4,20 @@ // // Copyright (c) 2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -124,7 +124,7 @@ struct stats { // range histogram #define RANGE_BUCKET_COUNT 76 uint32_t range_histogram[RANGE_BUCKET_COUNT]; -}; +}; void add_stats(const struct stats *st1, const struct stats *st2, struct stats *target); void display_stats(struct stats *st); diff --git a/track.c b/track.c index ea98ab1..8fb3f24 100644 --- a/track.c +++ b/track.c @@ -4,20 +4,20 @@ // // Copyright (c) 2014-2016 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo @@ -351,7 +351,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, } else if (!surface && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { reflat = Modes.fUserLat; reflon = Modes.fUserLon; - + // The cell size is at least 360NM, giving a nominal // max range of 180NM (half a cell). // @@ -762,7 +762,7 @@ 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)) { diff --git a/track.h b/track.h index dc441bd..f79f1cd 100644 --- a/track.h +++ b/track.h @@ -4,20 +4,20 @@ // // Copyright (c) 2014-2016 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo diff --git a/util.c b/util.c index 3711237..d7afc07 100644 --- a/util.c +++ b/util.c @@ -4,20 +4,20 @@ // // Copyright (c) 2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// 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 +// This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo diff --git a/util.h b/util.h index 51405b4..6030c97 100644 --- a/util.h +++ b/util.h @@ -4,17 +4,17 @@ // // Copyright (c) 2015 Oliver Jowett // -// This file is free software: you may copy, redistribute and/or modify it +// This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 2 of the License, or (at your -// option) any later version. +// Free Software Foundation, either version 2 of the License, or (at your +// option) 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 +// 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 +// You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DUMP1090_UTIL_H diff --git a/view1090.c b/view1090.c index c1e81f9..863cc83 100644 --- a/view1090.c +++ b/view1090.c @@ -67,10 +67,10 @@ void view1090Init(void) { pthread_cond_init(&Modes.data_cond,NULL); #ifdef _WIN32 - if ( (!Modes.wsaData.wVersion) + if ( (!Modes.wsaData.wVersion) && (!Modes.wsaData.wHighVersion) ) { // Try to start the windows socket support - if (WSAStartup(MAKEWORD(2,1),&Modes.wsaData) != 0) + if (WSAStartup(MAKEWORD(2,1),&Modes.wsaData) != 0) { fprintf(stderr, "WSAStartup returned Error\n"); } @@ -79,17 +79,17 @@ void view1090Init(void) { // Validate the users Lat/Lon home location inputs if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90 - || (Modes.fUserLat < -90.0) // and + || (Modes.fUserLat < -90.0) // and || (Modes.fUserLon > 360.0) // Longitude must be -180 to +360 || (Modes.fUserLon < -180.0) ) { Modes.fUserLat = Modes.fUserLon = 0.0; } else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0 Modes.fUserLon -= 360.0; } - // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the - // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. - // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian - // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. + // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the + // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. + // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian + // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. // Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0)) Modes.bUserFlags &= ~MODES_USER_LATLON_VALID; if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) { From 27b0e9c40fb3f4bca6a829d16a51170173725456 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 18:23:28 +0100 Subject: [PATCH 03/52] Rename TARGET_* -> INTENT_ALT_* Rearrange the intent tracking a bit. --- comm_b.c | 12 ++++++------ dump1090.h | 2 +- mode_s.c | 8 ++++---- track.c | 24 ++++++++++++------------ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/comm_b.c b/comm_b.c index b03ea43..085454b 100644 --- a/comm_b.c +++ b/comm_b.c @@ -391,23 +391,23 @@ static int decodeBDS40(struct modesMessage *mm, bool store) if (source_valid) { switch (source_raw) { case 0: - mm->intent.altitude_source = TARGET_UNKNOWN; + mm->intent.altitude_source = INTENT_ALT_UNKNOWN; break; case 1: - mm->intent.altitude_source = TARGET_AIRCRAFT; + mm->intent.altitude_source = INTENT_ALT_AIRCRAFT; break; case 2: - mm->intent.altitude_source = TARGET_MCP; + mm->intent.altitude_source = INTENT_ALT_MCP; break; case 3: - mm->intent.altitude_source = TARGET_FMS; + mm->intent.altitude_source = INTENT_ALT_FMS; break; default: - mm->intent.altitude_source = TARGET_INVALID; + mm->intent.altitude_source = INTENT_ALT_INVALID; break; } } else { - mm->intent.altitude_source = TARGET_INVALID; + mm->intent.altitude_source = INTENT_ALT_INVALID; } } diff --git a/dump1090.h b/dump1090.h index ffc6c27..2e4408b 100644 --- a/dump1090.h +++ b/dump1090.h @@ -529,7 +529,7 @@ struct modesMessage { unsigned mcp_altitude; // MCP/FCU selected altitude float alt_setting; // altimeter setting (QFE or QNH/QNE), millibars - enum { TARGET_INVALID, TARGET_UNKNOWN, TARGET_AIRCRAFT, TARGET_MCP, TARGET_FMS } altitude_source; + enum { INTENT_ALT_INVALID, INTENT_ALT_UNKNOWN, INTENT_ALT_AIRCRAFT, INTENT_ALT_MCP, INTENT_ALT_FMS } altitude_source; unsigned mode_autopilot : 1; // Autopilot engaged unsigned mode_vnav : 1; // Vertical Navigation Mode active diff --git a/mode_s.c b/mode_s.c index 16aec58..7f087ce 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1716,16 +1716,16 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->intent.alt_setting_valid) printf(" Altimeter setting: %.1f millibars\n", mm->intent.alt_setting); - if (mm->intent.altitude_source != TARGET_INVALID) { + if (mm->intent.altitude_source != INTENT_ALT_INVALID) { printf(" Target altitude source: "); switch (mm->intent.altitude_source) { - case TARGET_AIRCRAFT: + case INTENT_ALT_AIRCRAFT: printf("aircraft altitude\n"); break; - case TARGET_MCP: + case INTENT_ALT_MCP: printf("MCP selected altitude\n"); break; - case TARGET_FMS: + case INTENT_ALT_FMS: printf("FMS selected altitude\n"); break; default: diff --git a/track.c b/track.c index 8fb3f24..b2ae5dd 100644 --- a/track.c +++ b/track.c @@ -622,20 +622,20 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) memcpy(a->callsign, mm->callsign, sizeof(a->callsign)); } - if (mm->intent.valid) { - if (mm->intent.mcp_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source, now)) { - a->intent_altitude = mm->intent.mcp_altitude; - } else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source, now)) { - a->intent_altitude = mm->intent.fms_altitude; - } + // prefer MCP over FMS + // unless the source says otherwise + if (mm->intent.mcp_altitude_valid && mm->intent.altitude_source != INTENT_ALT_FMS && accept_data(&a->intent_altitude_valid, mm->source, now)) { + a->intent_altitude = mm->intent.mcp_altitude; + } else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source, now)) { + a->intent_altitude = mm->intent.fms_altitude; + } - if (mm->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source, now)) { - a->intent_heading = mm->intent.heading; - } + if (mm->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source, now)) { + a->intent_heading = mm->intent.heading; + } - if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source, now)) { - a->alt_setting = mm->intent.alt_setting; - } + if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source, now)) { + a->alt_setting = mm->intent.alt_setting; } // CPR, even From 05e9601903e4a454d0d30fafbcb4d2d2851a2235 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 18:30:12 +0100 Subject: [PATCH 04/52] Fix compilation. Emit some changes immediately in faup1090. --- net_io.c | 21 +++++++++++++-------- track.h | 1 + 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/net_io.c b/net_io.c index bc7c7fe..b0b9a36 100644 --- a/net_io.c +++ b/net_io.c @@ -1866,7 +1866,6 @@ static void writeFATSV() uint64_t minAge; int useful = 0; - int changed = 0; tisb_flags tisb = 0; char *p, *end; @@ -1897,7 +1896,7 @@ static void writeFATSV() int intentAltValid = trackDataValidEx(&a->intent_altitude_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int intentHeadingValid = trackDataValidEx(&a->intent_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int altSettingValid = trackDataValidEx(&a->alt_setting_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int callsignValid = trackDataValidEx(&a->callsign, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int callsignValid = trackDataValidEx(&a->callsign_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES // 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 @@ -1908,7 +1907,8 @@ static void writeFATSV() // if it hasn't changed altitude, heading, or speed much, // don't update so often - changed = 0; + int changed = 0; + int immediate = 0; if (altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) { changed = 1; } @@ -1946,21 +1946,25 @@ static void writeFATSV() changed = 1; } if (intentAltValid && unsigned_difference(a->intent_altitude, a->fatsv_emitted_intent_altitude) > 50) { - changed = 1; + changed = immediate = 1; } if (intentHeadingValid && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) { - changed = 1; + changed = immediate = 1; } if (altSettingValid && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) { // 0.8 is the ES message resolution - changed = 1; + changed = immediate = 1; } if (callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) { - changed = 1; + changed = immediate = 1; } - if (airgroundValid && ((a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || (a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE))) { // Air-ground transition, handle it immediately. + changed = immediate = 1; + } + + if (immediate) { + // a change we want to emit right away minAge = 0; } else if (!positionValid) { // don't send mode S very often @@ -2002,6 +2006,7 @@ static void writeFATSV() if (trackDataValidEx(&a->callsign_valid, now, 35000, SOURCE_MODE_S) && strcmp(a->callsign, " ") != 0 && a->callsign_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tident\t%s", a->callsign); + memcpy(a->fatsv_emitted_callsign, a->callsign, sizeof(a->fatsv_emitted_callsign)); switch (a->callsign_valid.source) { case SOURCE_MODE_S: p += snprintf(p, bufsize(p,end), "\tiSource\tmodes"); diff --git a/track.h b/track.h index f79f1cd..82a619c 100644 --- a/track.h +++ b/track.h @@ -181,6 +181,7 @@ struct aircraft { unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message unsigned char fatsv_emitted_es_acas_ra[7]; // -"- ES ACAS RA report message + char fatsv_emitted_callsign[9]; // -"- callsign uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted From 1ec32903bae3f080a3e9313614e0fa13007b97ca Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 21:07:53 +0100 Subject: [PATCH 05/52] Rework heading/track to include HRD/TAH. Clean up TIS-B flag output. --- comm_b.c | 10 ++++--- dump1090.h | 23 +++++++------- mode_s.c | 58 ++++++++++++++++++++++------------- net_io.c | 88 +++++++++++++++++++++++++++++++++++++++++++----------- track.c | 39 ++++++++++++++++++++---- track.h | 12 ++++++-- 6 files changed, 169 insertions(+), 61 deletions(-) diff --git a/comm_b.c b/comm_b.c index 085454b..80f9022 100644 --- a/comm_b.c +++ b/comm_b.c @@ -552,8 +552,9 @@ static int decodeBDS50(struct modesMessage *mm, bool store) } if (track_valid) { - mm->track_valid = 1; - mm->track = track; + mm->heading_valid = 1; + mm->heading = track; + mm->heading_type = HEADING_GROUND_TRACK; } if (gs_valid) { @@ -704,8 +705,9 @@ static int decodeBDS60(struct modesMessage *mm, bool store) mm->commb_format = COMMB_HEADING_SPEED; if (heading_valid) { - mm->mag_heading_valid = 1; - mm->mag_heading = heading; + mm->heading_valid = 1; + mm->heading = heading; + mm->heading_type = HEADING_MAGNETIC; } if (ias_valid) { diff --git a/dump1090.h b/dump1090.h index 2e4408b..7da9819 100644 --- a/dump1090.h +++ b/dump1090.h @@ -177,11 +177,6 @@ typedef enum { AG_UNCERTAIN } airground_t; -typedef enum { - HEADING_TRUE, - HEADING_MAGNETIC -} heading_source_t; - typedef enum { SIL_PER_SAMPLE, SIL_PER_HOUR } sil_type_t; @@ -190,6 +185,14 @@ typedef enum { CPR_SURFACE, CPR_AIRBORNE, CPR_COARSE } cpr_type_t; +typedef enum { + HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north + HEADING_TRUE, // Heading, degrees clockwise from true north + HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north + HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus + HEADING_TRACK_OR_HEADING // HEADING_GROUND_TRACK or HEADING_REF_DIR depending on the TAH bit in opstatus +} heading_type_t; + typedef enum { COMMB_UNKNOWN, COMMB_EMPTY_RESPONSE, @@ -414,7 +417,7 @@ struct modesMessage { unsigned altitude_valid : 1; unsigned track_valid : 1; unsigned track_rate_valid : 1; - unsigned mag_heading_valid : 1; + unsigned heading_valid : 1; unsigned roll_valid : 1; unsigned gs_valid : 1; unsigned ias_valid : 1; @@ -449,9 +452,9 @@ struct modesMessage { // following fields are valid if the corresponding _valid field is set: int geom_delta; // Difference between geometric and baro alt - float track; // True ground track, degrees (0-359). Reported directly or computed from from EW and NS velocity + float heading; // ground track or heading, degrees (0-359). Reported directly or computed from from EW and NS velocity + heading_type_t heading_type;// how to interpret 'track_or_heading' float track_rate; // Rate of change of track, degrees/second - float mag_heading; // Magnetic heading, degrees (0-359) float roll; // Roll, degrees, negative is left roll unsigned gs; // Groundspeed, kts, reported directly or computed from from EW and NS velocity unsigned ias; // Indicated airspeed, kts @@ -506,8 +509,8 @@ struct modesMessage { unsigned nic_baro : 1; sil_type_t sil_type; - enum { ANGLE_HEADING, ANGLE_TRACK } track_angle; - heading_source_t hrd; + heading_type_t tah; + heading_type_t hrd; unsigned cc_lw; unsigned cc_antenna_offset; diff --git a/mode_s.c b/mode_s.c index 7f087ce..f5c1405 100644 --- a/mode_s.c +++ b/mode_s.c @@ -765,12 +765,13 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) mm->gs_valid = 1; if (mm->gs) { - float heading = atan2(ew_vel, ns_vel) * 180.0 / M_PI; + float ground_track = atan2(ew_vel, ns_vel) * 180.0 / M_PI; // We don't want negative values but a 0-360 scale - if (heading < 0) - heading += 360; - mm->track = heading; - mm->track_valid = 1; + if (ground_track < 0) + ground_track += 360; + mm->heading = ground_track; + mm->heading_type = HEADING_GROUND_TRACK; + mm->heading_valid = 1; } } break; @@ -791,8 +792,9 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) } if (getbit(me, 14)) { - mm->mag_heading_valid = 1; - mm->mag_heading = getbits(me, 15, 24) * 360.0 / 1024.0; + mm->heading_valid = 1; + mm->heading = getbits(me, 15, 24) * 360.0 / 1024.0; + mm->heading_type = HEADING_MAGNETIC_OR_TRUE; } break; } @@ -828,8 +830,9 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf) } if (getbit(me, 13)) { - mm->track_valid = 1; - mm->track = getbits(me, 14, 20) * 360.0 / 128.0; + mm->heading_valid = 1; + mm->heading = getbits(me, 14, 20) * 360.0 / 128.0; + mm->heading_type = HEADING_TRACK_OR_HEADING; } } @@ -1022,12 +1025,12 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) mm->opstatus.nic_supp_a = getbit(me, 44); mm->opstatus.nac_p = getbits(me, 45, 48); mm->opstatus.sil = getbits(me, 51, 52); + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; if (mm->mesub == 0) { mm->opstatus.nic_baro = getbit(me, 53); } else { - mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; + mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd; } - mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; break; case 2: @@ -1064,13 +1067,13 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) mm->opstatus.nic_supp_a = getbit(me, 44); mm->opstatus.nac_p = getbits(me, 45, 48); mm->opstatus.sil = getbits(me, 51, 52); + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; if (mm->mesub == 0) { mm->opstatus.gva = getbits(me, 49, 50); mm->opstatus.nic_baro = getbit(me, 53); } else { - mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING; + mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd; } - mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; break; } @@ -1299,6 +1302,23 @@ static const char *cpr_type_to_string(cpr_type_t type) { } } +static const char *heading_type_to_string(heading_type_t type) { + switch (type) { + case HEADING_GROUND_TRACK: + return "Ground track"; + case HEADING_MAGNETIC: + return "Mag heading"; + case HEADING_TRUE: + return "True heading"; + case HEADING_MAGNETIC_OR_TRUE: + return "Heading"; + case HEADING_TRACK_OR_HEADING: + return "Track/Heading"; + default: + return "unknown heading type"; + } +} + static const char *commb_format_to_string(commb_format_t format) { switch (format) { case COMMB_EMPTY_RESPONSE: @@ -1585,12 +1605,8 @@ void displayModesMessage(struct modesMessage *mm) { mm->geom_delta); } - if (mm->track_valid) { - printf(" Track: %.1f\n", mm->track); - } - - if (mm->mag_heading_valid) { - printf(" Mag heading: %.1f\n", mm->mag_heading); + if (mm->heading_valid) { + printf(" %-13s %.1f\n", heading_type_to_string(mm->heading_type), mm->heading); } if (mm->track_rate_valid) { @@ -1701,8 +1717,8 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->opstatus.nic_baro) printf(" NICbaro: %d\n", mm->opstatus.nic_baro); if (mm->mesub == 1) - printf(" Heading type: %s\n", (mm->opstatus.track_angle == ANGLE_HEADING ? "heading" : "track angle")); - printf(" Heading reference: %s\n", (mm->opstatus.hrd == HEADING_TRUE ? "true north" : "magnetic north")); + printf(" Track/heading: %s\n", heading_type_to_string(mm->opstatus.tah)); + printf(" Heading ref dir: %s\n", heading_type_to_string(mm->opstatus.hrd)); } if (mm->intent.valid) { diff --git a/net_io.c b/net_io.c index b0b9a36..59ea606 100644 --- a/net_io.c +++ b/net_io.c @@ -604,14 +604,14 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { // Field 13 is the ground Speed (if we have it) if (mm->gs_valid) { - p += sprintf(p, ",%d", mm->gs); + p += sprintf(p, ",%u", mm->gs); } else { p += sprintf(p, ","); } // Field 14 is the ground Heading (if we have it) - if (mm->track_valid) { - p += sprintf(p, ",%.0f", mm->track); + if (mm->heading_valid && mm->heading_type == HEADING_GROUND_TRACK) { + p += sprintf(p, ",%.0f", mm->heading); } else { p += sprintf(p, ","); } @@ -1201,6 +1201,10 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, ",\"track\":%.1f", a->track); if (trackDataValid(&a->track_rate_valid)) p += snprintf(p, end-p, ",\"track_rate\":%.2f", a->track_rate); + if (trackDataValid(&a->mag_heading_valid)) + p += snprintf(p, end-p, ",\"mag_heading\":%.1f", a->mag_heading); + if (trackDataValid(&a->true_heading_valid)) + p += snprintf(p, end-p, ",\"true_heading\":%.1f", a->true_heading); if (trackDataValid(&a->gs_valid)) p += snprintf(p, end-p, ",\"gs\":%u", a->gs); if (trackDataValid(&a->ias_valid)) @@ -1821,18 +1825,47 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) typedef enum { TISB_IDENT = 1, TISB_SQUAWK = 2, - TISB_ALTITUDE = 4, - TISB_ALTITUDE_GEOM = 8, + TISB_ALT = 4, + TISB_ALT_GEOM = 8, TISB_GS = 16, TISB_IAS = 32, TISB_TAS = 64, - TISB_POSITION = 128, - TISB_TRACK = 256, - TISB_MAG_HEADING = 512, - TISB_AIRGROUND = 1024, - TISB_CATEGORY = 2048 + TISB_LAT = 128, + TISB_LON = 256, + TISB_TRACK = 512, + TISB_MAG_HEADING = 1024, + TISB_TRUE_HEADING = 2048, + TISB_AIRGROUND = 4096, + TISB_CATEGORY = 8192, + TISB_INTENT_ALT = 16384, + TISB_INTENT_HEADING = 32768, + TISB_ALT_SETTING = 65536 } tisb_flags; +struct { + tisb_flags flag; + const char *name; +} tisb_flag_names[] = { + { TISB_IDENT, "ident" }, + { TISB_SQUAWK, "squawk" }, + { TISB_ALT, "alt" }, + { TISB_ALT_GEOM, "alt_geom" }, + { TISB_GS, "gs" }, + { TISB_IAS, "ias" }, + { TISB_TAS, "tas" }, + { TISB_LAT, "lat" }, + { TISB_LON, "lat" }, + { TISB_TRACK, "track" }, + { TISB_MAG_HEADING, "mag_heading" }, + { TISB_TRUE_HEADING, "true_heading" }, + { TISB_AIRGROUND, "airGround" }, + { TISB_CATEGORY, "category" }, + { TISB_INTENT_ALT, "intent_alt" }, + { TISB_INTENT_HEADING, "intent_heading" }, + { TISB_ALT_SETTING, "alt_setting" }, + { 0, NULL } +}; + static inline unsigned unsigned_difference(unsigned v1, unsigned v2) { return (v1 > v2) ? (v1 - v2) : (v2 - v1); @@ -1887,6 +1920,7 @@ static void writeFATSV() int trackValid = trackDataValidEx(&a->track_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int trackRateValid = trackDataValidEx(&a->track_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B int rollValid = trackDataValidEx(&a->roll_valid, now, 15000, SOURCE_MODE_S); // Comm-B + int trueHeadingValid = trackDataValidEx(&a->true_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int magHeadingValid = trackDataValidEx(&a->mag_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int gsValid = trackDataValidEx(&a->gs_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int iasValid = trackDataValidEx(&a->ias_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES @@ -1921,7 +1955,7 @@ static void writeFATSV() if (geomRateValid && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) { changed = 1; } - if (trackValid && heading_difference(a->track, a->fatsv_emitted_heading) >= 2) { + if (trackValid && heading_difference(a->track, a->fatsv_emitted_track) >= 2) { changed = 1; } if (trackRateValid && fabs(a->track_rate - a->fatsv_emitted_track_rate) >= 0.5) { @@ -1930,7 +1964,10 @@ static void writeFATSV() if (rollValid && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) { changed = 1; } - if (magHeadingValid && heading_difference(a->mag_heading, a->fatsv_emitted_heading_magnetic) >= 2) { + if (magHeadingValid && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) { + changed = 1; + } + if (trueHeadingValid && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) { changed = 1; } if (gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) { @@ -2039,14 +2076,14 @@ static void writeFATSV() p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude); a->fatsv_emitted_altitude = a->altitude; useful = 1; - tisb |= (a->altitude_valid.source == SOURCE_TISB) ? TISB_ALTITUDE : 0; + tisb |= (a->altitude_valid.source == SOURCE_TISB) ? TISB_ALT : 0; } if (altGeomValid && a->altitude_geom_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\talt_geom\t%d", a->altitude_geom); a->fatsv_emitted_altitude_gnss = a->altitude_geom; useful = 1; - tisb |= (a->altitude_geom_valid.source == SOURCE_TISB) ? TISB_ALTITUDE_GEOM : 0; + tisb |= (a->altitude_geom_valid.source == SOURCE_TISB) ? TISB_ALT_GEOM : 0; } if (baroRateValid && a->baro_rate_valid.updated > a->fatsv_last_emitted) { @@ -2091,12 +2128,12 @@ static void writeFATSV() if (positionValid && a->position_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon); useful = 1; - tisb |= (a->position_valid.source == SOURCE_TISB) ? TISB_POSITION : 0; + tisb |= (a->position_valid.source == SOURCE_TISB) ? (TISB_LAT | TISB_LON) : 0; } if (trackValid && a->track_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\ttrack\t%.0f", a->track); - a->fatsv_emitted_heading = a->track; + a->fatsv_emitted_track = a->track; useful = 1; tisb |= (a->track_valid.source == SOURCE_TISB) ? TISB_TRACK : 0; } @@ -2115,11 +2152,18 @@ static void writeFATSV() if (magHeadingValid && a->mag_heading_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tmag_heading\t%.0f", a->mag_heading); - a->fatsv_emitted_heading_magnetic = a->mag_heading; + a->fatsv_emitted_mag_heading = a->mag_heading; useful = 1; tisb |= (a->mag_heading_valid.source == SOURCE_TISB) ? TISB_MAG_HEADING : 0; } + if (trueHeadingValid && a->true_heading_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, bufsize(p,end), "\true_heading\t%.0f", a->true_heading); + a->fatsv_emitted_true_heading = a->true_heading; + useful = 1; + tisb |= (a->true_heading_valid.source == SOURCE_TISB) ? TISB_TRUE_HEADING : 0; + } + if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE) && a->airground_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tairGround\t%s", a->airground == AG_GROUND ? "G+" : "A+"); a->fatsv_emitted_airground = a->airground; @@ -2138,18 +2182,21 @@ static void writeFATSV() p += snprintf(p, bufsize(p,end), "\tintent_alt\t%u", a->intent_altitude); a->fatsv_emitted_intent_altitude = a->intent_altitude; useful = 1; + tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_ALT : 0; } if (intentHeadingValid && a->intent_heading_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tintent_heading\t%.0f", a->intent_heading); a->fatsv_emitted_intent_heading = a->intent_heading; useful = 1; + tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_HEADING : 0; } if (altSettingValid && a->alt_setting_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\talt_setting\t%.1f", a->alt_setting); a->fatsv_emitted_alt_setting = a->alt_setting; useful = 1; + tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_ALT_SETTING : 0; } @@ -2160,7 +2207,12 @@ static void writeFATSV() } if (tisb != 0) { - p += snprintf(p, bufsize(p,end), "\ttisb\t%d", (int)tisb); + p += snprintf(p, bufsize(p,end), "\ttisb\t"); + for (int i = 0; tisb_flag_names[i].name; ++i) { + if (tisb & tisb_flag_names[i].flag) { + p += snprintf(p, bufsize(p,end), "%s ", tisb_flag_names[i].name); + } + } } p += snprintf(p, bufsize(p,end), "\n"); diff --git a/track.c b/track.c index b2ae5dd..f1b9020 100644 --- a/track.c +++ b/track.c @@ -81,6 +81,11 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { a->fatsv_emitted_bds_30[0] = 0x30; a->fatsv_emitted_es_acas_ra[0] = 0xE2; + // defaults until we see an op status message + a->adsb_version = -1; + a->adsb_hrd = HEADING_MAGNETIC; + a->adsb_tah = HEADING_GROUND_TRACK; + // Copy the first message so we can emit it later when a second message arrives. a->first_message = *mm; @@ -543,6 +548,10 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) if (mm->addrtype < a->addrtype) a->addrtype = mm->addrtype; + // if we saw some direct ADS-B for the first time, assume version 0 + if (mm->source == SOURCE_ADSB && a->adsb_version < 0) + a->adsb_version = 0; + if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) { if (a->modeC_hit) { int new_modeC = (a->altitude + 49) / 100; @@ -570,8 +579,21 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->geom_delta = mm->geom_delta; } - if (mm->track_valid && accept_data(&a->track_valid, mm->source, now)) { - a->track = mm->track; + if (mm->heading_valid) { + heading_type_t htype = mm->heading_type; + if (htype == HEADING_MAGNETIC_OR_TRUE) { + htype = a->adsb_hrd; + } else if (htype == HEADING_TRACK_OR_HEADING) { + htype = a->adsb_tah; + } + + if (htype == HEADING_GROUND_TRACK && accept_data(&a->track_valid, mm->source, now)) { + a->track = mm->heading; + } else if (htype == HEADING_MAGNETIC && accept_data(&a->mag_heading_valid, mm->source, now)) { + a->mag_heading = mm->heading; + } else if (htype == HEADING_TRUE && accept_data(&a->true_heading_valid, mm->source, now)) { + a->true_heading = mm->heading; + } } if (mm->track_rate_valid && accept_data(&a->track_rate_valid, mm->source, now)) { @@ -582,10 +604,6 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->roll = mm->roll; } - if (mm->mag_heading_valid && accept_data(&a->mag_heading_valid, mm->source, now)) { - a->mag_heading = mm->mag_heading; - } - if (mm->gs_valid && accept_data(&a->gs_valid, mm->source, now)) { a->gs = mm->gs; } @@ -654,6 +672,15 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->cpr_odd_nuc = mm->cpr_nucp; } + // operational status message + if (mm->opstatus.valid) { + a->adsb_version = mm->opstatus.version; + if (mm->opstatus.version > 0) { + a->adsb_hrd = mm->opstatus.hrd; + a->adsb_tah = mm->opstatus.tah; + } + } + // Now handle derived data // derive geometric altitude if we have baro + delta diff --git a/track.h b/track.h index 82a619c..dadebcf 100644 --- a/track.h +++ b/track.h @@ -118,6 +118,9 @@ struct aircraft { data_validity mag_heading_valid; float mag_heading; // Magnetic heading + data_validity true_heading_valid; + float true_heading; // True heading + data_validity baro_rate_valid; int baro_rate; // Vertical rate (barometric) @@ -158,6 +161,10 @@ struct aircraft { double lat, lon; // Coordinated obtained from CPR encoded data unsigned pos_nuc; // NUCp of last computed position + int adsb_version; // ADS-B version (from ADS-B operational status); -1 means no ADS-B messages seen + heading_type_t adsb_hrd; // Heading Reference Direction setting (from ADS-B operational status) + heading_type_t adsb_tah; // Track Angle / Heading setting (from ADS-B operational status) + int modeA_hit; // did our squawk match a possible mode A reply in the last check period? int modeC_hit; // did our altitude match a possible mode C reply in the last check period? @@ -165,9 +172,10 @@ struct aircraft { int fatsv_emitted_altitude_gnss; // -"- GNSS altitude int fatsv_emitted_baro_rate; // -"- barometric rate int fatsv_emitted_geom_rate; // -"- geometric rate - float fatsv_emitted_heading; // -"- true track - float fatsv_emitted_heading_magnetic; // -"- magnetic heading + float fatsv_emitted_track; // -"- true track float fatsv_emitted_track_rate; // -"- track rate of change + float fatsv_emitted_mag_heading; // -"- magnetic heading + float fatsv_emitted_true_heading; // -"- true heading float fatsv_emitted_roll; // -"- roll angle unsigned fatsv_emitted_speed; // -"- groundspeed unsigned fatsv_emitted_speed_ias; // -"- IAS From c7855c5ab690092bcfacfd1b89029c0b4b732757 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 22:36:23 +0100 Subject: [PATCH 06/52] Feed more data to skyview, basic display updates. --- net_io.c | 18 ++++++++-- public_html/index.html | 56 +++++++++++++++++++++++++++++-- public_html/planeObject.js | 69 +++++++++++++++++++++++++++++--------- public_html/script.js | 27 ++++++++++++--- 4 files changed, 146 insertions(+), 24 deletions(-) diff --git a/net_io.c b/net_io.c index 59ea606..20a7ffd 100644 --- a/net_io.c +++ b/net_io.c @@ -1113,8 +1113,16 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s p += snprintf(p, end-p, "\"altitude\","); if (a->track_valid.source == source) p += snprintf(p, end-p, "\"track\","); + if (a->mag_heading_valid.source == source) + p += snprintf(p, end-p, "\"mag_heading\","); + if (a->true_heading_valid.source == source) + p += snprintf(p, end-p, "\"true_heading\","); if (a->gs_valid.source == source) - p += snprintf(p, end-p, "\"speed\","); + p += snprintf(p, end-p, "\"gs\","); + if (a->ias_valid.source == source) + p += snprintf(p, end-p, "\"ias\","); + if (a->tas_valid.source == source) + p += snprintf(p, end-p, "\"tas\","); if (a->baro_rate_valid.source == source) p += snprintf(p, end-p, "\"baro_rate\","); if (a->geom_rate_valid.source == source) @@ -1191,7 +1199,7 @@ char *generateAircraftJson(const char *url_path, int *len) { if (trackDataValid(&a->altitude_valid)) p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude); if (trackDataValid(&a->altitude_geom_valid)) - p += snprintf(p, end-p, ",\"altitude_geom\":%d", a->altitude_geom); + p += snprintf(p, end-p, ",\"alt_geom\":%d", a->altitude_geom); } if (trackDataValid(&a->baro_rate_valid)) p += snprintf(p, end-p, ",\"baro_rate\":%d", a->baro_rate); @@ -1217,6 +1225,12 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, ",\"roll\":%.1f", a->roll); if (trackDataValid(&a->category_valid)) p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category); + if (trackDataValid(&a->intent_altitude_valid)) + p += snprintf(p, end-p, ",\"intent_alt\":%d", a->intent_altitude); + if (trackDataValid(&a->intent_heading_valid)) + p += snprintf(p, end-p, ",\"intent_heading\":%.1f", a->intent_heading); + if (trackDataValid(&a->alt_setting_valid)) + p += snprintf(p, end-p, ",\"alt_setting\":%.1f", a->alt_setting); p += snprintf(p, end-p, ",\"mlat\":"); p = append_flags(p, end, a, SOURCE_MLAT); diff --git a/public_html/index.html b/public_html/index.html index 1a2acbc..62d5a39 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -170,11 +170,63 @@ - + + +
+
+
Alt (geom):
+
+
Geom rate:
+
+
+
+ +
+
+
Mag heading:
+
+
True heading:
+
+
+
+
Roll:
+
+
Track rate:
+
+
+
+ +
+
+
IAS:
+
+
TAS:
+
+
+
+
Mach:
+
+
+
+ +
+
+
AP alt:
+
+
AP heading:
+
+
+
+
Alt setting:
+
+
+
+ +
AirFrames.org - + diff --git a/public_html/planeObject.js b/public_html/planeObject.js index a0df910..2269174 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -10,9 +10,23 @@ function PlaneObject(icao) { this.category = null; // Basic location information - this.altitude = null; - this.speed = null; - this.track = null; + this.altitude = null; + this.alt_geom = null; + this.gs = null; + this.ias = null; + this.tas = null; + this.track = null; + this.track_rate = null; + this.mag_heading = null; + this.true_heading = null; + this.mach = null; + this.roll = null; + this.intent_alt = null; + this.intent_heading = null; + this.alt_setting = null; + this.baro_rate = null; + this.geom_rate = null; + this.prev_position = null; this.position = null; this.position_from_mlat = false @@ -417,13 +431,44 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { this.addrtype = 'adsb_icao'; if (typeof data.altitude !== "undefined") - this.altitude = data.altitude; - if (typeof data.vert_rate !== "undefined") - this.vert_rate = data.vert_rate; - if (typeof data.speed !== "undefined") - this.speed = data.speed; + this.altitude = data.altitude; + if (typeof data.alt_geom !== "undefined") + this.alt_geom = data.alt_geom; + if (typeof data.gs !== "undefined") + this.gs = data.gs; + if (typeof data.ias !== "undefined") + this.ias = data.ias; + if (typeof data.tas !== "undefined") + this.tas = data.tas; if (typeof data.track !== "undefined") - this.track = data.track; + this.track = data.track; + if (typeof data.track_rate !== "undefined") + this.track_rate = data.track_rate; + if (typeof data.mag_heading !== "undefined") + this.mag_heading = data.mag_heading; + if (typeof data.true_heading !== "undefined") + this.true_heading = data.true_heading; + if (typeof data.mach !== "undefined") + this.mach = data.mach; + if (typeof data.roll !== "undefined") + this.roll = data.roll; + if (typeof data.intent_alt !== "undefined") + this.intent_alt = data.intent_alt; + if (typeof data.intent_heading !== "undefined") + this.intent_heading = data.intent_heading; + if (typeof data.alt_setting !== "undefined") + this.alt_setting = data.alt_setting; + if (typeof data.baro_rate !== "undefined") + this.baro_rate = data.baro_rate; + if (typeof data.geom_rate !== "undefined") + this.geom_rate = data.geom_rate; + if (typeof data.flight !== "undefined") + this.flight = data.flight; + if (typeof data.squawk !== "undefined") + this.squawk = data.squawk; + if (typeof data.category !== "undefined") + this.category = data.category; + if (typeof data.lat !== "undefined") { this.position = [data.lon, data.lat]; this.last_position_time = receiver_timestamp - data.seen_pos; @@ -443,12 +488,6 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { } } } - if (typeof data.flight !== "undefined") - this.flight = data.flight; - if (typeof data.squawk !== "undefined") - this.squawk = data.squawk; - if (typeof data.category !== "undefined") - this.category = data.category; }; PlaneObject.prototype.updateTick = function(receiver_timestamp, last_timestamp) { diff --git a/public_html/script.js b/public_html/script.js index 1075c54..0dac429 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -872,7 +872,7 @@ function refreshSelected() { // emerg.className = 'hidden'; // } - $("#selected_altitude").text(format_altitude_long(selected.altitude, selected.vert_rate, DisplayUnits)); + $("#selected_altitude").text(format_altitude_long(selected.altitude, selected.baro_rate, DisplayUnits)); if (selected.squawk === null || selected.squawk === '0000') { $('#selected_squawk').text('n/a'); @@ -880,8 +880,8 @@ function refreshSelected() { $('#selected_squawk').text(selected.squawk); } - $('#selected_speed').text(format_speed_long(selected.speed, DisplayUnits)); - $('#selected_vertical_rate').text(format_vert_rate_long(selected.vert_rate, DisplayUnits)); + $('#selected_speed').text(format_speed_long(selected.gs, DisplayUnits)); + $('#selected_vertical_rate').text(format_vert_rate_long(selected.baro_rate, DisplayUnits)); $('#selected_icao').text(selected.icao.toUpperCase()); $('#airframes_post_icao').attr('value',selected.icao); $('#selected_track').text(format_track_long(selected.track)); @@ -933,6 +933,23 @@ function refreshSelected() { $('#selected_rssi').text(selected.rssi.toFixed(1) + ' dBFS'); $('#selected_message_count').text(selected.messages); $('#selected_photo_link').html(getFlightAwarePhotoLink(selected.registration)); + + $('#selected_alt_geom').text(format_altitude_long(selected.alt_geom, selected.geom_rate, DisplayUnits)); + $('#selected_mag_heading').text(format_track_long(selected.mag_heading)); + $('#selected_true_heading').text(format_track_long(selected.true_heading)); + $('#selected_ias').text(format_speed_long(selected.ias, DisplayUnits)); + $('#selected_tas').text(format_speed_long(selected.tas, DisplayUnits)); + $('#selected_mach').text(selected.mach.toFixed(3)); + $('#selected_roll').text(selected.roll.toFixed(2)); + $('#selected_track_rate').text(selected.track_rate.toFixed(2)); + $('#selected_geom_rate').text(format_vert_rate_long(selected.geom_rate, DisplayUnits)); + if (selected.alt_setting == null) { + $('#selected_alt_setting').text("n/a"); + } else { + $('#selected_alt_setting').text(selected.alt_setting.toFixed(1) + " hPa"); + } + $('#selected_intent_alt').text(format_altitude_long(selected.intent_alt, 0, DisplayUnits)); + $('#selected_intent_heading').text(format_track_long(selected.intent_heading)) } function refreshHighlighted() { @@ -964,9 +981,9 @@ function refreshHighlighted() { } - $('#highlighted_speed').text(format_speed_long(highlighted.speed, DisplayUnits)); + $('#highlighted_speed').text(format_speed_long(highlighted.gs, DisplayUnits)); - $("#highlighted_altitude").text(format_altitude_long(highlighted.altitude, highlighted.vert_rate, DisplayUnits)); + $("#highlighted_altitude").text(format_altitude_long(highlighted.altitude, highlighted.baro_rate, DisplayUnits)); $('#highlighted_icao').text(highlighted.icao.toUpperCase()); From 17c8265ce8ad6606b83fe1241d4b978abbcc78c3 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 22:41:32 +0100 Subject: [PATCH 07/52] Fix a few more things that were broken by the field renaming. --- public_html/script.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public_html/script.js b/public_html/script.js index 0dac429..a6d8c69 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -1052,9 +1052,9 @@ function refreshTableInfo() { tableplane.tr.cells[3].textContent = (tableplane.registration !== null ? tableplane.registration : ""); tableplane.tr.cells[4].textContent = (tableplane.icaotype !== null ? tableplane.icaotype : ""); tableplane.tr.cells[5].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); - tableplane.tr.cells[6].innerHTML = format_altitude_brief(tableplane.altitude, tableplane.vert_rate, DisplayUnits); - tableplane.tr.cells[7].textContent = format_speed_brief(tableplane.speed, DisplayUnits); - tableplane.tr.cells[8].textContent = format_vert_rate_brief(tableplane.vert_rate, DisplayUnits); + tableplane.tr.cells[6].innerHTML = format_altitude_brief(tableplane.altitude, tableplane.baro_rate, DisplayUnits); + tableplane.tr.cells[7].textContent = format_speed_brief(tableplane.gs, DisplayUnits); + tableplane.tr.cells[8].textContent = format_vert_rate_brief(tableplane.baro_rate, DisplayUnits); tableplane.tr.cells[9].textContent = format_distance_brief(tableplane.sitedist, DisplayUnits); tableplane.tr.cells[10].textContent = format_track_brief(tableplane.track); tableplane.tr.cells[11].textContent = tableplane.messages; @@ -1104,8 +1104,8 @@ function sortByRegistration() { sortBy('registration', compareAlpha, func function sortByAircraftType() { sortBy('icaotype', compareAlpha, function(x) { return x.icaotype; }); } function sortBySquawk() { sortBy('squawk', compareAlpha, function(x) { return x.squawk; }); } function sortByAltitude() { sortBy('altitude',compareNumeric, function(x) { return (x.altitude == "ground" ? -1e9 : x.altitude); }); } -function sortBySpeed() { sortBy('speed', compareNumeric, function(x) { return x.speed; }); } -function sortByVerticalRate() { sortBy('vert_rate', compareNumeric, function(x) { return x.vert_rate; }); } +function sortBySpeed() { sortBy('speed', compareNumeric, function(x) { return x.gs; }); } +function sortByVerticalRate() { sortBy('vert_rate', compareNumeric, function(x) { return x.baro_rate; }); } function sortByDistance() { sortBy('sitedist',compareNumeric, function(x) { return x.sitedist; }); } function sortByTrack() { sortBy('track', compareNumeric, function(x) { return x.track; }); } function sortByMsgs() { sortBy('msgs', compareNumeric, function(x) { return x.messages; }); } From e3c8c91852eb22eabd21a0695e3a4ef585087afc Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 15 Jun 2017 23:42:05 +0100 Subject: [PATCH 08/52] Emit adsb version in json & faup1090. Show it in skyview. --- net_io.c | 6 ++++++ public_html/index.html | 2 ++ public_html/planeObject.js | 3 +++ public_html/script.js | 30 +++++++++++++++++++++++++++--- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/net_io.c b/net_io.c index 20a7ffd..d72ce6e 100644 --- a/net_io.c +++ b/net_io.c @@ -1187,6 +1187,8 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); if (a->addrtype != ADDR_ADSB_ICAO) p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_short_string(a->addrtype)); + if (a->adsb_version >= 0) + p += snprintf(p, end-p, ",\"version\":%d", a->adsb_version); if (trackDataValid(&a->squawk_valid)) p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk); if (trackDataValid(&a->callsign_valid)) @@ -2055,6 +2057,10 @@ static void writeFATSV() p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(a->addrtype)); } + if (a->adsb_version >= 0) { + p += snprintf(p, bufsize(p, end), "\tadsb_version\t%d", a->adsb_version); + } + if (trackDataValidEx(&a->callsign_valid, now, 35000, SOURCE_MODE_S) && strcmp(a->callsign, " ") != 0 && a->callsign_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tident\t%s", a->callsign); memcpy(a->fatsv_emitted_callsign, a->callsign, sizeof(a->fatsv_emitted_callsign)); diff --git a/public_html/index.html b/public_html/index.html index 62d5a39..e479197 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -219,6 +219,8 @@
Alt setting:
+
ADS-B:
+
diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 2269174..1d6eace 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -26,6 +26,7 @@ function PlaneObject(icao) { this.alt_setting = null; this.baro_rate = null; this.geom_rate = null; + this.version = null; this.prev_position = null; this.position = null; @@ -468,6 +469,8 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { this.squawk = data.squawk; if (typeof data.category !== "undefined") this.category = data.category; + if (typeof data.version !== "undefined") + this.version = data.version; if (typeof data.lat !== "undefined") { this.position = [data.lon, data.lat]; diff --git a/public_html/script.js b/public_html/script.js index a6d8c69..c6d2cc8 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -939,9 +939,21 @@ function refreshSelected() { $('#selected_true_heading').text(format_track_long(selected.true_heading)); $('#selected_ias').text(format_speed_long(selected.ias, DisplayUnits)); $('#selected_tas').text(format_speed_long(selected.tas, DisplayUnits)); - $('#selected_mach').text(selected.mach.toFixed(3)); - $('#selected_roll').text(selected.roll.toFixed(2)); - $('#selected_track_rate').text(selected.track_rate.toFixed(2)); + if (selected.mach == null) { + $('#selected_mach').text('n/a'); + } else { + $('#selected_mach').text(selected.mach.toFixed(3)); + } + if (selected.roll == null) { + $('#selected_roll').text('n/a'); + } else { + $('#selected_roll').text(selected.roll.toFixed(1)); + } + if (selected.track_rate == null) { + $('#selected_track_rate').text('n/a'); + } else { + $('#selected_track_rate').text(selected.track_rate.toFixed(2)); + } $('#selected_geom_rate').text(format_vert_rate_long(selected.geom_rate, DisplayUnits)); if (selected.alt_setting == null) { $('#selected_alt_setting').text("n/a"); @@ -950,6 +962,18 @@ function refreshSelected() { } $('#selected_intent_alt').text(format_altitude_long(selected.intent_alt, 0, DisplayUnits)); $('#selected_intent_heading').text(format_track_long(selected.intent_heading)) + + if (selected.version == null) { + $('#selected_version').text('none'); + } else if (selected.version == 0) { + $('#selected_version').text('v0 (DO-260)'); + } else if (selected.version == 1) { + $('#selected_version').text('v1 (DO-260A)'); + } else if (selected.version == 2) { + $('#selected_version').text('v2 (DO-260B)'); + } else { + $('#selected_version').text('v' + selected.version); + } } function refreshHighlighted() { From 382554a250fc57098ed7f72e841eab56aacf3fa5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Jun 2017 10:39:01 +0100 Subject: [PATCH 09/52] Tweak tracking of AP modes, emit in json & faup1090 output --- comm_b.c | 8 +++++--- dump1090.h | 16 ++++++++++------ mode_s.c | 52 +++++++++++++++++++++++++++++++++------------------- net_io.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- track.c | 4 ++++ track.h | 4 ++++ 6 files changed, 106 insertions(+), 29 deletions(-) diff --git a/comm_b.c b/comm_b.c index 80f9022..a5f1bfe 100644 --- a/comm_b.c +++ b/comm_b.c @@ -383,9 +383,11 @@ static int decodeBDS40(struct modesMessage *mm, bool store) } if (mode_valid) { - mm->intent.mode_vnav = (mode_raw & 4) ? 1 : 0; - mm->intent.mode_alt_hold = (mode_raw & 2) ? 1 : 0; - mm->intent.mode_approach = (mode_raw & 1) ? 1 : 0; + mm->intent.modes_valid = 1; + mm->intent.modes = + ((mode_raw & 4) ? INTENT_MODE_VNAV : 0) | + ((mode_raw & 2) ? INTENT_MODE_ALT_HOLD : 0) | + ((mode_raw & 1) ? INTENT_MODE_APPROACH : 0); } if (source_valid) { diff --git a/dump1090.h b/dump1090.h index 7da9819..b0fbc09 100644 --- a/dump1090.h +++ b/dump1090.h @@ -205,6 +205,14 @@ typedef enum { COMMB_HEADING_SPEED } commb_format_t; +typedef enum { + INTENT_MODE_AUTOPILOT = 1, + INTENT_MODE_VNAV = 2, + INTENT_MODE_ALT_HOLD = 4, + INTENT_MODE_APPROACH = 8, + INTENT_MODE_LNAV = 16 +} intent_modes_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -526,6 +534,7 @@ struct modesMessage { unsigned fms_altitude_valid : 1; unsigned mcp_altitude_valid : 1; unsigned alt_setting_valid : 1; + unsigned modes_valid : 1; float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended) unsigned fms_altitude; // FMS selected altitude @@ -534,12 +543,7 @@ struct modesMessage { enum { INTENT_ALT_INVALID, INTENT_ALT_UNKNOWN, INTENT_ALT_AIRCRAFT, INTENT_ALT_MCP, INTENT_ALT_FMS } altitude_source; - unsigned mode_autopilot : 1; // Autopilot engaged - unsigned mode_vnav : 1; // Vertical Navigation Mode active - unsigned mode_alt_hold : 1; // Altitude Hold Mode active - unsigned mode_approach : 1; // Approach Mode active - unsigned mode_lnav : 1; // Lateral Navigation Mode active - + intent_modes_t modes; } intent; }; diff --git a/mode_s.c b/mode_s.c index f5c1405..4ace63e 100644 --- a/mode_s.c +++ b/mode_s.c @@ -968,13 +968,15 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) // 45-46: SIL if (getbit(me, 47)) { - mm->intent.mode_autopilot = getbit(me, 48); - mm->intent.mode_vnav = getbit(me, 49); - mm->intent.mode_alt_hold = getbit(me, 50); - // 51: IMF - mm->intent.mode_approach = getbit(me, 52); - // 53: TCAS operational - mm->intent.mode_lnav = getbit(me, 54); + mm->intent.modes_valid = 1; + mm->intent.modes = + (getbit(me, 48) ? INTENT_MODE_AUTOPILOT : 0) | + (getbit(me, 49) ? INTENT_MODE_VNAV : 0) | + (getbit(me, 50) ? INTENT_MODE_ALT_HOLD : 0) | + // 51: IMF + (getbit(me, 52) ? INTENT_MODE_APPROACH : 0) | + // 53: TCAS operational + (getbit(me, 54) ? INTENT_MODE_LNAV : 0); } // 55-56 reserved @@ -1342,6 +1344,28 @@ static const char *commb_format_to_string(commb_format_t format) { } } +static const char *intent_modes_to_string(intent_modes_t flags) +{ + static char buf[128]; + + buf[0] = 0; + if (flags & INTENT_MODE_AUTOPILOT) + strcat(buf, "autopilot "); + if (flags & INTENT_MODE_VNAV) + strcat(buf, "vnav "); + if (flags & INTENT_MODE_ALT_HOLD) + strcat(buf, "althold "); + if (flags & INTENT_MODE_APPROACH) + strcat(buf, "approach "); + if (flags & INTENT_MODE_LNAV) + strcat(buf, "lnav "); + + if (buf[0] != 0) + buf[strlen(buf)-1] = 0; + + return buf; +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -1749,18 +1773,8 @@ void displayModesMessage(struct modesMessage *mm) { } } - if (mm->intent.mode_autopilot || - mm->intent.mode_vnav || - mm->intent.mode_alt_hold || - mm->intent.mode_approach || - mm->intent.mode_lnav) { - printf(" Active modes: "); - if (mm->intent.mode_autopilot) printf("autopilot "); - if (mm->intent.mode_vnav) printf("VNAV "); - if (mm->intent.mode_alt_hold) printf("altitude-hold "); - if (mm->intent.mode_approach) printf("approach "); - if (mm->intent.mode_lnav) printf("LNAV "); - printf("\n"); + if (mm->intent.modes_valid) { + printf(" Active modes: %s\n", intent_modes_to_string(mm->intent.modes)); } } diff --git a/net_io.c b/net_io.c index d72ce6e..4119d19 100644 --- a/net_io.c +++ b/net_io.c @@ -1135,6 +1135,37 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s return p; } +static struct { + intent_modes_t flag; + const char *name; +} intent_modes_names[] = { + { INTENT_MODE_AUTOPILOT, "autopilot" }, + { INTENT_MODE_VNAV, "vnav" }, + { INTENT_MODE_ALT_HOLD, "althold" }, + { INTENT_MODE_APPROACH, "approach" }, + { INTENT_MODE_LNAV, "lnav" }, + { 0, NULL } +}; + +static char *append_intent_modes(char *p, char *end, intent_modes_t flags, const char *quote, const char *sep) +{ + int first = 1; + for (int i = 0; intent_modes_names[i].name; ++i) { + if (!(flags & intent_modes_names[i].flag)) { + continue; + } + + if (!first) { + p += snprintf(p, end-p, "%s", sep); + } + + first = 0; + p += snprintf(p, end-p, "%s%s%s", quote, intent_modes_names[i].name, quote); + } + + return p; +} + static const char *addrtype_short_string(addrtype_t type) { switch (type) { case ADDR_ADSB_ICAO: @@ -1231,6 +1262,11 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, ",\"intent_alt\":%d", a->intent_altitude); if (trackDataValid(&a->intent_heading_valid)) p += snprintf(p, end-p, ",\"intent_heading\":%.1f", a->intent_heading); + if (trackDataValid(&a->intent_modes_valid)) { + p += snprintf(p, end-p, ",\"intent_modes\":["); + p = append_intent_modes(p, end, a->intent_modes, "\"", ","); + p += snprintf(p, end-p, "]"); + } if (trackDataValid(&a->alt_setting_valid)) p += snprintf(p, end-p, ",\"alt_setting\":%.1f", a->alt_setting); @@ -1855,7 +1891,8 @@ typedef enum { TISB_CATEGORY = 8192, TISB_INTENT_ALT = 16384, TISB_INTENT_HEADING = 32768, - TISB_ALT_SETTING = 65536 + TISB_ALT_SETTING = 65536, + TISB_INTENT_MODES = 131072 } tisb_flags; struct { @@ -1879,6 +1916,7 @@ struct { { TISB_INTENT_ALT, "intent_alt" }, { TISB_INTENT_HEADING, "intent_heading" }, { TISB_ALT_SETTING, "alt_setting" }, + { TISB_INTENT_MODES, "intent_modes" }, { 0, NULL } }; @@ -1945,6 +1983,7 @@ static void writeFATSV() int categoryValid = trackDataValidEx(&a->category_valid, now, 15000, SOURCE_MODE_S_CHECKED); int intentAltValid = trackDataValidEx(&a->intent_altitude_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int intentHeadingValid = trackDataValidEx(&a->intent_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + int intentModesValid = trackDataValidEx(&a->intent_modes_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int altSettingValid = trackDataValidEx(&a->alt_setting_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES int callsignValid = trackDataValidEx(&a->callsign_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES @@ -2004,6 +2043,9 @@ static void writeFATSV() if (intentHeadingValid && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) { changed = immediate = 1; } + if (intentModesValid && a->intent_modes != a->fatsv_emitted_intent_modes) { + changed = immediate = 1; + } if (altSettingValid && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) { // 0.8 is the ES message resolution changed = immediate = 1; } @@ -2212,6 +2254,13 @@ static void writeFATSV() tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_HEADING : 0; } + if (intentModesValid && a->intent_modes_valid.updated > a->fatsv_last_emitted) { + p += snprintf(p, end-p, "\tintent_modes\t"); + p = append_intent_modes(p, end, a->intent_modes, "", " "); + a->fatsv_emitted_intent_modes = a->intent_modes; + tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_MODES : 0; + } + if (altSettingValid && a->alt_setting_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\talt_setting\t%.1f", a->alt_setting); a->fatsv_emitted_alt_setting = a->alt_setting; diff --git a/track.c b/track.c index f1b9020..2c094c6 100644 --- a/track.c +++ b/track.c @@ -652,6 +652,10 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->intent_heading = mm->intent.heading; } + if (mm->intent.modes_valid && accept_data(&a->intent_modes_valid, mm->source, now)) { + a->intent_modes = mm->intent.modes; + } + if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source, now)) { a->alt_setting = mm->intent.alt_setting; } diff --git a/track.h b/track.h index dadebcf..c03063f 100644 --- a/track.h +++ b/track.h @@ -145,6 +145,9 @@ struct aircraft { data_validity intent_heading_valid; float intent_heading; // intent heading, degrees (0-359) + data_validity intent_modes_valid; + intent_modes_t intent_modes; // enabled modes (autopilot, vnav, etc) + data_validity cpr_odd_valid; // Last seen even CPR message cpr_type_t cpr_odd_type; unsigned cpr_odd_lat; @@ -184,6 +187,7 @@ struct aircraft { airground_t fatsv_emitted_airground; // -"- air/ground state unsigned fatsv_emitted_intent_altitude; // -"- intent altitude float fatsv_emitted_intent_heading; // -"- intent heading + intent_modes_t fatsv_emitted_intent_modes; // -"- enabled modes float fatsv_emitted_alt_setting; // -"- altimeter setting unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message From 8a52108d773a3e98f11fbf3ee60714208edfcc40 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Jun 2017 10:39:57 +0100 Subject: [PATCH 10/52] Clean up js-side data capture, fix naming on ground track / speed. --- public_html/index.html | 4 +-- public_html/planeObject.js | 63 ++++++++++++-------------------------- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/public_html/index.html b/public_html/index.html index e479197..6a99d14 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -103,7 +103,7 @@
- Speed: + Groundspeed:
n/a @@ -127,7 +127,7 @@
- Heading: + Ground track:
n/a diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 1d6eace..684a3f0 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -425,54 +425,31 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { this.messages = data.messages; this.rssi = data.rssi; this.last_message_time = receiver_timestamp - data.seen; + + // simple fields + + var fields = ["altitude", "alt_geom", "gs", "ias", "tas", "track", + "track_rate", "mag_heading", "true_heading", "mach", + "roll", "intent_alt", "intent_heading", + "alt_setting", "baro_rate", "geom_rate", "flight", + "squawk", "category", "version"]; + + for (var i = 0; i < fields.length; ++i) { + if (fields[i] in data) { + this[fields[i]] = data[fields[i]]; + } else { + this[fields[i]] = null; + } + } + + // fields with more complex behaviour - if (typeof data.type !== "undefined") + if ('type' in data) this.addrtype = data.type; else this.addrtype = 'adsb_icao'; - if (typeof data.altitude !== "undefined") - this.altitude = data.altitude; - if (typeof data.alt_geom !== "undefined") - this.alt_geom = data.alt_geom; - if (typeof data.gs !== "undefined") - this.gs = data.gs; - if (typeof data.ias !== "undefined") - this.ias = data.ias; - if (typeof data.tas !== "undefined") - this.tas = data.tas; - if (typeof data.track !== "undefined") - this.track = data.track; - if (typeof data.track_rate !== "undefined") - this.track_rate = data.track_rate; - if (typeof data.mag_heading !== "undefined") - this.mag_heading = data.mag_heading; - if (typeof data.true_heading !== "undefined") - this.true_heading = data.true_heading; - if (typeof data.mach !== "undefined") - this.mach = data.mach; - if (typeof data.roll !== "undefined") - this.roll = data.roll; - if (typeof data.intent_alt !== "undefined") - this.intent_alt = data.intent_alt; - if (typeof data.intent_heading !== "undefined") - this.intent_heading = data.intent_heading; - if (typeof data.alt_setting !== "undefined") - this.alt_setting = data.alt_setting; - if (typeof data.baro_rate !== "undefined") - this.baro_rate = data.baro_rate; - if (typeof data.geom_rate !== "undefined") - this.geom_rate = data.geom_rate; - if (typeof data.flight !== "undefined") - this.flight = data.flight; - if (typeof data.squawk !== "undefined") - this.squawk = data.squawk; - if (typeof data.category !== "undefined") - this.category = data.category; - if (typeof data.version !== "undefined") - this.version = data.version; - - if (typeof data.lat !== "undefined") { + if ('lat' in data && 'lon' in data) { this.position = [data.lon, data.lat]; this.last_position_time = receiver_timestamp - data.seen_pos; From f45a7d61a5878de831f542709b1343118bcc6902 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Jun 2017 10:40:22 +0100 Subject: [PATCH 11/52] Include AP modes in info window. --- public_html/index.html | 10 ++++++++-- public_html/planeObject.js | 3 ++- public_html/script.js | 7 ++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/public_html/index.html b/public_html/index.html index 6a99d14..2c60ce1 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -217,8 +217,14 @@
-
Alt setting:
-
+
AP modes:
+
+
Alt setting:
+
+
+
+
+
ADS-B:
diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 684a3f0..906de1e 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -23,6 +23,7 @@ function PlaneObject(icao) { this.roll = null; this.intent_alt = null; this.intent_heading = null; + this.intent_modes = null; this.alt_setting = null; this.baro_rate = null; this.geom_rate = null; @@ -430,7 +431,7 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { var fields = ["altitude", "alt_geom", "gs", "ias", "tas", "track", "track_rate", "mag_heading", "true_heading", "mach", - "roll", "intent_alt", "intent_heading", + "roll", "intent_alt", "intent_heading", "intent_modes", "alt_setting", "baro_rate", "geom_rate", "flight", "squawk", "category", "version"]; diff --git a/public_html/script.js b/public_html/script.js index c6d2cc8..2dea753 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -961,7 +961,12 @@ function refreshSelected() { $('#selected_alt_setting').text(selected.alt_setting.toFixed(1) + " hPa"); } $('#selected_intent_alt').text(format_altitude_long(selected.intent_alt, 0, DisplayUnits)); - $('#selected_intent_heading').text(format_track_long(selected.intent_heading)) + $('#selected_intent_heading').text(format_track_long(selected.intent_heading)); + if (selected.intent_modes == null) { + $('#selected_intent_modes').text("n/a"); + } else { + $('#selected_intent_modes').text(selected.intent_modes.join()); + } if (selected.version == null) { $('#selected_version').text('none'); From 2f53b57278d121e517eb1f8421e7287d3e5718b5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Jun 2017 10:56:11 +0100 Subject: [PATCH 12/52] Fix typo in BDS6,0 scoring --- comm_b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comm_b.c b/comm_b.c index a5f1bfe..371308b 100644 --- a/comm_b.c +++ b/comm_b.c @@ -698,7 +698,7 @@ static int decodeBDS60(struct modesMessage *mm, bool store) int delta = abs(baro_rate - inertial_rate); if (delta < 500) { score += 5; - } else if (delta > 200) { + } else if (delta > 2000) { score -= 5; } } From c6d7f1ec6f7b3c5a7d7a994a65419cccc7ee99f1 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Jun 2017 11:42:41 +0100 Subject: [PATCH 13/52] Expire tracked true_heading, intent_modes --- track.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/track.c b/track.c index 2c094c6..55ef7bc 100644 --- a/track.c +++ b/track.c @@ -824,6 +824,7 @@ static void trackRemoveStaleAircraft(uint64_t now) EXPIRE(track_rate); EXPIRE(roll); EXPIRE(mag_heading); + EXPIRE(true_heading); EXPIRE(baro_rate); EXPIRE(geom_rate); EXPIRE(squawk); @@ -832,6 +833,7 @@ static void trackRemoveStaleAircraft(uint64_t now) EXPIRE(alt_setting); EXPIRE(intent_altitude); EXPIRE(intent_heading); + EXPIRE(intent_modes); EXPIRE(cpr_odd); EXPIRE(cpr_even); EXPIRE(position); From 4baae970ae30ada4fe500ed69713dfcc304e9d01 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Jun 2017 13:30:30 +0100 Subject: [PATCH 14/52] Retain callsign even after it disappears from json. --- public_html/planeObject.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 906de1e..b24e9b3 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -432,7 +432,7 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { var fields = ["altitude", "alt_geom", "gs", "ias", "tas", "track", "track_rate", "mag_heading", "true_heading", "mach", "roll", "intent_alt", "intent_heading", "intent_modes", - "alt_setting", "baro_rate", "geom_rate", "flight", + "alt_setting", "baro_rate", "geom_rate", "squawk", "category", "version"]; for (var i = 0; i < fields.length; ++i) { @@ -450,6 +450,10 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { else this.addrtype = 'adsb_icao'; + // don't expire callsigns + if ('flight' in data) + this.flight = data.flight; + if ('lat' in data && 'lon' in data) { this.position = [data.lon, data.lat]; this.last_position_time = receiver_timestamp - data.seen_pos; From 634d80668611543a42fabc012f976f9acc4dcbe9 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 19 Jun 2017 11:12:23 -0500 Subject: [PATCH 15/52] Version bump to 3.6.0~dev --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index c32eaa1..78721eb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-fa (3.6.0~dev) UNRELEASED; urgency=medium + + * In development + + -- Oliver Jowett Mon, 19 Jun 2017 11:11:59 -0500 + dump1090-fa (3.5.0) stable; urgency=medium * SkyView map UI added. From a3cdd47d8090fdd6eb0331f699ca7133551cd0d1 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 19 Jun 2017 11:27:14 -0500 Subject: [PATCH 16/52] ADSBv2 uses bit 15 of BDS1,0 --- comm_b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comm_b.c b/comm_b.c index 371308b..079b594 100644 --- a/comm_b.c +++ b/comm_b.c @@ -97,7 +97,7 @@ static int decodeBDS10(struct modesMessage *mm, bool store) } // Reserved bits - if (getbits(msg, 10, 15) != 0) { + if (getbits(msg, 10, 14) != 0) { return 0; } From 6986b3847fb7d2037a3f9ae43fda9d68d828aea4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 2 Dec 2017 17:38:33 +0000 Subject: [PATCH 17/52] WIP on new faup1090 output, data aging, refactoring --- comm_b.c | 19 +- demod_2400.c | 8 +- dump1090.c | 2 +- dump1090.h | 4 +- mode_s.c | 4 +- net_io.c | 548 ++++++++++++++++++-------------------------------- sdr_bladerf.c | 9 +- sdr_ifile.c | 2 +- sdr_rtlsdr.c | 6 +- track.c | 133 +++++++----- track.h | 39 ++-- util.c | 7 + util.h | 9 + 13 files changed, 335 insertions(+), 455 deletions(-) diff --git a/comm_b.c b/comm_b.c index 079b594..9ac9676 100644 --- a/comm_b.c +++ b/comm_b.c @@ -281,6 +281,9 @@ static int decodeBDS40(struct modesMessage *mm, bool store) mcp_alt = mcp_raw * 16; if (mcp_alt >= 1000 && mcp_alt <= 50000) { score += 13; + } else { + // unlikely altitude + score -= 2; } } else if (!mcp_valid && mcp_raw == 0) { score += 1; @@ -293,6 +296,9 @@ static int decodeBDS40(struct modesMessage *mm, bool store) fms_alt = fms_raw * 16; if (fms_alt >= 1000 && fms_alt <= 50000) { score += 13; + } else { + // unlikely altitude + score -= 2; } } else if (!fms_valid && fms_raw == 0) { score += 1; @@ -305,6 +311,9 @@ static int decodeBDS40(struct modesMessage *mm, bool store) baro_setting = 800 + baro_raw * 0.1; if (baro_setting >= 900 && baro_setting <= 1100) { score += 13; + } else { + // unlikely pressure setting + score -= 2; } } else if (!baro_valid && baro_raw == 0) { score += 1; @@ -685,14 +694,8 @@ static int decodeBDS60(struct modesMessage *mm, bool store) } // small bonuses for consistent data - if (ias_valid && mach_valid) { - double delta = fabs(ias / 666.0 - mach); - if (delta < 0.1) { - score += 5; - } else if (delta > 0.25) { - score -= 5; - } - } + + // Should check IAS vs Mach at given altitude, but the maths is a little involved if (baro_rate_valid && inertial_rate_valid) { int delta = abs(baro_rate - inertial_rate); diff --git a/demod_2400.c b/demod_2400.c index 006cd2e..6c0fcbd 100644 --- a/demod_2400.c +++ b/demod_2400.c @@ -311,9 +311,7 @@ void demodulate2400(struct mag_buf *mag) mm.timestampMsg = mag->sampleTimestamp + j*5 + (8 + 56) * 12 + bestphase; // compute message receive time as block-start-time + difference in the 12MHz clock - mm.sysTimestampMsg = mag->sysTimestamp; // start of block time - mm.sysTimestampMsg.tv_nsec += receiveclock_ns_elapsed(mag->sampleTimestamp, mm.timestampMsg); - normalize_timespec(&mm.sysTimestampMsg); + mm.sysTimestampMsg = mag->sysTimestamp + receiveclock_ms_elapsed(mag->sampleTimestamp, mm.timestampMsg); mm.score = bestscore; @@ -646,9 +644,7 @@ void demodulate2400AC(struct mag_buf *mag) mm.timestampMsg = mag->sampleTimestamp + f2_clock / 5; // 60MHz -> 12MHz // compute message receive time as block-start-time + difference in the 12MHz clock - mm.sysTimestampMsg = mag->sysTimestamp; // start of block time - mm.sysTimestampMsg.tv_nsec += receiveclock_ns_elapsed(mag->sampleTimestamp, mm.timestampMsg); - normalize_timespec(&mm.sysTimestampMsg); + mm.sysTimestampMsg = mag->sysTimestamp + receiveclock_ms_elapsed(mag->sampleTimestamp, mm.timestampMsg); decodeModeAMessage(&mm, modeac); diff --git a/dump1090.c b/dump1090.c index 314b85a..eae1223 100644 --- a/dump1090.c +++ b/dump1090.c @@ -381,7 +381,7 @@ void backgroundTasks(void) { } // always update end time so it is current when requests arrive - Modes.stats_current.end = now; + Modes.stats_current.end = mstime(); if (now >= next_stats_update) { int i; diff --git a/dump1090.h b/dump1090.h index b0fbc09..fe95ec2 100644 --- a/dump1090.h +++ b/dump1090.h @@ -272,7 +272,7 @@ struct mag_buf { uint16_t *data; // Magnitude data. Starts with Modes.trailing_samples worth of overlap from the previous block unsigned length; // Number of valid samples _after_ overlap. Total buffer length is buf->length + Modes.trailing_samples. uint64_t sampleTimestamp; // Clock timestamp of the start of this block, 12MHz clock - struct timespec sysTimestamp; // Estimated system time at start of block + uint64_t sysTimestamp; // Estimated system time at start of block uint32_t dropped; // Number of dropped samples preceding this buffer double mean_level; // Mean of normalized (0..1) signal level double mean_power; // Mean of normalized (0..1) power level @@ -392,7 +392,7 @@ struct modesMessage { uint32_t addr; // Address Announced addrtype_t addrtype; // address format / source uint64_t timestampMsg; // Timestamp of the message (12MHz clock) - struct timespec sysTimestampMsg; // Timestamp of the message (system time) + uint64_t sysTimestampMsg; // Timestamp of the message (system time) int remote; // If set this message is from a remote station double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power int score; // Scoring from scoreModesMessage, if used diff --git a/mode_s.c b/mode_s.c index 4ace63e..2f13af4 100644 --- a/mode_s.c +++ b/mode_s.c @@ -674,7 +674,6 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) return 0; } - static void decodeESIdentAndCategory(struct modesMessage *mm) { // Aircraft Identification and Category @@ -690,7 +689,8 @@ static void decodeESIdentAndCategory(struct modesMessage *mm) mm->callsign[5] = ais_charset[getbits(me, 39, 44)]; mm->callsign[6] = ais_charset[getbits(me, 45, 50)]; mm->callsign[7] = ais_charset[getbits(me, 51, 56)]; - + mm->callsign[8] = 0; + // A common failure mode seems to be to intermittently send // all zeros. Catch that here. mm->callsign_valid = (strcmp(mm->callsign, "@@@@@@@@") != 0); diff --git a/net_io.c b/net_io.c index 4119d19..61d2f61 100644 --- a/net_io.c +++ b/net_io.c @@ -53,6 +53,7 @@ #include #include +#include // // ============================= Networking ============================= @@ -565,11 +566,12 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { localtime_r(&now.tv_sec, &stTime_now); // Find message reception time - localtime_r(&mm->sysTimestampMsg.tv_sec, &stTime_receive); + time_t received = (time_t) (mm->sysTimestampMsg / 1000); + localtime_r(&received, &stTime_receive); // Fields 7 & 8 are the message reception time and date p += sprintf(p, "%04d/%02d/%02d,", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday); - p += sprintf(p, "%02d:%02d:%02d.%03u,", stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned) (mm->sysTimestampMsg.tv_nsec / 1000000U)); + p += sprintf(p, "%02d:%02d:%02d.%03u,", stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, (unsigned) (mm->sysTimestampMsg / 1000)); // Fields 9 & 10 are the current time and date p += sprintf(p, "%04d/%02d/%02d,", (stTime_now.tm_year+1900),(stTime_now.tm_mon+1), stTime_now.tm_mday); @@ -920,7 +922,7 @@ static int decodeBinMessage(struct client *c, char *p) { } // record reception time as the time we read it. - clock_gettime(CLOCK_REALTIME, &mm.sysTimestampMsg); + mm.sysTimestampMsg = mstime(); ch = *p++; // Grab the signal level mm.signalLevel = ((unsigned char)ch / 255.0); @@ -1048,7 +1050,7 @@ static int decodeHexMessage(struct client *c, char *hex) { } // record reception time as the time we read it. - clock_gettime(CLOCK_REALTIME, &mm.sysTimestampMsg); + mm.sysTimestampMsg = mstime(); if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC Modes.stats_current.remote_received_modeac++; @@ -1166,6 +1168,13 @@ static char *append_intent_modes(char *p, char *end, intent_modes_t flags, const return p; } +static const char *intent_modes_string(intent_modes_t flags) { + static char buf[256]; + buf[0] = 0; + append_intent_modes(buf, buf + sizeof(buf), flags, "", " "); + return buf; +} + static const char *addrtype_short_string(addrtype_t type) { switch (type) { case ADDR_ADSB_ICAO: @@ -1748,6 +1757,35 @@ static void modesReadFromClient(struct client *c) { } } +static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap) +{ + p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); + return p; +} + +static char *safe_snprintf(char *p, char *end, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); + va_end(ap); + return p; +} + + +static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + + p = safe_snprintf(p, end, "%s\t", field); + p = safe_vsnprintf(p, end, format, ap); + p = safe_snprintf(p, end, "\t"); + + va_end(ap); + return p; +} + #define TSV_MAX_PACKET_SIZE 400 static void writeFATSVPositionUpdate(float lat, float lon, float alt) @@ -1766,22 +1804,20 @@ static void writeFATSVPositionUpdate(float lat, float lon, float alt) return; char *end = p + TSV_MAX_PACKET_SIZE; -# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) - p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, mstime() / 1000); - p += snprintf(p, bufsize(p, end), "\ttype\t%s", "location_update"); - p += snprintf(p, bufsize(p, end), "\tlat\t%.5f", lat); - p += snprintf(p, bufsize(p, end), "\tlon\t%.5f", lon); - p += snprintf(p, bufsize(p, end), "\talt\t%.0f", alt); - p += snprintf(p, bufsize(p, end), "\taltref\t%s", "egm96_meters"); - p += snprintf(p, bufsize(p, end), "\n"); + p = appendFATSV(p, end, "clock", "%" PRIu64, messageNow() / 1000); + p = appendFATSV(p, end, "type", "%s", "location_update"); + p = appendFATSV(p, end, "lat", "%.5f", lat); + p = appendFATSV(p, end, "lon", "%.5f", lon); + p = appendFATSV(p, end, "alt", "%.0f", alt); + p = appendFATSV(p, end, "altref", "%s", "egm96_meters"); + --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)); - -# undef bufsize } static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafield, unsigned char *data, size_t len) @@ -1791,26 +1827,18 @@ static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafiel return; char *end = p + TSV_MAX_PACKET_SIZE; -# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) - - p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, mstime() / 1000); - - if (mm->addr & MODES_NON_ICAO_ADDRESS) { - p += snprintf(p, bufsize(p, end), "\totherid\t%06X", mm->addr & 0xFFFFFF); - } else { - p += snprintf(p, bufsize(p, end), "\thexid\t%06X", mm->addr); - } + 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) { - p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(mm->addrtype)); + p = appendFATSV(p, end, "addrtype", "%s", addrtype_short_string(mm->addrtype)); } - p += snprintf(p, bufsize(p, end), "\t%s\t", datafield); + p = safe_snprintf(p, end, "%s\t", datafield); for (size_t i = 0; i < len; ++i) { - p += snprintf(p, bufsize(p, end), "%02X", data[i]); + p = safe_snprintf(p, end, "%02X", data[i]); } - - p += snprintf(p, bufsize(p, end), "\n"); + p = safe_snprintf(p, end, "\n"); if (p <= end) completeWrite(&Modes.fatsv_out, p); @@ -1874,52 +1902,6 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) } } -typedef enum { - TISB_IDENT = 1, - TISB_SQUAWK = 2, - TISB_ALT = 4, - TISB_ALT_GEOM = 8, - TISB_GS = 16, - TISB_IAS = 32, - TISB_TAS = 64, - TISB_LAT = 128, - TISB_LON = 256, - TISB_TRACK = 512, - TISB_MAG_HEADING = 1024, - TISB_TRUE_HEADING = 2048, - TISB_AIRGROUND = 4096, - TISB_CATEGORY = 8192, - TISB_INTENT_ALT = 16384, - TISB_INTENT_HEADING = 32768, - TISB_ALT_SETTING = 65536, - TISB_INTENT_MODES = 131072 -} tisb_flags; - -struct { - tisb_flags flag; - const char *name; -} tisb_flag_names[] = { - { TISB_IDENT, "ident" }, - { TISB_SQUAWK, "squawk" }, - { TISB_ALT, "alt" }, - { TISB_ALT_GEOM, "alt_geom" }, - { TISB_GS, "gs" }, - { TISB_IAS, "ias" }, - { TISB_TAS, "tas" }, - { TISB_LAT, "lat" }, - { TISB_LON, "lat" }, - { TISB_TRACK, "track" }, - { TISB_MAG_HEADING, "mag_heading" }, - { TISB_TRUE_HEADING, "true_heading" }, - { TISB_AIRGROUND, "airGround" }, - { TISB_CATEGORY, "category" }, - { TISB_INTENT_ALT, "intent_alt" }, - { TISB_INTENT_HEADING, "intent_heading" }, - { TISB_ALT_SETTING, "alt_setting" }, - { TISB_INTENT_MODES, "intent_modes" }, - { 0, NULL } -}; - static inline unsigned unsigned_difference(unsigned v1, unsigned v2) { return (v1 > v2) ? (v1 - v2) : (v2 - v1); @@ -1931,17 +1913,69 @@ static inline float heading_difference(float h1, float h2) return (d < 180) ? d : (360 - d); } +static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircraft *a, const data_validity *source, const char *format, ...) +{ + const char *sourcetype; + switch (source->source) { + case SOURCE_MODE_S: + sourcetype = "U"; + break; + case SOURCE_MODE_S_CHECKED: + sourcetype = "S"; + break; + case SOURCE_TISB: + sourcetype = "T"; + break; + case SOURCE_ADSB: + sourcetype = "A"; + break; + default: + // don't want to forward data sourced from these + return p; + } + + if (!trackDataValid(source)) { + // expired data + return p; + } + + if (source->updated > messageNow()) { + // data in the future + return p; + } + + if (source->updated < a->fatsv_last_emitted) { + // not updated since last time + return p; + } + + uint64_t age = (messageNow() - source->updated) / 1000; + if (age > 255) { + // too old + return p; + } + + p = safe_snprintf(p, end, "%s\t", field); + + va_list ap; + va_start(ap, format); + p = safe_vsnprintf(p, end, format, ap); + va_end(ap); + + p = safe_snprintf(p, end, " %" PRIu64 " %s\t", age, sourcetype); + return p; +} + static void writeFATSV() { struct aircraft *a; - uint64_t now; static uint64_t next_update; if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) { return; // not enabled or no active connections } - now = mstime(); + uint64_t now = mstime(); if (now < next_update) { return; } @@ -1949,14 +1983,9 @@ static void writeFATSV() // scan once a second at most next_update = now + 1000; + // Pretend we are "processing a message" so the validity checks work as expected + _messageNow = now; for (a = Modes.aircrafts; a; a = a->next) { - uint64_t minAge; - - int useful = 0; - tisb_flags tisb = 0; - - char *p, *end; - if (a->messages < 2) // basic filter for bad decodes continue; @@ -1965,27 +1994,12 @@ static void writeFATSV() continue; } - int altValid = trackDataValidEx(&a->altitude_valid, now, 15000, SOURCE_MODE_S); // for non-ADS-B transponders, DF0/4/16/20 are the only sources of altitude data - int altGeomValid = trackDataValidEx(&a->altitude_geom_valid, now, 15000, SOURCE_MODE_S_CHECKED); - int airgroundValid = trackDataValidEx(&a->airground_valid, now, 15000, SOURCE_MODE_S_CHECKED); // for non-ADS-B transponders, only trust DF11 CA field - int baroRateValid = trackDataValidEx(&a->baro_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int geomRateValid = trackDataValidEx(&a->geom_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int positionValid = trackDataValidEx(&a->position_valid, now, 15000, SOURCE_MODE_S_CHECKED); - int trackValid = trackDataValidEx(&a->track_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int trackRateValid = trackDataValidEx(&a->track_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B - int rollValid = trackDataValidEx(&a->roll_valid, now, 15000, SOURCE_MODE_S); // Comm-B - int trueHeadingValid = trackDataValidEx(&a->true_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int magHeadingValid = trackDataValidEx(&a->mag_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int gsValid = trackDataValidEx(&a->gs_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int iasValid = trackDataValidEx(&a->ias_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int tasValid = trackDataValidEx(&a->tas_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int machValid = trackDataValidEx(&a->mach_valid, now, 15000, SOURCE_MODE_S); // Comm-B - int categoryValid = trackDataValidEx(&a->category_valid, now, 15000, SOURCE_MODE_S_CHECKED); - int intentAltValid = trackDataValidEx(&a->intent_altitude_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int intentHeadingValid = trackDataValidEx(&a->intent_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int intentModesValid = trackDataValidEx(&a->intent_modes_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int altSettingValid = trackDataValidEx(&a->alt_setting_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES - int callsignValid = trackDataValidEx(&a->callsign_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES + // some special cases: + int altValid = trackDataValid(&a->altitude_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 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 @@ -1996,68 +2010,31 @@ static void writeFATSV() // if it hasn't changed altitude, heading, or speed much, // don't update so often - int changed = 0; - int immediate = 0; - if (altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) { - changed = 1; - } - if (altGeomValid && abs(a->altitude_geom - a->fatsv_emitted_altitude_gnss) >= 50) { - changed = 1; - } - if (baroRateValid && abs(a->baro_rate - a->fatsv_emitted_baro_rate) > 500) { - changed = 1; - } - if (geomRateValid && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) { - changed = 1; - } - if (trackValid && heading_difference(a->track, a->fatsv_emitted_track) >= 2) { - changed = 1; - } - if (trackRateValid && fabs(a->track_rate - a->fatsv_emitted_track_rate) >= 0.5) { - changed = 1; - } - if (rollValid && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) { - changed = 1; - } - if (magHeadingValid && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) { - changed = 1; - } - if (trueHeadingValid && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) { - changed = 1; - } - if (gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) { - changed = 1; - } - if (iasValid && unsigned_difference(a->ias, a->fatsv_emitted_speed_ias) >= 25) { - changed = 1; - } - if (tasValid && unsigned_difference(a->tas, a->fatsv_emitted_speed_tas) >= 25) { - changed = 1; - } - if (machValid && fabs(a->mach - a->fatsv_emitted_mach) >= 0.02) { - changed = 1; - } - if (intentAltValid && unsigned_difference(a->intent_altitude, a->fatsv_emitted_intent_altitude) > 50) { - changed = immediate = 1; - } - if (intentHeadingValid && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) { - changed = immediate = 1; - } - if (intentModesValid && a->intent_modes != a->fatsv_emitted_intent_modes) { - changed = immediate = 1; - } - if (altSettingValid && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) { // 0.8 is the ES message resolution - changed = immediate = 1; - } - if (callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) { - changed = immediate = 1; - } - if (airgroundValid && ((a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || - (a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE))) { - // Air-ground transition, handle it immediately. - changed = immediate = 1; - } + int changed = + (altValid && abs(a->altitude - a->fatsv_emitted_altitude) >= 50) || + (trackDataValid(&a->altitude_geom_valid) && abs(a->altitude_geom - a->fatsv_emitted_altitude_gnss) >= 50) || + (trackDataValid(&a->baro_rate_valid) && abs(a->baro_rate - a->fatsv_emitted_baro_rate) > 500) || + (trackDataValid(&a->geom_rate_valid) && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) || + (trackDataValid(&a->track_valid) && heading_difference(a->track, a->fatsv_emitted_track) >= 2) || + (trackDataValid(&a->track_rate_valid) && fabs(a->track_rate - a->fatsv_emitted_track_rate) >= 0.5) || + (trackDataValid(&a->roll_valid) && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) || + (trackDataValid(&a->mag_heading_valid) && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) || + (trackDataValid(&a->true_heading_valid) && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) || + (gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) || + (trackDataValid(&a->ias_valid) && unsigned_difference(a->ias, a->fatsv_emitted_speed_ias) >= 25) || + (trackDataValid(&a->tas_valid) && unsigned_difference(a->tas, a->fatsv_emitted_speed_tas) >= 25) || + (trackDataValid(&a->mach_valid) && fabs(a->mach - a->fatsv_emitted_mach) >= 0.02); + int immediate = + (trackDataValid(&a->intent_altitude_valid) && unsigned_difference(a->intent_altitude, a->fatsv_emitted_intent_altitude) > 50) || + (trackDataValid(&a->intent_heading_valid) && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) || + (trackDataValid(&a->intent_modes_valid) && a->intent_modes != a->fatsv_emitted_intent_modes) || + (trackDataValid(&a->alt_setting_valid) && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) || // 0.8 is the ES message resolution + (callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) || + (airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || + (airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE); + + uint64_t minAge; if (immediate) { // a change we want to emit right away minAge = 0; @@ -2080,218 +2057,89 @@ static void writeFATSV() if ((now - a->fatsv_last_emitted) < minAge) continue; - p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); + char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); if (!p) return; + char *end = p + TSV_MAX_PACKET_SIZE; - end = p + TSV_MAX_PACKET_SIZE; -# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) - - p += snprintf(p, bufsize(p, end), "clock\t%" PRIu64, (uint64_t)(a->seen / 1000)); - - if (a->addr & MODES_NON_ICAO_ADDRESS) { - p += snprintf(p, bufsize(p, end), "\totherid\t%06X", a->addr & 0xFFFFFF); - } else { - p += snprintf(p, bufsize(p, end), "\thexid\t%06X", a->addr); - } + p = appendFATSV(p, end, "clock", "%" PRIu64, messageNow() / 1000); + p = appendFATSV(p, end, (a->addr & MODES_NON_ICAO_ADDRESS) ? "otherid" : "hexid", "%06X", a->addr & 0xFFFFFF); if (a->addrtype != ADDR_ADSB_ICAO) { - p += snprintf(p, bufsize(p, end), "\taddrtype\t%s", addrtype_short_string(a->addrtype)); + p = appendFATSV(p, end, "addrtype", "%s", addrtype_short_string(a->addrtype)); } if (a->adsb_version >= 0) { - p += snprintf(p, bufsize(p, end), "\tadsb_version\t%d", a->adsb_version); - } - - if (trackDataValidEx(&a->callsign_valid, now, 35000, SOURCE_MODE_S) && strcmp(a->callsign, " ") != 0 && a->callsign_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tident\t%s", a->callsign); - memcpy(a->fatsv_emitted_callsign, a->callsign, sizeof(a->fatsv_emitted_callsign)); - switch (a->callsign_valid.source) { - case SOURCE_MODE_S: - p += snprintf(p, bufsize(p,end), "\tiSource\tmodes"); - break; - case SOURCE_ADSB: - p += snprintf(p, bufsize(p,end), "\tiSource\tadsb"); - break; - case SOURCE_TISB: - p += snprintf(p, bufsize(p,end), "\tiSource\ttisb"); - break; - default: - p += snprintf(p, bufsize(p,end), "\tiSource\tunknown"); - break; - } - - useful = 1; - tisb |= (a->callsign_valid.source == SOURCE_TISB) ? TISB_IDENT : 0; - } - - if (trackDataValidEx(&a->squawk_valid, now, 35000, SOURCE_MODE_S) && a->squawk_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->squawk); - useful = 1; - tisb |= (a->squawk_valid.source == SOURCE_TISB) ? TISB_SQUAWK : 0; + p = appendFATSV(p, end, "adsbVer", "%d", a->adsb_version); } // only emit alt, speed, latlon, track etc if they have been received since the last time // and are not stale - if (altValid && a->altitude_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude); - a->fatsv_emitted_altitude = a->altitude; - useful = 1; - tisb |= (a->altitude_valid.source == SOURCE_TISB) ? TISB_ALT : 0; - } + char *dataStart = p; - if (altGeomValid && a->altitude_geom_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\talt_geom\t%d", a->altitude_geom); - a->fatsv_emitted_altitude_gnss = a->altitude_geom; - useful = 1; - tisb |= (a->altitude_geom_valid.source == SOURCE_TISB) ? TISB_ALT_GEOM : 0; - } - - if (baroRateValid && a->baro_rate_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tbaro_rate\t%d", a->baro_rate); - a->fatsv_emitted_baro_rate = a->baro_rate; - useful = 1; - } - - if (geomRateValid && a->geom_rate_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tgeom_rate\t%d", a->geom_rate); - a->fatsv_emitted_geom_rate = a->geom_rate; - useful = 1; - } - - if (gsValid && a->gs_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tgs\t%u", a->gs); - a->fatsv_emitted_speed = a->gs; - useful = 1; - tisb |= (a->gs_valid.source == SOURCE_TISB) ? TISB_GS : 0; - } - - if (iasValid && a->ias_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tias\t%u", a->ias); - a->fatsv_emitted_speed_ias = a->ias; - useful = 1; - tisb |= (a->ias_valid.source == SOURCE_TISB) ? TISB_IAS : 0; - } - - if (tasValid && a->tas_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\ttas\t%u", a->tas); - a->fatsv_emitted_speed_tas = a->tas; - useful = 1; - tisb |= (a->tas_valid.source == SOURCE_TISB) ? TISB_TAS : 0; - } - - if (machValid && a->mach_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tmach\t%.3f", a->mach); - a->fatsv_emitted_mach = a->mach; - useful = 1; - } - - if (positionValid && a->position_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon); - useful = 1; - tisb |= (a->position_valid.source == SOURCE_TISB) ? (TISB_LAT | TISB_LON) : 0; - } - - if (trackValid && a->track_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\ttrack\t%.0f", a->track); - a->fatsv_emitted_track = a->track; - useful = 1; - tisb |= (a->track_valid.source == SOURCE_TISB) ? TISB_TRACK : 0; - } - - if (trackRateValid && a->track_rate_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\ttrack_rate\t%.2f", a->track_rate); - a->fatsv_emitted_track_rate = a->track_rate; - useful = 1; - } - - if (rollValid && a->roll_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\troll\t%.1f", a->roll); - a->fatsv_emitted_roll = a->roll; - useful = 1; - } - - if (magHeadingValid && a->mag_heading_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tmag_heading\t%.0f", a->mag_heading); - a->fatsv_emitted_mag_heading = a->mag_heading; - useful = 1; - tisb |= (a->mag_heading_valid.source == SOURCE_TISB) ? TISB_MAG_HEADING : 0; - } - - if (trueHeadingValid && a->true_heading_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\true_heading\t%.0f", a->true_heading); - a->fatsv_emitted_true_heading = a->true_heading; - useful = 1; - tisb |= (a->true_heading_valid.source == SOURCE_TISB) ? TISB_TRUE_HEADING : 0; - } - - if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE) && a->airground_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tairGround\t%s", a->airground == AG_GROUND ? "G+" : "A+"); - a->fatsv_emitted_airground = a->airground; - useful = 1; - tisb |= (a->airground_valid.source == SOURCE_TISB) ? TISB_AIRGROUND : 0; - } - - if (categoryValid && (a->category & 0xF0) != 0xA0 && a->category_valid.updated > a->fatsv_last_emitted) { - // interesting category, not a regular aircraft - p += snprintf(p, bufsize(p,end), "\tcategory\t%02X", a->category); - useful = 1; - tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_CATEGORY : 0; - } - - if (intentAltValid && a->intent_altitude_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tintent_alt\t%u", a->intent_altitude); - a->fatsv_emitted_intent_altitude = a->intent_altitude; - useful = 1; - tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_ALT : 0; - } - - if (intentHeadingValid && a->intent_heading_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\tintent_heading\t%.0f", a->intent_heading); - a->fatsv_emitted_intent_heading = a->intent_heading; - useful = 1; - tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_HEADING : 0; - } - - if (intentModesValid && a->intent_modes_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, end-p, "\tintent_modes\t"); - p = append_intent_modes(p, end, a->intent_modes, "", " "); - a->fatsv_emitted_intent_modes = a->intent_modes; - tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_MODES : 0; - } - - if (altSettingValid && a->alt_setting_valid.updated > a->fatsv_last_emitted) { - p += snprintf(p, bufsize(p,end), "\talt_setting\t%.1f", a->alt_setting); - a->fatsv_emitted_alt_setting = a->alt_setting; - useful = 1; - tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_ALT_SETTING : 0; - } + // special cases + if (altValid) + p = appendFATSVMeta(p, end, "alt", a, &a->altitude_valid, "%d", a->altitude); + if (airgroundValid) + p = appendFATSVMeta(p, end, "ag", a, &a->airground_valid, "%s", a->airground == AG_GROUND ? "G+" : "A+"); + if (strcmp(a->callsign, " ") != 0) + p = appendFATSVMeta(p, end, "ident", a, &a->callsign_valid, "{%s}", a->callsign); + if (positionValid) + p = appendFATSVMeta(p, end, "pos", a, &a->position_valid, "{%.5f %.5f}", a->lat, a->lon); + p = appendFATSVMeta(p, end, "squawk", a, &a->squawk_valid, "%04x", a->squawk); + p = appendFATSVMeta(p, end, "altGeo", 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, "vrateGeo", a, &a->geom_rate_valid, "%d", a->geom_rate); + p = appendFATSVMeta(p, end, "gs", a, &a->gs_valid, "%u", a->gs); + p = appendFATSVMeta(p, end, "ias", a, &a->ias_valid, "%u", a->ias); + p = appendFATSVMeta(p, end, "tas", a, &a->tas_valid, "%u", a->tas); + p = appendFATSVMeta(p, end, "mach", a, &a->mach_valid, "%.3f", a->mach); + p = appendFATSVMeta(p, end, "trk", a, &a->track_valid, "%.0f", a->track); + p = appendFATSVMeta(p, end, "trkRate", 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, "hdgMag", a, &a->mag_heading_valid, "%.0f", a->mag_heading); + p = appendFATSVMeta(p, end, "hdgTrue", a, &a->true_heading_valid, "%.0f", a->true_heading); + if (a->category != 0xA0) + p = appendFATSVMeta(p, end, "category", a, &a->category_valid, "%02X", a->category); + p = appendFATSVMeta(p, end, "selAlt", a, &a->intent_altitude_valid,"%u", a->intent_altitude); + p = appendFATSVMeta(p, end, "selHdg", a, &a->intent_heading_valid, "%.0f", a->intent_heading); + p = appendFATSVMeta(p, end, "selModes", a, &a->intent_modes_valid, "{%s}", intent_modes_string(a->intent_modes)); + p = appendFATSVMeta(p, end, "qnh", a, &a->alt_setting_valid, "%.1f", a->alt_setting); // if we didn't get anything interesting, bail out. // We don't need to do anything special to unwind prepareWrite(). - if (!useful) { + if (p == dataStart) { continue; } - if (tisb != 0) { - p += snprintf(p, bufsize(p,end), "\ttisb\t"); - for (int i = 0; tisb_flag_names[i].name; ++i) { - if (tisb & tisb_flag_names[i].flag) { - p += snprintf(p, bufsize(p,end), "%s ", tisb_flag_names[i].name); - } - } - } - - p += snprintf(p, bufsize(p,end), "\n"); + --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)); -# undef bufsize + a->fatsv_emitted_altitude = a->altitude; + a->fatsv_emitted_altitude_gnss = a->altitude_geom; + a->fatsv_emitted_baro_rate = a->baro_rate; + a->fatsv_emitted_geom_rate = a->geom_rate; + a->fatsv_emitted_speed = a->gs; + a->fatsv_emitted_speed_ias = a->ias; + 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_intent_altitude = a->intent_altitude; + a->fatsv_emitted_intent_heading = a->intent_heading; + a->fatsv_emitted_intent_modes = a->intent_modes; + a->fatsv_emitted_alt_setting = a->alt_setting; + memcpy(a->fatsv_emitted_callsign, a->callsign, sizeof(a->fatsv_emitted_callsign)); a->fatsv_last_emitted = now; } } diff --git a/sdr_bladerf.c b/sdr_bladerf.c index c849b14..8577b9d 100644 --- a/sdr_bladerf.c +++ b/sdr_bladerf.c @@ -311,8 +311,7 @@ static void *handle_bladerf_samples(struct bladerf *dev, MODES_NOTUSED(num_samples); // record initial time for later sys timestamp calculation - struct timespec entryTimestamp; - clock_gettime(CLOCK_REALTIME, &entryTimestamp); + uint64_t entryTimestamp = mstime(); pthread_mutex_lock(&Modes.data_mutex); if (Modes.exit) { @@ -413,10 +412,8 @@ static void *handle_bladerf_samples(struct bladerf *dev, if (blocks_processed) { // Get the approx system time for the start of this block - unsigned block_duration = 1e9 * outbuf->length / Modes.sample_rate; - outbuf->sysTimestamp = entryTimestamp; - outbuf->sysTimestamp.tv_nsec -= block_duration; - normalize_timespec(&outbuf->sysTimestamp); + unsigned block_duration = 1e3 * outbuf->length / Modes.sample_rate; + outbuf->sysTimestamp = entryTimestamp - block_duration; outbuf->mean_level /= blocks_processed; outbuf->mean_power /= blocks_processed; diff --git a/sdr_ifile.c b/sdr_ifile.c index 771c36e..f615071 100644 --- a/sdr_ifile.c +++ b/sdr_ifile.c @@ -217,7 +217,7 @@ void ifileRun() } // Get the system time for the start of this block - clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); + outbuf->sysTimestamp = mstime(); toread = MODES_MAG_BUF_SAMPLES * ifile.bytes_per_sample; r = ifile.readbuf; diff --git a/sdr_rtlsdr.c b/sdr_rtlsdr.c index 0feb992..979ba38 100644 --- a/sdr_rtlsdr.c +++ b/sdr_rtlsdr.c @@ -313,12 +313,10 @@ void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { // Compute the sample timestamp and system timestamp for the start of the block outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate; sampleCounter += slen; - block_duration = 1e9 * slen / Modes.sample_rate; // Get the approx system time for the start of this block - clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); - outbuf->sysTimestamp.tv_nsec -= block_duration; - normalize_timespec(&outbuf->sysTimestamp); + block_duration = 1e3 * slen / Modes.sample_rate; + outbuf->sysTimestamp = mstime() - block_duration; // Copy trailing data from last block (or reset if not valid) if (outbuf->dropped == 0) { diff --git a/track.c b/track.c index 55ef7bc..2e39a23 100644 --- a/track.c +++ b/track.c @@ -89,6 +89,34 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { // Copy the first message so we can emit it later when a second message arrives. a->first_message = *mm; + // initialize data validity ages +#define F(f,s,e) do { a->f##_valid.stale_interval = (s) * 1000; a->f##_valid.expire_interval = (e) * 1000; } while (0) + F(callsign, 60, 70); // ADS-B or Comm-B + F(altitude, 15, 70); // ADS-B or Mode S + F(altitude_geom, 60, 70); // ADS-B only + F(geom_delta, 60, 70); // ADS-B only + F(gs, 60, 70); // ADS-B or Comm-B + F(ias, 60, 70); // ADS-B (rare) or Comm-B + F(tas, 60, 70); // ADS-B (rare) or Comm-B + F(mach, 60, 70); // Comm-B only + F(track, 60, 70); // ADS-B or Comm-B + F(track_rate, 60, 70); // Comm-B only + F(roll, 60, 70); // Comm-B only + F(mag_heading, 60, 70); // ADS-B (rare) or Comm-B + F(true_heading, 60, 70); // ADS-B only (rare) + F(baro_rate, 60, 70); // ADS-B or Comm-B + F(geom_rate, 60, 70); // ADS-B or Comm-B + F(squawk, 15, 70); // ADS-B or Mode S + F(category, 60, 70); // ADS-B only + F(airground, 15, 70); // ADS-B or Mode S + F(alt_setting, 60, 70); // Comm-B only + F(intent_altitude, 60, 70); // ADS-B or Comm-B + F(intent_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, 70); // ADS-B only +#undef F + Modes.stats_current.unique_aircraft++; return (a); @@ -112,15 +140,18 @@ struct aircraft *trackFindAircraft(uint32_t addr) { // Should we accept some new data from the given source? // If so, update the validity and return 1 -static int accept_data(data_validity *d, datasource_t source, uint64_t now) +static int accept_data(data_validity *d, datasource_t source) { - if (source < d->source && now < d->stale) + if (messageNow() < d->updated) + return 0; + + if (source < d->source && messageNow() < d->stale) return 0; d->source = source; - d->updated = now; - d->stale = now + 60000; - d->expires = now + 70000; + d->updated = messageNow(); + d->stale = messageNow() + d->stale_interval; + d->expires = messageNow() + d->expire_interval; return 1; } @@ -142,10 +173,10 @@ static void combine_validity(data_validity *to, const data_validity *from1, cons to->expires = (from1->expires < from2->expires) ? from1->expires : from2->expires; // the earlier of the two expiry times } -static int compare_validity(const data_validity *lhs, const data_validity *rhs, uint64_t now) { - if (now < lhs->stale && lhs->source > rhs->source) +static int compare_validity(const data_validity *lhs, const data_validity *rhs) { + if (messageNow() < lhs->stale && lhs->source > rhs->source) return 1; - else if (now < rhs->stale && lhs->source < rhs->source) + else if (messageNow() < rhs->stale && lhs->source < rhs->source) return -1; else if (lhs->updated > rhs->updated) return 1; @@ -201,7 +232,7 @@ static void update_range_histogram(double lat, double lon) // return true if it's OK for the aircraft to have travelled from its last known position // to a new position at (lat,lon,surface) at a time of now. -static int speed_check(struct aircraft *a, double lat, double lon, uint64_t now, int surface) +static int speed_check(struct aircraft *a, double lat, double lon, int surface) { uint64_t elapsed; double distance; @@ -212,7 +243,7 @@ static int speed_check(struct aircraft *a, double lat, double lon, uint64_t now, if (!trackDataValid(&a->position_valid)) return 1; // no reference, assume OK - elapsed = trackDataAge(&a->position_valid, now); + elapsed = trackDataAge(&a->position_valid); if (trackDataValid(&a->gs_valid)) speed = a->gs; @@ -256,7 +287,7 @@ static int speed_check(struct aircraft *a, double lat, double lon, uint64_t now, return inrange; } -static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double *lat, double *lon, unsigned *nuc) +static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nuc) { int result; int fflag = mm->cpr_odd; @@ -269,7 +300,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now // find reference location double reflat, reflon; - if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID)) { // Ok to try aircraft relative first + if (trackDataValid(&a->position_valid)) { // Ok to try aircraft relative first reflat = a->lat; reflon = a->lon; if (a->pos_nuc < *nuc) @@ -325,7 +356,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now return result; // check speed limit - if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, now, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, surface)) { Modes.stats_current.cpr_global_speed_checks++; return -2; } @@ -333,7 +364,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now return result; } -static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double *lat, double *lon, unsigned *nuc) +static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nuc) { // relative CPR // find reference location @@ -345,7 +376,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, *nuc = mm->cpr_nucp; - if (trackDataValidEx(&a->position_valid, now, 50000, SOURCE_INVALID)) { + if (trackDataValid(&a->position_valid)) { reflat = a->lat; reflon = a->lon; @@ -399,7 +430,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, } // check speed limit - if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, now, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, surface)) { #ifdef DEBUG_CPR_CHECKS fprintf(stderr, "Speed check for %06X with local decoding failed\n", a->addr); #endif @@ -418,7 +449,7 @@ static uint64_t time_between(uint64_t t1, uint64_t t2) return t2 - t1; } -static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t now) +static void updatePosition(struct aircraft *a, struct modesMessage *mm) { int location_result = -1; uint64_t max_elapsed; @@ -449,7 +480,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t a->cpr_odd_type == a->cpr_even_type && time_between(a->cpr_odd_valid.updated, a->cpr_even_valid.updated) <= max_elapsed) { - location_result = doGlobalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc); + location_result = doGlobalCPR(a, mm, &new_lat, &new_lon, &new_nuc); if (location_result == -2) { #ifdef DEBUG_CPR_CHECKS @@ -480,7 +511,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t // Otherwise try relative CPR. if (location_result == -1) { - location_result = doLocalCPR(a, mm, now, &new_lat, &new_lon, &new_nuc); + location_result = doLocalCPR(a, mm, &new_lat, &new_lon, &new_nuc); if (location_result < 0) { Modes.stats_current.cpr_local_skipped++; @@ -527,8 +558,8 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) return NULL; } - uint64_t now = mstime(); - + _messageNow = mm->sysTimestampMsg; + // Lookup our aircraft or create a new one a = trackFindAircraft(mm->addr); if (!a) { // If it's a currently unknown aircraft.... @@ -541,7 +572,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->signalLevel[a->signalNext] = mm->signalLevel; a->signalNext = (a->signalNext + 1) & 7; } - a->seen = now; + a->seen = messageNow(); a->messages++; // update addrtype, we only ever go towards "more direct" types @@ -552,7 +583,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) if (mm->source == SOURCE_ADSB && a->adsb_version < 0) a->adsb_version = 0; - if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) { + if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source)) { if (a->modeC_hit) { int new_modeC = (a->altitude + 49) / 100; int old_modeC = (mm->altitude + 49) / 100; @@ -564,18 +595,18 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->altitude = mm->altitude; } - if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source, now)) { + if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source)) { if (mm->squawk != a->squawk) { a->modeA_hit = 0; } a->squawk = mm->squawk; } - if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GEOM && accept_data(&a->altitude_geom_valid, mm->source, now)) { + if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GEOM && accept_data(&a->altitude_geom_valid, mm->source)) { a->altitude_geom = mm->altitude; } - if (mm->geom_delta_valid && accept_data(&a->geom_delta_valid, mm->source, now)) { + if (mm->geom_delta_valid && accept_data(&a->geom_delta_valid, mm->source)) { a->geom_delta = mm->geom_delta; } @@ -587,81 +618,81 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) htype = a->adsb_tah; } - if (htype == HEADING_GROUND_TRACK && accept_data(&a->track_valid, mm->source, now)) { + if (htype == HEADING_GROUND_TRACK && accept_data(&a->track_valid, mm->source)) { a->track = mm->heading; - } else if (htype == HEADING_MAGNETIC && accept_data(&a->mag_heading_valid, mm->source, now)) { + } else if (htype == HEADING_MAGNETIC && accept_data(&a->mag_heading_valid, mm->source)) { a->mag_heading = mm->heading; - } else if (htype == HEADING_TRUE && accept_data(&a->true_heading_valid, mm->source, now)) { + } else if (htype == HEADING_TRUE && accept_data(&a->true_heading_valid, mm->source)) { a->true_heading = mm->heading; } } - if (mm->track_rate_valid && accept_data(&a->track_rate_valid, mm->source, now)) { + if (mm->track_rate_valid && accept_data(&a->track_rate_valid, mm->source)) { a->track_rate = mm->track_rate; } - if (mm->roll_valid && accept_data(&a->roll_valid, mm->source, now)) { + if (mm->roll_valid && accept_data(&a->roll_valid, mm->source)) { a->roll = mm->roll; } - if (mm->gs_valid && accept_data(&a->gs_valid, mm->source, now)) { + if (mm->gs_valid && accept_data(&a->gs_valid, mm->source)) { a->gs = mm->gs; } - if (mm->ias_valid && accept_data(&a->ias_valid, mm->source, now)) { + if (mm->ias_valid && accept_data(&a->ias_valid, mm->source)) { a->ias = mm->ias; } - if (mm->tas_valid && accept_data(&a->tas_valid, mm->source, now)) { + if (mm->tas_valid && accept_data(&a->tas_valid, mm->source)) { a->tas = mm->tas; } - if (mm->mach_valid && accept_data(&a->mach_valid, mm->source, now)) { + if (mm->mach_valid && accept_data(&a->mach_valid, mm->source)) { a->mach = mm->mach; } - if (mm->baro_rate_valid && accept_data(&a->baro_rate_valid, mm->source, now)) { + if (mm->baro_rate_valid && accept_data(&a->baro_rate_valid, mm->source)) { a->baro_rate = mm->baro_rate; } - if (mm->geom_rate_valid && accept_data(&a->geom_rate_valid, mm->source, now)) { + if (mm->geom_rate_valid && accept_data(&a->geom_rate_valid, mm->source)) { a->geom_rate = mm->geom_rate; } - if (mm->category_valid && accept_data(&a->category_valid, mm->source, now)) { + if (mm->category_valid && accept_data(&a->category_valid, mm->source)) { a->category = mm->category; } - if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source, now)) { + if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source)) { a->airground = mm->airground; } - if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source, now)) { + if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source)) { memcpy(a->callsign, mm->callsign, sizeof(a->callsign)); } // prefer MCP over FMS // unless the source says otherwise - if (mm->intent.mcp_altitude_valid && mm->intent.altitude_source != INTENT_ALT_FMS && accept_data(&a->intent_altitude_valid, mm->source, now)) { + if (mm->intent.mcp_altitude_valid && mm->intent.altitude_source != INTENT_ALT_FMS && accept_data(&a->intent_altitude_valid, mm->source)) { a->intent_altitude = mm->intent.mcp_altitude; - } else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source, now)) { + } else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source)) { a->intent_altitude = mm->intent.fms_altitude; } - if (mm->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source, now)) { + if (mm->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source)) { a->intent_heading = mm->intent.heading; } - if (mm->intent.modes_valid && accept_data(&a->intent_modes_valid, mm->source, now)) { + if (mm->intent.modes_valid && accept_data(&a->intent_modes_valid, mm->source)) { a->intent_modes = mm->intent.modes; } - if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source, now)) { + if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source)) { a->alt_setting = mm->intent.alt_setting; } // CPR, even - if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) { + if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source)) { a->cpr_even_type = mm->cpr_type; a->cpr_even_lat = mm->cpr_lat; a->cpr_even_lon = mm->cpr_lon; @@ -669,7 +700,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) } // CPR, odd - if (mm->cpr_valid && mm->cpr_odd && accept_data(&a->cpr_odd_valid, mm->source, now)) { + if (mm->cpr_valid && mm->cpr_odd && accept_data(&a->cpr_odd_valid, mm->source)) { a->cpr_odd_type = mm->cpr_type; a->cpr_odd_lat = mm->cpr_lat; a->cpr_odd_lon = mm->cpr_lon; @@ -688,8 +719,8 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) // Now handle derived data // derive geometric altitude if we have baro + delta - if (compare_validity(&a->altitude_valid, &a->altitude_geom_valid, now) > 0 && - compare_validity(&a->geom_delta_valid, &a->altitude_geom_valid, now) > 0) { + if (compare_validity(&a->altitude_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 + a->geom_delta; combine_validity(&a->altitude_geom_valid, &a->altitude_valid, &a->geom_delta_valid); @@ -697,7 +728,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) // If we've got a new cprlat or cprlon if (mm->cpr_valid) { - updatePosition(a, mm, now); + updatePosition(a, mm); } return (a); @@ -837,7 +868,7 @@ static void trackRemoveStaleAircraft(uint64_t now) EXPIRE(cpr_odd); EXPIRE(cpr_even); EXPIRE(position); - +#undef EXPIRE prev = a; a = a->next; } } diff --git a/track.h b/track.h index c03063f..f8ab089 100644 --- a/track.h +++ b/track.h @@ -64,11 +64,18 @@ */ #define TRACK_MODEAC_MIN_MESSAGES 4 +// 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. +// expired: data is not valid. typedef struct { + uint64_t stale_interval; /* how long after an update until the data is stale */ + uint64_t expire_interval; /* how long after an update until the data expires */ + datasource_t source; /* where the data came from */ - uint64_t updated; /* when it arrived */ - uint64_t stale; /* when it will become stale */ - uint64_t expires; /* when it will expire */ + uint64_t updated; /* when it arrived */ + uint64_t stale; /* when it goes stale */ + uint64_t expires; /* when it expires */ } data_validity; /* Structure used to describe the state of one tracked aircraft */ @@ -213,33 +220,17 @@ extern uint32_t modeAC_age[4096]; /* is this bit of data valid? */ static inline int trackDataValid(const data_validity *v) { - return (v->source != SOURCE_INVALID); + return (v->source != SOURCE_INVALID && messageNow() < v->expires); } -/* .. with these constraints? */ -static inline int trackDataValidEx(const data_validity *v, - uint64_t now, - uint64_t maxAge, - datasource_t minSource) -{ - if (v->source == SOURCE_INVALID) - return 0; - if (v->source < minSource) - return 0; - if (v->updated < now && (now - v->updated) > maxAge) - return 0; - return 1; -} - -/* what's the age of this data? */ -static inline uint64_t trackDataAge(const data_validity *v, - uint64_t now) +/* what's the age of this data, in milliseconds? */ +static inline uint64_t trackDataAge(const data_validity *v) { if (v->source == SOURCE_INVALID) return ~(uint64_t)0; - if (v->updated >= now) + if (v->updated >= messageNow()) return 0; - return (now - v->updated); + return (messageNow() - v->updated); } /* Update aircraft state from data in the provided mesage. diff --git a/util.c b/util.c index d7afc07..d5f101c 100644 --- a/util.c +++ b/util.c @@ -52,6 +52,8 @@ #include #include +uint64_t _messageNow = 0; + uint64_t mstime(void) { struct timeval tv; @@ -68,6 +70,11 @@ int64_t receiveclock_ns_elapsed(uint64_t t1, uint64_t t2) return (t2 - t1) * 1000U / 12U; } +int64_t receiveclock_ms_elapsed(uint64_t t1, uint64_t t2) +{ + return (t2 - t1) / 12000U; +} + void normalize_timespec(struct timespec *ts) { if (ts->tv_nsec > 1000000000) { diff --git a/util.h b/util.h index 6030c97..5d32f05 100644 --- a/util.h +++ b/util.h @@ -25,11 +25,20 @@ /* Returns system time in milliseconds */ uint64_t mstime(void); +/* Returns the time for the current message we're dealing with */ +extern uint64_t _messageNow; +static inline uint64_t messageNow() { + return _messageNow; +} + /* Returns the time elapsed, in nanoseconds, from t1 to t2, * where t1 and t2 are 12MHz counters. */ int64_t receiveclock_ns_elapsed(uint64_t t1, uint64_t t2); +/* Same, in milliseconds */ +int64_t receiveclock_ms_elapsed(uint64_t t1, uint64_t t2); + /* Normalize the value in ts so that ts->nsec lies in * [0,999999999] */ From 161d2d8ded4674b43c134e558945777bd25547db Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 7 Dec 2017 10:34:08 -0600 Subject: [PATCH 18/52] More WIP --- comm_b.c | 38 ++-- dump1090.h | 94 +++++--- interactive.c | 4 +- mode_ac.c | 7 +- mode_s.c | 590 +++++++++++++++++++++++++++++++++++--------------- net_io.c | 259 +++++++++++++--------- track.c | 439 +++++++++++++++++++++++++++++++------ track.h | 78 +++++-- 8 files changed, 1088 insertions(+), 421 deletions(-) diff --git a/comm_b.c b/comm_b.c index 9ac9676..519bf84 100644 --- a/comm_b.c +++ b/comm_b.c @@ -374,51 +374,49 @@ static int decodeBDS40(struct modesMessage *mm, bool store) if (store) { mm->commb_format = COMMB_VERTICAL_INTENT; - mm->intent.valid = 1; - if (mcp_valid) { - mm->intent.mcp_altitude_valid = 1; - mm->intent.mcp_altitude = mcp_alt; + mm->nav.mcp_altitude_valid = 1; + mm->nav.mcp_altitude = mcp_alt; } if (fms_valid) { - mm->intent.fms_altitude_valid = 1; - mm->intent.fms_altitude = fms_alt; + mm->nav.fms_altitude_valid = 1; + mm->nav.fms_altitude = fms_alt; } if (baro_valid) { - mm->intent.alt_setting_valid = 1; - mm->intent.alt_setting = baro_setting; + mm->nav.qnh_valid = 1; + mm->nav.qnh = baro_setting; } if (mode_valid) { - mm->intent.modes_valid = 1; - mm->intent.modes = - ((mode_raw & 4) ? INTENT_MODE_VNAV : 0) | - ((mode_raw & 2) ? INTENT_MODE_ALT_HOLD : 0) | - ((mode_raw & 1) ? INTENT_MODE_APPROACH : 0); + mm->nav.modes_valid = 1; + mm->nav.modes = + ((mode_raw & 4) ? NAV_MODE_VNAV : 0) | + ((mode_raw & 2) ? NAV_MODE_ALT_HOLD : 0) | + ((mode_raw & 1) ? NAV_MODE_APPROACH : 0); } if (source_valid) { switch (source_raw) { case 0: - mm->intent.altitude_source = INTENT_ALT_UNKNOWN; + mm->nav.altitude_source = NAV_ALT_UNKNOWN; break; case 1: - mm->intent.altitude_source = INTENT_ALT_AIRCRAFT; + mm->nav.altitude_source = NAV_ALT_AIRCRAFT; break; case 2: - mm->intent.altitude_source = INTENT_ALT_MCP; + mm->nav.altitude_source = NAV_ALT_MCP; break; case 3: - mm->intent.altitude_source = INTENT_ALT_FMS; + mm->nav.altitude_source = NAV_ALT_FMS; break; default: - mm->intent.altitude_source = INTENT_ALT_INVALID; + mm->nav.altitude_source = NAV_ALT_INVALID; break; } } else { - mm->intent.altitude_source = INTENT_ALT_INVALID; + mm->nav.altitude_source = NAV_ALT_INVALID; } } @@ -570,7 +568,7 @@ static int decodeBDS50(struct modesMessage *mm, bool store) if (gs_valid) { mm->gs_valid = 1; - mm->gs = gs; + mm->gs.v0 = mm->gs.v2 = gs; } if (track_rate_valid) { diff --git a/dump1090.h b/dump1090.h index fe95ec2..55bcedd 100644 --- a/dump1090.h +++ b/dump1090.h @@ -178,7 +178,7 @@ typedef enum { } airground_t; typedef enum { - SIL_PER_SAMPLE, SIL_PER_HOUR + SIL_INVALID, SIL_PER_SAMPLE, SIL_PER_HOUR } sil_type_t; typedef enum { @@ -186,11 +186,12 @@ typedef enum { } cpr_type_t; typedef enum { + HEADING_INVALID, // Not set HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north HEADING_TRUE, // Heading, degrees clockwise from true north HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus - HEADING_TRACK_OR_HEADING // HEADING_GROUND_TRACK or HEADING_REF_DIR depending on the TAH bit in opstatus + HEADING_TRACK_OR_HEADING // GROUND_TRACK / MAGNETIC / TRUE depending on the TAH bit in opstatus } heading_type_t; typedef enum { @@ -206,12 +207,13 @@ typedef enum { } commb_format_t; typedef enum { - INTENT_MODE_AUTOPILOT = 1, - INTENT_MODE_VNAV = 2, - INTENT_MODE_ALT_HOLD = 4, - INTENT_MODE_APPROACH = 8, - INTENT_MODE_LNAV = 16 -} intent_modes_t; + NAV_MODE_AUTOPILOT = 1, + NAV_MODE_VNAV = 2, + NAV_MODE_ALT_HOLD = 4, + NAV_MODE_APPROACH = 8, + NAV_MODE_LNAV = 16, + NAV_MODE_TCAS = 32 +} nav_modes_t; #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses @@ -422,7 +424,8 @@ struct modesMessage { unsigned char MV[7]; // Decoded data - unsigned altitude_valid : 1; + unsigned altitude_baro_valid : 1; + unsigned altitude_geom_valid : 1; unsigned track_valid : 1; unsigned track_rate_valid : 1; unsigned heading_valid : 1; @@ -453,10 +456,13 @@ struct modesMessage { commb_format_t commb_format; // Inferred format of a comm-b message - // valid if altitude_valid: - int altitude; // Altitude in either feet or meters - altitude_unit_t altitude_unit; // the unit used for altitude - altitude_source_t altitude_source; // whether the altitude is a barometric altitude or a geometric height + // valid if altitude_baro_valid: + int altitude_baro; // Altitude in either feet or meters + altitude_unit_t altitude_baro_unit; // the unit used for altitude + + // valid if altitude_geom_valid: + int altitude_geom; // Altitude in either feet or meters + altitude_unit_t altitude_geom_unit; // the unit used for altitude // following fields are valid if the corresponding _valid field is set: int geom_delta; // Difference between geometric and baro alt @@ -464,7 +470,14 @@ struct modesMessage { heading_type_t heading_type;// how to interpret 'track_or_heading' float track_rate; // Rate of change of track, degrees/second float roll; // Roll, degrees, negative is left roll - unsigned gs; // Groundspeed, kts, reported directly or computed from from EW and NS velocity + struct { + // Groundspeed, kts, reported directly or computed from from EW and NS velocity + // For surface movement, this has different interpretations for v0 and v2; both + // fields are populated. The tracking layer will update "gs.selected". + float v0; + float v2; + float selected; + } gs; unsigned ias; // Indicated airspeed, kts unsigned tas; // True airspeed, kts double mach; // Mach number @@ -485,6 +498,36 @@ struct modesMessage { // valid if cpr_decoded: double decoded_lat; double decoded_lon; + unsigned decoded_nic; + unsigned decoded_rc; + + // various integrity/accuracy things + struct { + unsigned nic_a_valid : 1; + unsigned nic_b_valid : 1; + unsigned nic_c_valid : 1; + unsigned nic_baro_valid : 1; + unsigned nac_p_valid : 1; + unsigned nac_v_valid : 1; + unsigned sil_valid : 1; + unsigned gva_valid : 1; + unsigned sda_valid : 1; + + unsigned nic_a : 1; // if nic_a_valid + unsigned nic_b : 1; // if nic_b_valid + unsigned nic_c : 1; // if nic_c_valid + unsigned nic_baro : 1; // if nic_baro_valid + + unsigned nac_p : 4; // if nac_p_valid + unsigned nac_v : 3; // if nac_v_valid + + unsigned sil : 2; // if sil_valid + sil_type_t sil_type; // if sil_valid + + unsigned gva : 2; // if gva_valid + + unsigned sda : 2; // if sda_valid + } accuracy; // Operational Status struct { @@ -495,7 +538,6 @@ struct modesMessage { unsigned om_ident : 1; unsigned om_atc : 1; unsigned om_saf : 1; - unsigned om_sda : 2; unsigned cc_acas : 1; unsigned cc_cdti : 1; @@ -506,17 +548,8 @@ struct modesMessage { unsigned cc_uat_in : 1; unsigned cc_poa : 1; unsigned cc_b2_low : 1; - unsigned cc_nac_v : 3; - unsigned cc_nic_supp_c : 1; unsigned cc_lw_valid : 1; - unsigned nic_supp_a : 1; - unsigned nac_p : 4; - unsigned gva : 2; - unsigned sil : 2; - unsigned nic_baro : 1; - - sil_type_t sil_type; heading_type_t tah; heading_type_t hrd; @@ -528,23 +561,22 @@ struct modesMessage { // Target State & Status (ADS-B V2 only) // Comm-B BDS4,0 Vertical Intent struct { - unsigned valid : 1; - unsigned heading_valid : 1; unsigned fms_altitude_valid : 1; unsigned mcp_altitude_valid : 1; - unsigned alt_setting_valid : 1; + unsigned qnh_valid : 1; unsigned modes_valid : 1; float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended) + heading_type_t heading_type; unsigned fms_altitude; // FMS selected altitude unsigned mcp_altitude; // MCP/FCU selected altitude - float alt_setting; // altimeter setting (QFE or QNH/QNE), millibars + float qnh; // altimeter setting (QFE or QNH/QNE), millibars - enum { INTENT_ALT_INVALID, INTENT_ALT_UNKNOWN, INTENT_ALT_AIRCRAFT, INTENT_ALT_MCP, INTENT_ALT_FMS } altitude_source; + enum { NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS } altitude_source; - intent_modes_t modes; - } intent; + nav_modes_t modes; + } nav; }; // This one needs modesMessage: diff --git a/interactive.c b/interactive.c index 66fdfb9..580a5d8 100644 --- a/interactive.c +++ b/interactive.c @@ -162,8 +162,8 @@ void interactiveShowData(void) { 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_valid)) { - snprintf(strFl, 7, "%5d ", convert_altitude(a->altitude)); + } 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", diff --git a/mode_ac.c b/mode_ac.c index 9af1243..194ff67 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -146,10 +146,9 @@ void decodeModeAMessage(struct modesMessage *mm, int ModeA) if (!mm->spi) { int modeC = modeAToModeC(ModeA); if (modeC != INVALID_ALTITUDE) { - mm->altitude = modeC * 100; - mm->altitude_unit = UNIT_FEET; - mm->altitude_source = ALTITUDE_BARO; - mm->altitude_valid = 1; + mm->altitude_baro = modeC * 100; + mm->altitude_baro_unit = UNIT_FEET; + mm->altitude_baro_valid = 1; } } diff --git a/mode_s.c b/mode_s.c index 2f13af4..3f30c59 100644 --- a/mode_s.c +++ b/mode_s.c @@ -177,23 +177,50 @@ static int decodeAC12Field(int AC12Field, altitude_unit_t *unit) { // //========================================================================= // -// Decode the 7 bit ground movement field PWL exponential style scale +// Decode the 7 bit ground movement field PWL exponential style scale (ADS-B v2) // -static unsigned decodeMovementField(unsigned movement) { - int gspeed; - +static float decodeMovementFieldV2(unsigned movement) { // Note : movement codes 0,125,126,127 are all invalid, but they are // trapped for before this function is called. - if (movement > 123) gspeed = 199; // > 175kt - else if (movement > 108) gspeed = ((movement - 108) * 5) + 100; - else if (movement > 93) gspeed = ((movement - 93) * 2) + 70; - else if (movement > 38) gspeed = ((movement - 38) ) + 15; - else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2; - else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1; - else gspeed = 0; + // Each movement value is a range of speeds; + // we return the midpoint of the range (rounded to the nearest integer) + if (movement >= 125) return 0; // invalid + else if (movement == 124) return 180; // gs > 175kt, pick a value.. + else if (movement >= 109) return 100 + (movement - 109 + 0.5) * 5; // 100 < gs <= 175 in 5kt steps + else if (movement >= 94) return 70 + (movement - 94 + 0.5) * 2; // 70 < gs <= 100 in 2kt steps + else if (movement >= 39) return 15 + (movement - 39 + 0.5) * 1; // 15 < gs <= 70 in 1kt steps + else if (movement >= 13) return 2 + (movement - 13 + 0.5) * 0.50; // 2 < gs <= 15 in 0.5kt steps + else if (movement >= 9) return 1 + (movement - 9 + 0.5) * 0.25; // 1 < gs <= 2 in 0.25kt steps + else if (movement >= 3) return 0.125 + (movement - 3 + 0.5) * 0.875 / 6; // 0.125 < gs <= 1 in 0.875/6 kt step + else if (movement >= 2) return 0.125 / 2; // 0 < gs <= 0.125 + // 1: stopped, gs = 0 + // 0: no data + else return 0; +} - return (gspeed); +// +//========================================================================= +// +// Decode the 7 bit ground movement field PWL exponential style scale (ADS-B v0) +// +static float decodeMovementFieldV0(unsigned movement) { + // Note : movement codes 0,125,126,127 are all invalid, but they are + // trapped for before this function is called. + + // Each movement value is a range of speeds; + // we return the midpoint of the range + if (movement >= 125) return 0; // invalid + else if (movement == 124) return 180; // gs >= 175kt, pick a value.. + else if (movement >= 109) return 100 + (movement - 109 + 0.5) * 5; // 100 < gs <= 175 in 5kt steps + else if (movement >= 94) return 70 + (movement - 94 + 0.5) * 2; // 70 < gs <= 100 in 2kt steps + else if (movement >= 39) return 15 + (movement - 39 + 0.5) * 1; // 15 < gs <= 70 in 1kt steps + else if (movement >= 13) return 2 + (movement - 13 + 0.5) * 0.50; // 2 < gs <= 15 in 0.5kt steps + else if (movement >= 9) return 1 + (movement - 9 + 0.5) * 0.25; // 1 < gs <= 2 in 0.25kt steps + else if (movement >= 2) return 0.125 + (movement - 3 + 0.5) * 0.125; // 0.125 < gs <= 1 in 0.125kt step + // 1: stopped, gs < 0.125kt + // 0: no data + else return 0; } // Correct a decoded native-endian Address Announced field @@ -505,10 +532,9 @@ int decodeModesMessage(struct modesMessage *mm, unsigned char *msg) if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 16 || mm->msgtype == 20) { mm->AC = getbits(msg, 20, 32); if (mm->AC) { // Only attempt to decode if a valid (non zero) altitude is present - mm->altitude = decodeAC13Field(mm->AC, &mm->altitude_unit); - if (mm->altitude != INVALID_ALTITUDE) - mm->altitude_valid = 1; - mm->altitude_source = ALTITUDE_BARO; + mm->altitude_baro = decodeAC13Field(mm->AC, &mm->altitude_baro_unit); + if (mm->altitude_baro != INVALID_ALTITUDE) + mm->altitude_baro_valid = 1; } } @@ -729,30 +755,31 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) // Airborne Velocity Message unsigned char *me = mm->ME; + // 1-5: ME type + // 6-8: ME subtype mm->mesub = getbits(me, 6, 8); - if (check_imf && getbit(me, 9)) - setIMF(mm); - if (mm->mesub < 1 || mm->mesub > 4) return; - unsigned vert_rate = getbits(me, 38, 46); - unsigned vert_rate_is_geom = getbit(me, 36); - if (vert_rate) { - int rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64); - if (vert_rate_is_geom) { - mm->geom_rate = rate; - mm->geom_rate_valid = 1; - } else { - mm->baro_rate = rate; - mm->baro_rate_valid = 1; - } - } + // 9: IMF or Intent Change + if (check_imf && getbit(me, 9)) + setIMF(mm); + // 10: reserved + + // 11-13: NACv (NUCr in v0, maps directly to NACv in v2) + mm->accuracy.nac_v_valid = 1; + mm->accuracy.nac_v = getbits(me, 11, 13); + + // 14-35: speed/velocity depending on subtype switch (mm->mesub) { case 1: case 2: { + // 14: E/W direction + // 15-24: E/W speed + // 25: N/S direction + // 26-35: N/S speed unsigned ew_raw = getbits(me, 15, 24); unsigned ns_raw = getbits(me, 26, 35); @@ -761,10 +788,10 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) int ns_vel = (ns_raw - 1) * (getbit(me, 25) ? -1 : 1) * ((mm->mesub == 2) ? 4 : 1); // Compute velocity and angle from the two speed components - mm->gs = (unsigned) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); + mm->gs.v0 = mm->gs.v2 = mm->gs.selected = sqrtf((ns_vel * ns_vel) + (ew_vel * ew_vel) + 0.5); mm->gs_valid = 1; - if (mm->gs) { + if (mm->gs.selected > 0) { float ground_track = atan2(ew_vel, ns_vel) * 180.0 / M_PI; // We don't want negative values but a 0-360 scale if (ground_track < 0) @@ -779,6 +806,16 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) case 3: case 4: { + // 14: heading status + // 15-24: heading + if (getbit(me, 14)) { + mm->heading_valid = 1; + mm->heading = getbits(me, 15, 24) * 360.0 / 1024.0; + mm->heading_type = HEADING_MAGNETIC_OR_TRUE; + } + + // 25: airspeed type + // 26-35: airspeed unsigned airspeed = getbits(me, 26, 35); if (airspeed) { unsigned speed = (airspeed - 1) * (mm->mesub == 4 ? 4 : 1); @@ -791,15 +828,30 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) } } - if (getbit(me, 14)) { - mm->heading_valid = 1; - mm->heading = getbits(me, 15, 24) * 360.0 / 1024.0; - mm->heading_type = HEADING_MAGNETIC_OR_TRUE; - } break; } } + // 36: vert rate source + // 37: vert rate sign + // 38-46: vert rate magnitude + unsigned vert_rate = getbits(me, 38, 46); + unsigned vert_rate_is_geom = getbit(me, 36); + if (vert_rate) { + int rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64); + if (vert_rate_is_geom) { + mm->geom_rate = rate; + mm->geom_rate_valid = 1; + } else { + mm->baro_rate = rate; + mm->baro_rate_valid = 1; + } + } + + // 47-48: reserved + + // 49: baro/geom delta sign + // 50-56: baro/geom delta magnitude unsigned raw_delta = getbits(me, 50, 56); if (raw_delta) { mm->geom_delta_valid = 1; @@ -812,28 +864,37 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf) // Surface position and movement unsigned char *me = mm->ME; - if (check_imf && getbit(me, 21)) - setIMF(mm); - mm->airground = AG_GROUND; // definitely. - mm->cpr_lat = getbits(me, 23, 39); - mm->cpr_lon = getbits(me, 40, 56); - mm->cpr_odd = getbit(me, 22); - mm->cpr_nucp = (14 - mm->metype); mm->cpr_valid = 1; mm->cpr_type = CPR_SURFACE; + // 6-12: Movement unsigned movement = getbits(me, 6, 12); if (movement > 0 && movement < 125) { mm->gs_valid = 1; - mm->gs = decodeMovementField(movement); + mm->gs.selected = mm->gs.v0 = decodeMovementFieldV0(movement); // assumed v0 until told otherwise + mm->gs.v2 = decodeMovementFieldV2(movement); } + // 13: Heading/track status + // 14-20: Heading/track if (getbit(me, 13)) { mm->heading_valid = 1; mm->heading = getbits(me, 14, 20) * 360.0 / 128.0; mm->heading_type = HEADING_TRACK_OR_HEADING; } + + // 21: IMF or T flag + if (check_imf && getbit(me, 21)) + setIMF(mm); + + // 22: F flag (odd/even) + mm->cpr_odd = getbit(me, 22); + + // 23-39: CPR encoded latitude + mm->cpr_lat = getbits(me, 23, 39); + // 40-56: CPR encoded longitude + mm->cpr_lon = getbits(me, 40, 56); } static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) @@ -841,20 +902,34 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) // Airborne position and altitude unsigned char *me = mm->ME; - if (check_imf && getbit(me, 8)) - setIMF(mm); + // 6-7: surveillance status + // 8: IMF or NIC supplement-B + if (check_imf) { + if (getbit(me, 8)) + setIMF(mm); + } else { + // NIC-B (v2) or SAF (v0/v1) + mm->accuracy.nic_b_valid = 1; + mm->accuracy.nic_b = getbit(me, 8); + } + + // 9-20: altitude unsigned AC12Field = getbits(me, 9, 20); if (mm->metype == 0) { - mm->cpr_nucp = 0; + // no position information } else { - // Catch some common failure modes and don't mark them as valid - // (so they won't be used for positioning) + // 21: T flag (UTC sync or not) + // 22: F flag (odd or even) + // 23-39: CPR encoded latitude + // 40-56: CPR encoded longitude mm->cpr_lat = getbits(me, 23, 39); mm->cpr_lon = getbits(me, 40, 56); + // Catch some common failure modes and don't mark them as valid + // (so they won't be used for positioning) if (AC12Field == 0 && mm->cpr_lon == 0 && (mm->cpr_lat & 0x0fff) == 0 && mm->metype == 15) { // Seen from at least: // 400F3F (Eurocopter ECC155 B1) - Bristow Helicopters @@ -868,23 +943,23 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) mm->cpr_valid = 1; mm->cpr_type = CPR_AIRBORNE; mm->cpr_odd = getbit(me, 22); - - if (mm->metype == 18 || mm->metype == 22) - mm->cpr_nucp = 0; - else if (mm->metype < 18) - mm->cpr_nucp = (18 - mm->metype); - else - mm->cpr_nucp = (29 - mm->metype); } } if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present - mm->altitude = decodeAC12Field(AC12Field, &mm->altitude_unit); - if (mm->altitude != INVALID_ALTITUDE) { - mm->altitude_valid = 1; + altitude_unit_t unit; + int alt = decodeAC12Field(AC12Field, &unit); + if (alt != INVALID_ALTITUDE) { + if (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) { + mm->altitude_geom = alt; + mm->altitude_geom_unit = unit; + mm->altitude_geom_valid = 1; + } else { + mm->altitude_baro = alt; + mm->altitude_baro_unit = unit; + mm->altitude_baro_valid = 1; + } } - - mm->altitude_source = (mm->metype == 20 || mm->metype == 21 || mm->metype == 22) ? ALTITUDE_GEOM : ALTITUDE_BARO; } } @@ -931,52 +1006,186 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) if (check_imf && getbit(me, 51)) setIMF(mm); - if (mm->mesub == 0) { // Target state and status, V1 - // TODO: need RTCA/DO-260A - } else if (mm->mesub == 1) { // Target state and status, V2 - mm->intent.valid = 1; + if (mm->mesub == 0 && getbit(me, 11) == 0) { // Target state and status, V1 + // 8-9: vertical source + switch (getbits(me, 8, 9)) { + case 1: + mm->nav.altitude_source = NAV_ALT_MCP; + break; + case 2: + mm->nav.altitude_source = NAV_ALT_AIRCRAFT; + break; + case 3: + mm->nav.altitude_source = NAV_ALT_FMS; + break; + default: + // nothing + break; + } + // 10: target altitude type (ignored) + // 11: backward compatibility bit, always 0 + // 12-13: target alt capabilities (ignored) + // 14-15: vertical mode + switch (getbits(me, 14, 15)) { + case 1: // "acquiring" + mm->nav.modes_valid = 1; + if (mm->nav.altitude_source == NAV_ALT_FMS) { + mm->nav.modes |= NAV_MODE_VNAV; + } else { + mm->nav.modes |= NAV_MODE_AUTOPILOT; + } + break; + case 2: // "maintaining" + mm->nav.modes_valid = 1; + if (mm->nav.altitude_source == NAV_ALT_FMS) { + mm->nav.modes |= NAV_MODE_VNAV; + } else if (mm->nav.altitude_source == NAV_ALT_AIRCRAFT) { + mm->nav.modes |= NAV_MODE_ALT_HOLD; + } else { + mm->nav.modes |= NAV_MODE_AUTOPILOT; + } + break; + default: + // nothing + break; + } + // 16-25: altitude + int alt = -1000 + 100 * getbits(me, 16, 25); + switch (mm->nav.altitude_source) { + case NAV_ALT_MCP: + mm->nav.mcp_altitude_valid = 1; + mm->nav.mcp_altitude = alt; + break; + case NAV_ALT_FMS: + mm->nav.fms_altitude_valid = 1; + mm->nav.fms_altitude = alt; + break; + default: + // nothing + break; + } + // 26-27: horizontal data source + unsigned h_source = getbits(me, 26, 27); + if (h_source != 0) { + // 28-36: target heading/track + mm->nav.heading_valid = 1; + mm->nav.heading = getbits(me, 28, 36); + // 37: track vs heading + if (getbit(me, 37)) { + mm->nav.heading_type = HEADING_GROUND_TRACK; + } else { + mm->nav.heading_type = HEADING_MAGNETIC_OR_TRUE; + } + } + // 38-39: horiontal mode + switch (getbits(me, 38, 39)) { + case 1: // acquiring + case 2: // maintaining + mm->nav.modes_valid = 1; + if (h_source == 3) { // FMS + mm->nav.modes |= NAV_MODE_LNAV; + } else { + mm->nav.modes |= NAV_MODE_AUTOPILOT; + } + break; + default: + // nothing + break; + } + + // 40-43: NACp + mm->accuracy.nac_p_valid = 1; + mm->accuracy.nac_p = getbits(me, 40, 43); + + // 44: NICbaro + mm->accuracy.nic_baro_valid = 1; + mm->accuracy.nic_baro = getbit(me, 44); + + // 45-46: SIL + mm->accuracy.sil_valid = 1; + mm->accuracy.sil = getbits(me, 45, 46); + mm->accuracy.sil_type = SIL_INVALID; + + // 47-51: reserved + + // 52-53: TCAS status + switch (getbits(me, 52, 53)) { + case 1: + mm->nav.modes_valid = 1; + // no tcas + break; + case 2: + case 3: + mm->nav.modes_valid = 1; + mm->nav.modes |= NAV_MODE_TCAS; + break; + case 0: + // assume TCAS if we had any other modes + // but don't enable modes just for this + mm->nav.modes |= NAV_MODE_TCAS; + break; + default: + // nothing + break; + } + + + // 54-56: emergency/priority (ignored) + } else if (mm->mesub == 1) { // Target state and status, V2 // 8: SIL unsigned is_fms = getbit(me, 9); unsigned alt_bits = getbits(me, 10, 20); if (alt_bits != 0) { if (is_fms) { - mm->intent.fms_altitude_valid = 1; - mm->intent.fms_altitude = (alt_bits - 1) * 32; + mm->nav.fms_altitude_valid = 1; + mm->nav.fms_altitude = (alt_bits - 1) * 32; } else { - mm->intent.mcp_altitude_valid = 1; - mm->intent.mcp_altitude = (alt_bits - 1) * 32; + mm->nav.mcp_altitude_valid = 1; + mm->nav.mcp_altitude = (alt_bits - 1) * 32; } } unsigned baro_bits = getbits(me, 21, 29); if (baro_bits != 0) { - mm->intent.alt_setting_valid = 1; - mm->intent.alt_setting = 800.0 + (baro_bits - 1) * 0.8; + mm->nav.qnh_valid = 1; + mm->nav.qnh = 800.0 + (baro_bits - 1) * 0.8; } if (getbit(me, 30)) { - mm->intent.heading_valid = 1; + mm->nav.heading_valid = 1; // two's complement -180..+180, which is conveniently // also the same as unsigned 0..360 - mm->intent.heading = getbits(me, 31, 39) * 180.0 / 256.0; + mm->nav.heading = getbits(me, 31, 39) * 180.0 / 256.0; + mm->nav.heading_type = HEADING_MAGNETIC_OR_TRUE; } // 40-43: NACp - // 44: NICbaro - // 45-46: SIL + mm->accuracy.nac_p_valid = 1; + mm->accuracy.nac_p = getbits(me, 40, 43); + // 44: NICbaro + mm->accuracy.nic_baro_valid = 1; + mm->accuracy.nic_baro = getbit(me, 44); + + // 45-46: SIL + mm->accuracy.sil_valid = 1; + mm->accuracy.sil = getbits(me, 45, 46); + mm->accuracy.sil_type = SIL_INVALID; + + // 47: mode bits validity if (getbit(me, 47)) { - mm->intent.modes_valid = 1; - mm->intent.modes = - (getbit(me, 48) ? INTENT_MODE_AUTOPILOT : 0) | - (getbit(me, 49) ? INTENT_MODE_VNAV : 0) | - (getbit(me, 50) ? INTENT_MODE_ALT_HOLD : 0) | + // 48-54: mode bits + mm->nav.modes_valid = 1; + mm->nav.modes = + (getbit(me, 48) ? NAV_MODE_AUTOPILOT : 0) | + (getbit(me, 49) ? NAV_MODE_VNAV : 0) | + (getbit(me, 50) ? NAV_MODE_ALT_HOLD : 0) | // 51: IMF - (getbit(me, 52) ? INTENT_MODE_APPROACH : 0) | - // 53: TCAS operational - (getbit(me, 54) ? INTENT_MODE_LNAV : 0); + (getbit(me, 52) ? NAV_MODE_APPROACH : 0) | + (getbit(me, 53) ? NAV_MODE_TCAS : 0) | + (getbit(me, 54) ? NAV_MODE_LNAV : 0); } // 55-56 reserved @@ -999,6 +1208,10 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) switch (mm->opstatus.version) { case 0: + if (mm->mesub == 0 && getbits(me, 9, 10) == 0) { + mm->opstatus.cc_acas = !getbit(me, 12); + mm->opstatus.cc_cdti = getbit(me, 13); + } break; case 1: @@ -1024,59 +1237,72 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) mm->opstatus.cc_lw = getbits(me, 21, 24); } - mm->opstatus.nic_supp_a = getbit(me, 44); - mm->opstatus.nac_p = getbits(me, 45, 48); - mm->opstatus.sil = getbits(me, 51, 52); + mm->accuracy.nic_a_valid = 1; + mm->accuracy.nic_a = getbit(me, 44); + mm->accuracy.nac_p_valid = 1; + mm->accuracy.nac_p = getbits(me, 45, 48); + mm->accuracy.sil_valid = 1; + mm->accuracy.sil = getbits(me, 51, 52); + mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; + if (mm->mesub == 0) { - mm->opstatus.nic_baro = getbit(me, 53); + mm->accuracy.nic_baro_valid = 1; + mm->accuracy.nic_baro = getbit(me, 53); } else { mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd; } break; case 2: - default: if (getbits(me, 25, 26) == 0) { mm->opstatus.om_acas_ra = getbit(me, 27); mm->opstatus.om_ident = getbit(me, 28); mm->opstatus.om_atc = getbit(me, 29); mm->opstatus.om_saf = getbit(me, 30); - mm->opstatus.om_sda = getbits(me, 31, 32); + mm->accuracy.sda_valid = 1; + mm->accuracy.sda = getbits(me, 31, 32); } - if (mm->mesub == 0 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { + if (mm->mesub == 0 && getbits(me, 9, 10) == 0) { // airborne - mm->opstatus.cc_acas = getbit(me, 11); + mm->opstatus.cc_acas = getbit(me, 11); // nb inverted sense versus v0/v1 mm->opstatus.cc_1090_in = getbit(me, 12); mm->opstatus.cc_arv = getbit(me, 15); mm->opstatus.cc_ts = getbit(me, 16); mm->opstatus.cc_tc = getbits(me, 17, 18); mm->opstatus.cc_uat_in = getbit(me, 19); - } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0 && getbits(me, 13, 14) == 0) { + } else if (mm->mesub == 1 && getbits(me, 9, 10) == 0) { // surface mm->opstatus.cc_poa = getbit(me, 11); mm->opstatus.cc_1090_in = getbit(me, 12); mm->opstatus.cc_b2_low = getbit(me, 15); mm->opstatus.cc_uat_in = getbit(me, 16); - mm->opstatus.cc_nac_v = getbits(me, 17, 19); - mm->opstatus.cc_nic_supp_c = getbit(me, 20); + mm->accuracy.nac_v_valid = 1; + mm->accuracy.nac_v = getbits(me, 17, 19); + mm->accuracy.nic_c_valid = 1; + mm->accuracy.nic_c = getbit(me, 20); mm->opstatus.cc_lw_valid = 1; mm->opstatus.cc_lw = getbits(me, 21, 24); mm->opstatus.cc_antenna_offset = getbits(me, 33, 40); } - mm->opstatus.nic_supp_a = getbit(me, 44); - mm->opstatus.nac_p = getbits(me, 45, 48); - mm->opstatus.sil = getbits(me, 51, 52); + mm->accuracy.nic_a_valid = 1; + mm->accuracy.nic_a = getbit(me, 44); + mm->accuracy.nac_p_valid = 1; + mm->accuracy.nac_p = getbits(me, 45, 48); + mm->accuracy.sil_valid = 1; + mm->accuracy.sil = getbits(me, 51, 52); + mm->accuracy.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; if (mm->mesub == 0) { - mm->opstatus.gva = getbits(me, 49, 50); - mm->opstatus.nic_baro = getbit(me, 53); + mm->accuracy.gva_valid = 1; + mm->accuracy.gva = getbits(me, 49, 50); + mm->accuracy.nic_baro_valid = 1; + mm->accuracy.nic_baro = getbit(me, 53); } else { mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd; } - mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; break; } } @@ -1240,17 +1466,6 @@ static const char *altitude_unit_to_string(altitude_unit_t unit) { } } -static const char *altitude_source_to_string(altitude_source_t source) { - switch (source) { - case ALTITUDE_BARO: - return "barometric"; - case ALTITUDE_GEOM: - return "geometric"; - default: - return "(unknown altitude source)"; - } -} - static const char *airground_to_string(airground_t airground) { switch (airground) { case AG_GROUND: @@ -1344,21 +1559,23 @@ static const char *commb_format_to_string(commb_format_t format) { } } -static const char *intent_modes_to_string(intent_modes_t flags) +static const char *nav_modes_to_string(nav_modes_t flags) { static char buf[128]; buf[0] = 0; - if (flags & INTENT_MODE_AUTOPILOT) + if (flags & NAV_MODE_AUTOPILOT) strcat(buf, "autopilot "); - if (flags & INTENT_MODE_VNAV) + if (flags & NAV_MODE_VNAV) strcat(buf, "vnav "); - if (flags & INTENT_MODE_ALT_HOLD) + if (flags & NAV_MODE_ALT_HOLD) strcat(buf, "althold "); - if (flags & INTENT_MODE_APPROACH) + if (flags & NAV_MODE_APPROACH) strcat(buf, "approach "); - if (flags & INTENT_MODE_LNAV) + if (flags & NAV_MODE_LNAV) strcat(buf, "lnav "); + if (flags & NAV_MODE_TCAS) + strcat(buf, "tcas "); if (buf[0] != 0) buf[strlen(buf)-1] = 0; @@ -1617,11 +1834,16 @@ void displayModesMessage(struct modesMessage *mm) { airground_to_string(mm->airground)); } - if (mm->altitude_valid) { - printf(" Altitude: %d %s %s\n", - mm->altitude, - altitude_unit_to_string(mm->altitude_unit), - altitude_source_to_string(mm->altitude_source)); + if (mm->altitude_baro_valid) { + printf(" Baro altitude: %d %s\n", + mm->altitude_baro, + altitude_unit_to_string(mm->altitude_baro_unit)); + } + + if (mm->altitude_geom_valid) { + printf(" Geom altitude: %d %s\n", + mm->altitude_geom, + altitude_unit_to_string(mm->altitude_geom_unit)); } if (mm->geom_delta_valid) { @@ -1642,7 +1864,14 @@ void displayModesMessage(struct modesMessage *mm) { } if (mm->gs_valid) { - printf(" Groundspeed: %u kt\n", mm->gs); + printf(" Groundspeed: %.1f kt", mm->gs.selected); + if (mm->gs.v0 != mm->gs.selected) { + printf(" (v0: %.1f kt)", mm->gs.v0); + } + if (mm->gs.v2 != mm->gs.selected) { + printf(" (v2: %.1f kt)", mm->gs.v2); + } + printf("\n"); } if (mm->ias_valid) { @@ -1682,21 +1911,24 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->cpr_valid) { printf(" CPR type: %s\n" - " CPR odd flag: %s\n" - " CPR NUCp/NIC: %u\n", + " CPR odd flag: %s\n", cpr_type_to_string(mm->cpr_type), - mm->cpr_odd ? "odd" : "even", - mm->cpr_nucp); + mm->cpr_odd ? "odd" : "even"); if (mm->cpr_decoded) { printf(" CPR latitude: %.5f (%u)\n" " CPR longitude: %.5f (%u)\n" - " CPR decoding: %s\n", + " CPR decoding: %s\n" + " NIC: %u\n" + " Rc: %.3f km / %.1f NM\n", mm->decoded_lat, mm->cpr_lat, mm->decoded_lon, mm->cpr_lon, - mm->cpr_relative ? "local" : "global"); + mm->cpr_relative ? "local" : "global", + mm->decoded_nic, + mm->decoded_rc / 1000.0, + mm->decoded_rc / 1852.0); } else { printf(" CPR latitude: (%u)\n" " CPR longitude: (%u)\n" @@ -1706,6 +1938,36 @@ void displayModesMessage(struct modesMessage *mm) { } } + if (mm->accuracy.nic_a_valid) { + printf(" NIC-A: %d\n", mm->accuracy.nic_a); + } + if (mm->accuracy.nic_b_valid) { + printf(" NIC-B: %d\n", mm->accuracy.nic_b); + } + if (mm->accuracy.nic_c_valid) { + printf(" NIC-C: %d\n", mm->accuracy.nic_c); + } + if (mm->accuracy.nic_baro_valid) { + printf(" NIC-baro: %d\n", mm->accuracy.nic_baro); + } + if (mm->accuracy.nac_p_valid) { + printf(" NACp: %d\n", mm->accuracy.nac_p); + } + if (mm->accuracy.nac_v_valid) { + printf(" NACv: %d\n", mm->accuracy.nac_v); + } + if (mm->accuracy.gva_valid) { + printf(" GVA: %d\n", mm->accuracy.gva); + } + if (mm->accuracy.sil_valid) { + printf(" SIL: %d (%s)\n", + mm->accuracy.sil, + (mm->accuracy.sil_type == SIL_PER_HOUR ? "per hour" : "per sample")); + } + if (mm->accuracy.sda_valid) { + printf(" SDA: %d\n", mm->accuracy.sda); + } + if (mm->opstatus.valid) { printf(" Aircraft Operational Status:\n"); printf(" Version: %d\n", mm->opstatus.version); @@ -1720,8 +1982,6 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->opstatus.cc_uat_in) printf("UATIN "); if (mm->opstatus.cc_poa) printf("POA "); if (mm->opstatus.cc_b2_low) printf("B2-LOW "); - if (mm->opstatus.cc_nac_v) printf("NACv=%d ", mm->opstatus.cc_nac_v); - if (mm->opstatus.cc_nic_supp_c) printf("NIC-C=1 "); if (mm->opstatus.cc_lw_valid) printf("L/W=%d ", mm->opstatus.cc_lw); if (mm->opstatus.cc_antenna_offset) printf("GPS-OFFSET=%d ", mm->opstatus.cc_antenna_offset); printf("\n"); @@ -1731,53 +1991,43 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->opstatus.om_ident) printf("IDENT "); if (mm->opstatus.om_atc) printf("ATC "); if (mm->opstatus.om_saf) printf("SAF "); - if (mm->opstatus.om_sda) printf("SDA=%d ", mm->opstatus.om_sda); printf("\n"); - if (mm->opstatus.nic_supp_a) printf(" NIC-A: %d\n", mm->opstatus.nic_supp_a); - if (mm->opstatus.nac_p) printf(" NACp: %d\n", mm->opstatus.nac_p); - if (mm->opstatus.gva) printf(" GVA: %d\n", mm->opstatus.gva); - if (mm->opstatus.sil) printf(" SIL: %d (%s)\n", mm->opstatus.sil, (mm->opstatus.sil_type == SIL_PER_HOUR ? "per hour" : "per sample")); - if (mm->opstatus.nic_baro) printf(" NICbaro: %d\n", mm->opstatus.nic_baro); - if (mm->mesub == 1) printf(" Track/heading: %s\n", heading_type_to_string(mm->opstatus.tah)); printf(" Heading ref dir: %s\n", heading_type_to_string(mm->opstatus.hrd)); } - if (mm->intent.valid) { - printf(" Intent:\n"); - if (mm->intent.heading_valid) - printf(" Selected heading: %.1f\n", mm->intent.heading); - if (mm->intent.fms_altitude_valid) - printf(" FMS selected altitude: %u ft\n", mm->intent.fms_altitude); - if (mm->intent.mcp_altitude_valid) - printf(" MCP selected altitude: %u ft\n", mm->intent.mcp_altitude); - if (mm->intent.alt_setting_valid) - printf(" Altimeter setting: %.1f millibars\n", mm->intent.alt_setting); - - if (mm->intent.altitude_source != INTENT_ALT_INVALID) { - printf(" Target altitude source: "); - switch (mm->intent.altitude_source) { - case INTENT_ALT_AIRCRAFT: - printf("aircraft altitude\n"); - break; - case INTENT_ALT_MCP: - printf("MCP selected altitude\n"); - break; - case INTENT_ALT_FMS: - printf("FMS selected altitude\n"); - break; - default: - printf("unknown\n"); - } - } - - if (mm->intent.modes_valid) { - printf(" Active modes: %s\n", intent_modes_to_string(mm->intent.modes)); + if (mm->nav.heading_valid) + printf(" Selected heading: %.1f\n", mm->nav.heading); + if (mm->nav.fms_altitude_valid) + printf(" FMS selected altitude: %u ft\n", mm->nav.fms_altitude); + if (mm->nav.mcp_altitude_valid) + printf(" MCP selected altitude: %u ft\n", mm->nav.mcp_altitude); + if (mm->nav.qnh_valid) + printf(" QNH: %.1f millibars\n", mm->nav.qnh); + if (mm->nav.altitude_source != NAV_ALT_INVALID) { + printf(" Target altitude source: "); + switch (mm->nav.altitude_source) { + case NAV_ALT_AIRCRAFT: + printf("aircraft altitude\n"); + break; + case NAV_ALT_MCP: + printf("MCP selected altitude\n"); + break; + case NAV_ALT_FMS: + printf("FMS selected altitude\n"); + break; + default: + printf("unknown\n"); } } + if (mm->nav.modes_valid) { + printf(" Nav modes: %s\n", nav_modes_to_string(mm->nav.modes)); + } + + printf("\n"); fflush(stdout); } diff --git a/net_io.c b/net_io.c index 61d2f61..1ba7635 100644 --- a/net_io.c +++ b/net_io.c @@ -582,31 +582,29 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { else {p += sprintf(p, ",");} // Field 12 is the altitude (if we have it) - if (mm->altitude_valid) { - if (Modes.use_gnss) { - if (mm->altitude_source == ALTITUDE_GEOM) { - p += sprintf(p, ",%dH", mm->altitude); - } else if (trackDataValid(&a->geom_delta_valid)) { - p += sprintf(p, ",%dH", mm->altitude + a->geom_delta); - } else { - p += sprintf(p, ",%d", mm->altitude); - } + if (Modes.use_gnss) { + if (mm->altitude_geom_valid) { + p += sprintf(p, ",%dH", mm->altitude_geom); + } else if (mm->altitude_baro_valid && trackDataValid(&a->geom_delta_valid)) { + p += sprintf(p, ",%dH", mm->altitude_baro + a->geom_delta); + } else if (mm->altitude_baro_valid) { + p += sprintf(p, ",%d", mm->altitude_baro); } else { - if (mm->altitude_source == ALTITUDE_BARO) { - p += sprintf(p, ",%d", mm->altitude); - } else if (trackDataValid(&a->geom_delta_valid)) { - p += sprintf(p, ",%d", mm->altitude - a->geom_delta); - } else { - p += sprintf(p, ","); - } + p += sprintf(p, ","); } } else { - p += sprintf(p, ","); + if (mm->altitude_baro_valid) { + p += sprintf(p, ",%d", mm->altitude_baro); + } else if (mm->altitude_geom_valid && trackDataValid(&a->geom_delta_valid)) { + p += sprintf(p, ",%d", mm->altitude_geom - a->geom_delta); + } else { + p += sprintf(p, ","); + } } // Field 13 is the ground Speed (if we have it) if (mm->gs_valid) { - p += sprintf(p, ",%u", mm->gs); + p += sprintf(p, ",%.0f", mm->gs.selected); } else { p += sprintf(p, ","); } @@ -1111,8 +1109,10 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s p += snprintf(p, end-p, "\"callsign\","); if (a->position_valid.source == source) p += snprintf(p, end-p, "\"lat\",\"lon\","); - if (a->altitude_valid.source == source) + if (a->altitude_baro_valid.source == source) p += snprintf(p, end-p, "\"altitude\","); + if (a->altitude_geom_valid.source == source) + p += snprintf(p, end-p, "\"alt_geom\","); if (a->track_valid.source == source) p += snprintf(p, end-p, "\"track\","); if (a->mag_heading_valid.source == source) @@ -1129,8 +1129,6 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s p += snprintf(p, end-p, "\"baro_rate\","); if (a->geom_rate_valid.source == source) p += snprintf(p, end-p, "\"geom_rate\","); - if (a->category_valid.source == source) - p += snprintf(p, end-p, "\"category\","); if (p[-1] != '[') --p; p += snprintf(p, end-p, "]"); @@ -1138,22 +1136,23 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s } static struct { - intent_modes_t flag; + nav_modes_t flag; const char *name; -} intent_modes_names[] = { - { INTENT_MODE_AUTOPILOT, "autopilot" }, - { INTENT_MODE_VNAV, "vnav" }, - { INTENT_MODE_ALT_HOLD, "althold" }, - { INTENT_MODE_APPROACH, "approach" }, - { INTENT_MODE_LNAV, "lnav" }, +} nav_modes_names[] = { + { NAV_MODE_AUTOPILOT, "autopilot" }, + { NAV_MODE_VNAV, "vnav" }, + { NAV_MODE_ALT_HOLD, "althold" }, + { NAV_MODE_APPROACH, "approach" }, + { NAV_MODE_LNAV, "lnav" }, + { NAV_MODE_TCAS, "tcas" }, { 0, NULL } }; -static char *append_intent_modes(char *p, char *end, intent_modes_t flags, const char *quote, const char *sep) +static char *append_nav_modes(char *p, char *end, nav_modes_t flags, const char *quote, const char *sep) { int first = 1; - for (int i = 0; intent_modes_names[i].name; ++i) { - if (!(flags & intent_modes_names[i].flag)) { + for (int i = 0; nav_modes_names[i].name; ++i) { + if (!(flags & nav_modes_names[i].flag)) { continue; } @@ -1162,16 +1161,16 @@ static char *append_intent_modes(char *p, char *end, intent_modes_t flags, const } first = 0; - p += snprintf(p, end-p, "%s%s%s", quote, intent_modes_names[i].name, quote); + p += snprintf(p, end-p, "%s%s%s", quote, nav_modes_names[i].name, quote); } return p; } -static const char *intent_modes_string(intent_modes_t flags) { +static const char *nav_modes_string(nav_modes_t flags) { static char buf[256]; buf[0] = 0; - append_intent_modes(buf, buf + sizeof(buf), flags, "", " "); + append_nav_modes(buf, buf + sizeof(buf), flags, "", " "); return buf; } @@ -1234,12 +1233,12 @@ char *generateAircraftJson(const char *url_path, int *len) { if (trackDataValid(&a->callsign_valid)) p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign)); if (trackDataValid(&a->position_valid)) - p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"nucp\":%u,\"seen_pos\":%.1f", a->lat, a->lon, a->pos_nuc, (now - a->position_valid.updated)/1000.0); + p += snprintf(p, end-p, ",\"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 (trackDataValid(&a->airground_valid) && a->airground_valid.source >= SOURCE_MODE_S_CHECKED && a->airground == AG_GROUND) p += snprintf(p, end-p, ",\"altitude\":\"ground\""); else { - if (trackDataValid(&a->altitude_valid)) - p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude); + if (trackDataValid(&a->altitude_baro_valid)) + p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude_baro); if (trackDataValid(&a->altitude_geom_valid)) p += snprintf(p, end-p, ",\"alt_geom\":%d", a->altitude_geom); } @@ -1256,7 +1255,7 @@ char *generateAircraftJson(const char *url_path, int *len) { if (trackDataValid(&a->true_heading_valid)) p += snprintf(p, end-p, ",\"true_heading\":%.1f", a->true_heading); if (trackDataValid(&a->gs_valid)) - p += snprintf(p, end-p, ",\"gs\":%u", a->gs); + p += snprintf(p, end-p, ",\"gs\":%.1f", a->gs); if (trackDataValid(&a->ias_valid)) p += snprintf(p, end-p, ",\"ias\":%u", a->ias); if (trackDataValid(&a->tas_valid)) @@ -1265,19 +1264,19 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, ",\"mach\":%.3f", a->mach); if (trackDataValid(&a->roll_valid)) p += snprintf(p, end-p, ",\"roll\":%.1f", a->roll); - if (trackDataValid(&a->category_valid)) + if (a->category != 0) p += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category); - if (trackDataValid(&a->intent_altitude_valid)) - p += snprintf(p, end-p, ",\"intent_alt\":%d", a->intent_altitude); - if (trackDataValid(&a->intent_heading_valid)) - p += snprintf(p, end-p, ",\"intent_heading\":%.1f", a->intent_heading); - if (trackDataValid(&a->intent_modes_valid)) { - p += snprintf(p, end-p, ",\"intent_modes\":["); - p = append_intent_modes(p, end, a->intent_modes, "\"", ","); + if (trackDataValid(&a->nav_altitude_valid)) + p += snprintf(p, end-p, ",\"nav_alt\":%d", a->nav_altitude); + if (trackDataValid(&a->nav_heading_valid)) + p += snprintf(p, end-p, ",\"nav_heading\":%.1f", a->nav_heading); + if (trackDataValid(&a->nav_modes_valid)) { + p += snprintf(p, end-p, ",\"nav_modes\":["); + p = append_nav_modes(p, end, a->nav_modes, "\"", ","); p += snprintf(p, end-p, "]"); } - if (trackDataValid(&a->alt_setting_valid)) - p += snprintf(p, end-p, ",\"alt_setting\":%.1f", a->alt_setting); + if (trackDataValid(&a->nav_qnh_valid)) + p += snprintf(p, end-p, ",\"nav_qnh\":%.1f", a->nav_qnh); p += snprintf(p, end-p, ",\"mlat\":"); p = append_flags(p, end, a, SOURCE_MLAT); @@ -1757,13 +1756,13 @@ static void modesReadFromClient(struct client *c) { } } -static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap) +__attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap) { p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); return p; } -static char *safe_snprintf(char *p, char *end, const char *format, ...) + __attribute__ ((format (printf,3,4))) static char *safe_snprintf(char *p, char *end, const char *format, ...) { va_list ap; va_start(ap, format); @@ -1773,7 +1772,7 @@ static char *safe_snprintf(char *p, char *end, const char *format, ...) } -static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...) +__attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...) { va_list ap; va_start(ap, format); @@ -1913,7 +1912,7 @@ static inline float heading_difference(float h1, float h2) return (d < 180) ? d : (360 - d); } -static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircraft *a, const data_validity *source, const char *format, ...) + __attribute__ ((format (printf,6,7))) static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircraft *a, const data_validity *source, const char *format, ...) { const char *sourcetype; switch (source->source) { @@ -1966,15 +1965,51 @@ static char *appendFATSVMeta(char *p, char *end, const char *field, struct aircr return p; } +static const char *airground_string(airground_t ag) +{ + switch (ag) { + case AG_AIRBORNE: + return "A+"; + case AG_GROUND: + return "G+"; + default: + return "?"; + } +} + +static void writeFATSVBanner() +{ + char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); + if (!p) + return; + char *end = p + TSV_MAX_PACKET_SIZE; + + p = appendFATSV(p, end, "faup1090_format_version", "%s", "2"); + + --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)); +} + static void writeFATSV() { struct aircraft *a; static uint64_t next_update; + static int first_run = 1; if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) { return; // not enabled or no active connections } + if (first_run) { + writeFATSVBanner(); + first_run = 0; + } + uint64_t now = mstime(); if (now < next_update) { return; @@ -1995,9 +2030,10 @@ static void writeFATSV() } // some special cases: - int altValid = trackDataValid(&a->altitude_valid); + 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); @@ -2005,14 +2041,14 @@ static void writeFATSV() // 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_valid.source < SOURCE_MODE_S_CHECKED) + 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 - a->fatsv_emitted_altitude) >= 50) || - (trackDataValid(&a->altitude_geom_valid) && abs(a->altitude_geom - a->fatsv_emitted_altitude_gnss) >= 50) || + (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) || (trackDataValid(&a->geom_rate_valid) && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) || (trackDataValid(&a->track_valid) && heading_difference(a->track, a->fatsv_emitted_track) >= 2) || @@ -2020,19 +2056,20 @@ static void writeFATSV() (trackDataValid(&a->roll_valid) && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) || (trackDataValid(&a->mag_heading_valid) && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) || (trackDataValid(&a->true_heading_valid) && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) || - (gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) || - (trackDataValid(&a->ias_valid) && unsigned_difference(a->ias, a->fatsv_emitted_speed_ias) >= 25) || - (trackDataValid(&a->tas_valid) && unsigned_difference(a->tas, a->fatsv_emitted_speed_tas) >= 25) || + (gsValid && fabs(a->gs - a->fatsv_emitted_gs) >= 25) || + (trackDataValid(&a->ias_valid) && unsigned_difference(a->ias, a->fatsv_emitted_ias) >= 25) || + (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 = - (trackDataValid(&a->intent_altitude_valid) && unsigned_difference(a->intent_altitude, a->fatsv_emitted_intent_altitude) > 50) || - (trackDataValid(&a->intent_heading_valid) && heading_difference(a->intent_heading, a->fatsv_emitted_intent_heading) > 2) || - (trackDataValid(&a->intent_modes_valid) && a->intent_modes != a->fatsv_emitted_intent_modes) || - (trackDataValid(&a->alt_setting_valid) && fabs(a->alt_setting - a->fatsv_emitted_alt_setting) > 0.8) || // 0.8 is the ES message resolution + (trackDataValid(&a->nav_altitude_valid) && unsigned_difference(a->nav_altitude, a->fatsv_emitted_nav_altitude) > 50) || + (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 (callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) || (airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || - (airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE); + (airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE) || + (squawkValid && a->squawk != a->fatsv_emitted_squawk); uint64_t minAge; if (immediate) { @@ -2042,11 +2079,11 @@ static void writeFATSV() // don't send mode S very often minAge = 30000; } else if ((airgroundValid && a->airground == AG_GROUND) || - (altValid && a->altitude < 500 && (!gsValid || a->gs < 200)) || - (gsValid && a->gs < 100 && (!altValid || a->altitude < 1000))) { + (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 < 10000) { + } else if (!altValid || a->altitude_baro < 10000) { // Below 10000 feet, emit up to every 5s when changing, 10s otherwise minAge = (changed ? 5000 : 10000); } else { @@ -2065,13 +2102,20 @@ static void writeFATSV() p = appendFATSV(p, end, "clock", "%" PRIu64, messageNow() / 1000); p = appendFATSV(p, end, (a->addr & MODES_NON_ICAO_ADDRESS) ? "otherid" : "hexid", "%06X", a->addr & 0xFFFFFF); - if (a->addrtype != ADDR_ADSB_ICAO) { + // 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_short_string(a->addrtype)); } - - if (a->adsb_version >= 0) { + if (forceEmit || a->adsb_version != a->fatsv_emitted_adsb_version) { p = appendFATSV(p, end, "adsbVer", "%d", a->adsb_version); } + if (forceEmit || a->category != a->fatsv_emitted_category) { + p = appendFATSV(p, end, "category", "%02X", a->category); + } // only emit alt, speed, latlon, track etc if they have been received since the last time // and are not stale @@ -2079,34 +2123,35 @@ static void writeFATSV() char *dataStart = p; // special cases - if (altValid) - p = appendFATSVMeta(p, end, "alt", a, &a->altitude_valid, "%d", a->altitude); if (airgroundValid) - p = appendFATSVMeta(p, end, "ag", a, &a->airground_valid, "%s", a->airground == AG_GROUND ? "G+" : "A+"); - if (strcmp(a->callsign, " ") != 0) + p = appendFATSVMeta(p, end, "airGround", a, &a->airground_valid, "%s", airground_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 (positionValid) - p = appendFATSVMeta(p, end, "pos", a, &a->position_valid, "{%.5f %.5f}", a->lat, a->lon); + 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, "nac_p", a, &a->nac_p_valid, "%u", a->nac_p); + } - p = appendFATSVMeta(p, end, "squawk", a, &a->squawk_valid, "%04x", a->squawk); - p = appendFATSVMeta(p, end, "altGeo", 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, "vrateGeo", a, &a->geom_rate_valid, "%d", a->geom_rate); - p = appendFATSVMeta(p, end, "gs", a, &a->gs_valid, "%u", a->gs); - p = appendFATSVMeta(p, end, "ias", a, &a->ias_valid, "%u", a->ias); - p = appendFATSVMeta(p, end, "tas", a, &a->tas_valid, "%u", a->tas); - p = appendFATSVMeta(p, end, "mach", a, &a->mach_valid, "%.3f", a->mach); - p = appendFATSVMeta(p, end, "trk", a, &a->track_valid, "%.0f", a->track); - p = appendFATSVMeta(p, end, "trkRate", 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, "hdgMag", a, &a->mag_heading_valid, "%.0f", a->mag_heading); - p = appendFATSVMeta(p, end, "hdgTrue", a, &a->true_heading_valid, "%.0f", a->true_heading); - if (a->category != 0xA0) - p = appendFATSVMeta(p, end, "category", a, &a->category_valid, "%02X", a->category); - p = appendFATSVMeta(p, end, "selAlt", a, &a->intent_altitude_valid,"%u", a->intent_altitude); - p = appendFATSVMeta(p, end, "selHdg", a, &a->intent_heading_valid, "%.0f", a->intent_heading); - p = appendFATSVMeta(p, end, "selModes", a, &a->intent_modes_valid, "{%s}", intent_modes_string(a->intent_modes)); - p = appendFATSVMeta(p, end, "qnh", a, &a->alt_setting_valid, "%.1f", a->alt_setting); + 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", a, &a->nav_altitude_valid, "%u", a->nav_altitude); + 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_string(a->nav_modes)); + p = appendFATSVMeta(p, end, "nav_qnh", a, &a->nav_qnh_valid, "%.1f", a->nav_qnh); // if we didn't get anything interesting, bail out. // We don't need to do anything special to unwind prepareWrite(). @@ -2122,12 +2167,13 @@ static void writeFATSV() else fprintf(stderr, "fatsv: output too large (max %d, overran by %d)\n", TSV_MAX_PACKET_SIZE, (int) (p - end)); - a->fatsv_emitted_altitude = a->altitude; - a->fatsv_emitted_altitude_gnss = a->altitude_geom; + 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_speed = a->gs; - a->fatsv_emitted_speed_ias = a->ias; + 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; @@ -2135,12 +2181,19 @@ 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_intent_altitude = a->intent_altitude; - a->fatsv_emitted_intent_heading = a->intent_heading; - a->fatsv_emitted_intent_modes = a->intent_modes; - a->fatsv_emitted_alt_setting = a->alt_setting; + a->fatsv_emitted_nav_altitude = a->nav_altitude; + 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_last_emitted = now; + if (forceEmit) { + a->fatsv_last_force_emit = now; + } } } diff --git a/track.c b/track.c index 2e39a23..ecfd066 100644 --- a/track.c +++ b/track.c @@ -76,15 +76,22 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { a->signalLevel[i] = 1e-5; a->signalNext = 0; + // defaults until we see a message otherwise + a->adsb_version = -1; + a->adsb_hrd = HEADING_MAGNETIC; + a->adsb_tah = HEADING_GROUND_TRACK; + + // prime FATSV defaults we only emit on change + // start off with the "last emitted" ACAS RA being blank (just the BDS 3,0 // or ES type code) a->fatsv_emitted_bds_30[0] = 0x30; a->fatsv_emitted_es_acas_ra[0] = 0xE2; + a->fatsv_emitted_adsb_version = -1; + a->fatsv_emitted_addrtype = ADDR_UNKNOWN; - // defaults until we see an op status message - a->adsb_version = -1; - a->adsb_hrd = HEADING_MAGNETIC; - a->adsb_tah = HEADING_GROUND_TRACK; + // don't immediately emit, let some data build up + a->fatsv_last_emitted = a->fatsv_last_force_emit = messageNow(); // Copy the first message so we can emit it later when a second message arrives. a->first_message = *mm; @@ -92,7 +99,7 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { // initialize data validity ages #define F(f,s,e) do { a->f##_valid.stale_interval = (s) * 1000; a->f##_valid.expire_interval = (e) * 1000; } while (0) F(callsign, 60, 70); // ADS-B or Comm-B - F(altitude, 15, 70); // ADS-B or Mode S + F(altitude_baro, 15, 70); // ADS-B or Mode S F(altitude_geom, 60, 70); // ADS-B only F(geom_delta, 60, 70); // ADS-B only F(gs, 60, 70); // ADS-B or Comm-B @@ -107,14 +114,22 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { F(baro_rate, 60, 70); // ADS-B or Comm-B F(geom_rate, 60, 70); // ADS-B or Comm-B F(squawk, 15, 70); // ADS-B or Mode S - F(category, 60, 70); // ADS-B only F(airground, 15, 70); // ADS-B or Mode S - F(alt_setting, 60, 70); // Comm-B only - F(intent_altitude, 60, 70); // ADS-B or Comm-B - F(intent_modes, 60, 70); // ADS-B or Comm-B + F(nav_qnh, 60, 70); // Comm-B only + F(nav_altitude, 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 F(cpr_even, 60, 70); // 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 + F(nac_p, 60, 70); // ADS-B only + F(nac_v, 60, 70); // ADS-B only + F(sil, 60, 70); // ADS-B only + F(gva, 60, 70); // ADS-B only + F(sda, 60, 70); // ADS-B only #undef F Modes.stats_current.unique_aircraft++; @@ -150,8 +165,8 @@ static int accept_data(data_validity *d, datasource_t source) d->source = source; d->updated = messageNow(); - d->stale = messageNow() + d->stale_interval; - d->expires = messageNow() + d->expire_interval; + d->stale = messageNow() + (d->stale_interval ? d->stale_interval : 60000); + d->expires = messageNow() + (d->expire_interval ? d->expire_interval : 70000); return 1; } @@ -287,13 +302,16 @@ static int speed_check(struct aircraft *a, double lat, double lon, int surface) return inrange; } -static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nuc) +static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nic, unsigned *rc) { int result; int fflag = mm->cpr_odd; int surface = (mm->cpr_type == CPR_SURFACE); - *nuc = (a->cpr_even_nuc < a->cpr_odd_nuc ? a->cpr_even_nuc : a->cpr_odd_nuc); // worst of the two positions + // derive NIC, Rc from the worse of the two position + // smaller NIC is worse; larger Rc 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); if (surface) { // surface global CPR @@ -303,8 +321,6 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, if (trackDataValid(&a->position_valid)) { // Ok to try aircraft relative first reflat = a->lat; reflon = a->lon; - if (a->pos_nuc < *nuc) - *nuc = a->pos_nuc; } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { reflat = Modes.fUserLat; reflon = Modes.fUserLon; @@ -356,7 +372,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, return result; // check speed limit - if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && a->pos_rc <= *rc && !speed_check(a, *lat, *lon, surface)) { Modes.stats_current.cpr_global_speed_checks++; return -2; } @@ -364,7 +380,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, return result; } -static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nuc) +static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, double *lon, unsigned *nic, unsigned *rc) { // relative CPR // find reference location @@ -374,14 +390,22 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, int fflag = mm->cpr_odd; int surface = (mm->cpr_type == CPR_SURFACE); - *nuc = mm->cpr_nucp; + if (fflag) { + *nic = a->cpr_odd_nic; + *rc = a->cpr_odd_rc; + } else { + *nic = a->cpr_even_nic; + *rc = a->cpr_even_rc; + } if (trackDataValid(&a->position_valid)) { reflat = a->lat; reflon = a->lon; - if (a->pos_nuc < *nuc) - *nuc = a->pos_nuc; + if (a->pos_nic < *nic) + *nic = a->pos_nic; + if (a->pos_rc < *rc) + *rc = a->pos_rc; range_limit = 50e3; } else if (!surface && (Modes.bUserFlags & MODES_USER_LATLON_VALID)) { @@ -430,7 +454,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, double *lat, } // check speed limit - if (trackDataValid(&a->position_valid) && a->pos_nuc >= *nuc && !speed_check(a, *lat, *lon, surface)) { + if (trackDataValid(&a->position_valid) && a->pos_nic >= *nic && 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 @@ -454,7 +478,8 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) int location_result = -1; uint64_t max_elapsed; double new_lat = 0, new_lon = 0; - unsigned new_nuc = 0; + unsigned new_nic = 0; + unsigned new_rc = 0; int surface; surface = (mm->cpr_type == CPR_SURFACE); @@ -463,7 +488,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) ++Modes.stats_current.cpr_surface; // Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise - if (mm->gs_valid && mm->gs <= 25) + if (mm->gs_valid && mm->gs.selected <= 25) max_elapsed = 50000; else max_elapsed = 25000; @@ -480,7 +505,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) a->cpr_odd_type == a->cpr_even_type && time_between(a->cpr_odd_valid.updated, a->cpr_even_valid.updated) <= max_elapsed) { - location_result = doGlobalCPR(a, mm, &new_lat, &new_lon, &new_nuc); + location_result = doGlobalCPR(a, mm, &new_lat, &new_lon, &new_nic, &new_rc); if (location_result == -2) { #ifdef DEBUG_CPR_CHECKS @@ -511,7 +536,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) // Otherwise try relative CPR. if (location_result == -1) { - location_result = doLocalCPR(a, mm, &new_lat, &new_lon, &new_nuc); + location_result = doLocalCPR(a, mm, &new_lat, &new_lon, &new_nic, &new_rc); if (location_result < 0) { Modes.stats_current.cpr_local_skipped++; @@ -532,16 +557,247 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm) mm->cpr_decoded = 1; mm->decoded_lat = new_lat; mm->decoded_lon = new_lon; + mm->decoded_nic = new_nic; + mm->decoded_rc = new_rc; // Update aircraft state a->lat = new_lat; a->lon = new_lon; - a->pos_nuc = new_nuc; + a->pos_nic = new_nic; + a->pos_rc = new_rc; update_range_histogram(new_lat, new_lon); } } +static unsigned compute_nic(unsigned metype, unsigned version, unsigned nic_a, unsigned nic_b, unsigned nic_c) +{ + switch (metype) { + case 5: // surface + case 9: // airborne + case 20: // airborne, GNSS altitude + return 11; + + case 6: // surface + case 10: // airborne + case 21: // airborne, GNSS altitude + return 10; + + case 7: // surface + if (version == 2) { + if (nic_a && !nic_c) { + return 9; + } else { + return 8; + } + } else if (version == 1) { + if (nic_a) { + return 9; + } else { + return 8; + } + } else { + return 8; + } + + case 8: // surface + if (version == 2) { + if (nic_a && nic_c) { + return 7; + } else if (nic_a && !nic_c) { + return 6; + } else if (!nic_a && nic_c) { + return 6; + } else { + return 0; + } + } else { + return 0; + } + + case 11: // airborne + if (version == 2) { + if (nic_a && nic_b) { + return 9; + } else { + return 8; + } + } else if (version == 1) { + if (nic_a) { + return 9; + } else { + return 8; + } + } else { + return 8; + } + + case 12: // airborne + return 7; + + case 13: // airborne + return 6; + + case 14: // airborne + return 5; + + case 15: // airborne + return 4; + + case 16: // airborne + if (nic_a && nic_b) { + return 3; + } else { + return 2; + } + + case 17: // airborne + return 1; + + default: + return 0; + } +} + +static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, unsigned nic_b, unsigned nic_c) +{ + switch (metype) { + case 5: // surface + case 9: // airborne + case 20: // airborne, GNSS altitude + return 8; // 7.5m + + case 6: // surface + case 10: // airborne + case 21: // airborne, GNSS altitude + return 25; + + case 7: // surface + if (version == 2) { + if (nic_a && !nic_c) { + return 75; + } else { + return 186; // 185.2m, 0.1NM + } + } else if (version == 1) { + if (nic_a) { + return 75; + } else { + return 186; // 185.2m, 0.1NM + } + } else { + return 186; // 185.2m, 0.1NM + } + + case 8: // surface + if (version == 2) { + if (nic_a && nic_c) { + return 371; // 370.4m, 0.2NM + } else if (nic_a && !nic_c) { + return 556; // 555.6m, 0.3NM + } else if (!nic_a && nic_c) { + return 926; // 926m, 0.5NM + } else { + return RC_UNKNOWN; + } + } else { + return RC_UNKNOWN; + } + + case 11: // airborne + if (version == 2) { + if (nic_a && nic_b) { + return 75; + } else { + return 186; // 370.4m, 0.2NM + } + } else if (version == 1) { + if (nic_a) { + return 75; + } else { + return 186; // 370.4m, 0.2NM + } + } else { + return 186; // 370.4m, 0.2NM + } + + case 12: // airborne + return 371; // 370.4m, 0.2NM + + case 13: // airborne + if (version == 2) { + if (!nic_a && nic_b) { + return 556; // 555.6m, 0.3NM + } else if (!nic_a && !nic_b) { + return 926; // 926m, 0.5NM + } else if (nic_a && nic_b) { + return 1112; // 1111.2m, 0.6NM + } else { + return 1112; // bad combination, assume worst Rc + } + } else if (version == 1) { + if (nic_a) { + return 1112; // 1111.2m, 0.6NM + } else { + return 926; // 926m, 0.5NM + } + } else { + return 926; // 926m, 0.5NM + } + + case 14: // airborne + return 1852; // 1.0NM + + case 15: // airborne + return 3704; // 2NM + + case 16: // airborne + if (version == 2) { + if (nic_a && nic_b) { + return 7408; // 4NM + } else { + return 14816; // 8NM + } + } else if (version == 1) { + if (nic_a) { + return 7408; // 4NM + } else { + return 14816; // 8NM + } + } else { + return 18510; // 10NM + } + + case 17: // airborne + return 37040; // 20NM + + default: + return RC_UNKNOWN; + } +} + +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); + int nic_b = (mm->accuracy.nic_b_valid && mm->accuracy.nic_b); + int nic_c = (trackDataValid(&a->nic_c_valid) && a->nic_c); + + *nic = compute_nic(mm->metype, a->adsb_version, nic_a, nic_b, nic_c); + *rc = compute_rc(mm->metype, a->adsb_version, nic_a, nic_b, nic_c); +} + +static int altitude_to_feet(int raw, altitude_unit_t unit) +{ + switch (unit) { + case UNIT_METERS: + return raw / 0.3048; + case UNIT_FEET: + return raw; + default: + return 0; + } +} + // //========================================================================= // @@ -583,16 +839,34 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) if (mm->source == SOURCE_ADSB && a->adsb_version < 0) a->adsb_version = 0; - if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source)) { + // category shouldn't change over time, don't bother with metadata + if (mm->category_valid) { + a->category = mm->category; + } + + // operational status message + // done early to update version / HRD / TAH + if (mm->opstatus.valid) { + a->adsb_version = mm->opstatus.version; + if (mm->opstatus.hrd != HEADING_INVALID) { + a->adsb_hrd = mm->opstatus.hrd; + } + if (mm->opstatus.tah != HEADING_INVALID) { + a->adsb_tah = mm->opstatus.tah; + } + } + + 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) { - int new_modeC = (a->altitude + 49) / 100; - int old_modeC = (mm->altitude + 49) / 100; + int new_modeC = (a->altitude_baro + 49) / 100; + int old_modeC = (alt + 49) / 100; if (new_modeC != old_modeC) { a->modeC_hit = 0; } } - a->altitude = mm->altitude; + a->altitude_baro = alt; } if (mm->squawk_valid && accept_data(&a->squawk_valid, mm->source)) { @@ -602,8 +876,8 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->squawk = mm->squawk; } - if (mm->altitude_valid && mm->altitude_source == ALTITUDE_GEOM && accept_data(&a->altitude_geom_valid, mm->source)) { - a->altitude_geom = mm->altitude; + if (mm->altitude_geom_valid && accept_data(&a->altitude_geom_valid, mm->source)) { + a->altitude_geom = altitude_to_feet(mm->altitude_geom, mm->altitude_geom_unit); } if (mm->geom_delta_valid && accept_data(&a->geom_delta_valid, mm->source)) { @@ -635,8 +909,11 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->roll = mm->roll; } - if (mm->gs_valid && accept_data(&a->gs_valid, mm->source)) { - a->gs = mm->gs; + if (mm->gs_valid) { + mm->gs.selected = (a->adsb_version == 2 ? mm->gs.v2 : mm->gs.v0); + if (accept_data(&a->gs_valid, mm->source)) { + a->gs = mm->gs.selected; + } } if (mm->ias_valid && accept_data(&a->ias_valid, mm->source)) { @@ -659,10 +936,6 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->geom_rate = mm->geom_rate; } - if (mm->category_valid && accept_data(&a->category_valid, mm->source)) { - a->category = mm->category; - } - if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source)) { a->airground = mm->airground; } @@ -673,22 +946,22 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) // prefer MCP over FMS // unless the source says otherwise - if (mm->intent.mcp_altitude_valid && mm->intent.altitude_source != INTENT_ALT_FMS && accept_data(&a->intent_altitude_valid, mm->source)) { - a->intent_altitude = mm->intent.mcp_altitude; - } else if (mm->intent.fms_altitude_valid && accept_data(&a->intent_altitude_valid, mm->source)) { - a->intent_altitude = mm->intent.fms_altitude; + 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->intent.heading_valid && accept_data(&a->intent_heading_valid, mm->source)) { - a->intent_heading = mm->intent.heading; + if (mm->nav.heading_valid && accept_data(&a->nav_heading_valid, mm->source)) { + a->nav_heading = mm->nav.heading; } - if (mm->intent.modes_valid && accept_data(&a->intent_modes_valid, mm->source)) { - a->intent_modes = mm->intent.modes; + if (mm->nav.modes_valid && accept_data(&a->nav_modes_valid, mm->source)) { + a->nav_modes = mm->nav.modes; } - if (mm->intent.alt_setting_valid && accept_data(&a->alt_setting_valid, mm->source)) { - a->alt_setting = mm->intent.alt_setting; + if (mm->nav.qnh_valid && accept_data(&a->nav_qnh_valid, mm->source)) { + a->nav_qnh = mm->nav.qnh; } // CPR, even @@ -696,7 +969,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->cpr_even_type = mm->cpr_type; a->cpr_even_lat = mm->cpr_lat; a->cpr_even_lon = mm->cpr_lon; - a->cpr_even_nuc = mm->cpr_nucp; + compute_nic_rc_from_message(mm, a, &a->cpr_even_nic, &a->cpr_even_rc); } // CPR, odd @@ -704,26 +977,52 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->cpr_odd_type = mm->cpr_type; a->cpr_odd_lat = mm->cpr_lat; a->cpr_odd_lon = mm->cpr_lon; - a->cpr_odd_nuc = mm->cpr_nucp; + compute_nic_rc_from_message(mm, a, &a->cpr_odd_nic, &a->cpr_odd_rc); } - // operational status message - if (mm->opstatus.valid) { - a->adsb_version = mm->opstatus.version; - if (mm->opstatus.version > 0) { - a->adsb_hrd = mm->opstatus.hrd; - a->adsb_tah = mm->opstatus.tah; + if (mm->accuracy.sda_valid && accept_data(&a->sda_valid, mm->source)) { + a->sda = mm->accuracy.sda; + } + + if (mm->accuracy.nic_a_valid && accept_data(&a->nic_a_valid, mm->source)) { + a->nic_a = mm->accuracy.nic_a; + } + + if (mm->accuracy.nic_c_valid && accept_data(&a->nic_c_valid, mm->source)) { + a->nic_c = mm->accuracy.nic_c; + } + + if (mm->accuracy.nac_p_valid && accept_data(&a->nac_p_valid, mm->source)) { + a->nac_p = mm->accuracy.nac_p; + } + + if (mm->accuracy.nac_v_valid && accept_data(&a->nac_v_valid, mm->source)) { + a->nac_v = mm->accuracy.nac_v; + } + + if (mm->accuracy.sil_valid && accept_data(&a->sil_valid, mm->source)) { + a->sil = mm->accuracy.sil; + if (mm->accuracy.sil_type != SIL_INVALID) { + a->sil_type = mm->accuracy.sil_type; } } + if (mm->accuracy.gva_valid && accept_data(&a->gva_valid, mm->source)) { + a->gva = mm->accuracy.gva; + } + + if (mm->accuracy.sda_valid && accept_data(&a->sda_valid, mm->source)) { + a->sda = mm->accuracy.sda; + } + // Now handle derived data // derive geometric altitude if we have baro + delta - if (compare_validity(&a->altitude_valid, &a->altitude_geom_valid) > 0 && + if (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 + a->geom_delta; - combine_validity(&a->altitude_geom_valid, &a->altitude_valid, &a->geom_delta_valid); + a->altitude_geom = a->altitude_baro + a->geom_delta; + combine_validity(&a->altitude_geom_valid, &a->altitude_baro_valid, &a->geom_delta_valid); } // If we've got a new cprlat or cprlon @@ -762,8 +1061,8 @@ static void trackMatchAC(uint64_t now) } // match on Mode C (+/- 100ft) - if (trackDataValid(&a->altitude_valid)) { - int modeC = (a->altitude + 49) / 100; + if (trackDataValid(&a->altitude_baro_valid)) { + int modeC = (a->altitude_baro + 49) / 100; unsigned modeA = modeCToModeA(modeC); unsigned i = modeAToIndex(modeA); @@ -844,7 +1143,7 @@ static void trackRemoveStaleAircraft(uint64_t now) #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); + EXPIRE(altitude_baro); EXPIRE(altitude_geom); EXPIRE(geom_delta); EXPIRE(gs); @@ -859,15 +1158,21 @@ static void trackRemoveStaleAircraft(uint64_t now) EXPIRE(baro_rate); EXPIRE(geom_rate); EXPIRE(squawk); - EXPIRE(category); EXPIRE(airground); - EXPIRE(alt_setting); - EXPIRE(intent_altitude); - EXPIRE(intent_heading); - EXPIRE(intent_modes); + EXPIRE(nav_qnh); + EXPIRE(nav_altitude); + 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 prev = a; a = a->next; } diff --git a/track.h b/track.h index f8ab089..f78af1a 100644 --- a/track.h +++ b/track.h @@ -64,6 +64,9 @@ */ #define TRACK_MODEAC_MIN_MESSAGES 4 +/* Special value for Rc unknown (100NM) */ +#define RC_UNKNOWN 185200 + // 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. @@ -92,8 +95,8 @@ struct aircraft { data_validity callsign_valid; char callsign[9]; // Flight number - data_validity altitude_valid; - int altitude; // Altitude (Baro) + data_validity altitude_baro_valid; + int altitude_baro; // Altitude (Baro) data_validity altitude_geom_valid; int altitude_geom; // Altitude (Geometric) @@ -102,7 +105,7 @@ struct aircraft { int geom_delta; // Difference between Geometric and Baro altitudes data_validity gs_valid; - unsigned gs; + float gs; data_validity ias_valid; unsigned ias; @@ -137,49 +140,71 @@ struct aircraft { data_validity squawk_valid; unsigned squawk; // Squawk - data_validity category_valid; unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte data_validity airground_valid; airground_t airground; // air/ground status - data_validity alt_setting_valid; - float alt_setting; // Altimeter setting (QNH/QFE), millibars + data_validity nav_qnh_valid; + float nav_qnh; // Altimeter setting (QNH/QFE), millibars - data_validity intent_altitude_valid; - unsigned intent_altitude; // intent altitude (FMS or FCU selected altitude) + data_validity nav_altitude_valid; + unsigned nav_altitude; // FMS or FCU selected altitude - data_validity intent_heading_valid; - float intent_heading; // intent heading, degrees (0-359) + data_validity nav_heading_valid; + float nav_heading; // target heading, degrees (0-359) - data_validity intent_modes_valid; - intent_modes_t intent_modes; // enabled modes (autopilot, vnav, etc) + data_validity nav_modes_valid; + nav_modes_t nav_modes; // enabled modes (autopilot, vnav, etc) data_validity cpr_odd_valid; // Last seen even CPR message cpr_type_t cpr_odd_type; unsigned cpr_odd_lat; unsigned cpr_odd_lon; - unsigned cpr_odd_nuc; + unsigned cpr_odd_nic; + unsigned cpr_odd_rc; data_validity cpr_even_valid; // Last seen odd CPR message cpr_type_t cpr_even_type; unsigned cpr_even_lat; unsigned cpr_even_lon; - unsigned cpr_even_nuc; + unsigned cpr_even_nic; + unsigned cpr_even_rc; data_validity position_valid; double lat, lon; // Coordinated obtained from CPR encoded data - unsigned pos_nuc; // NUCp of last computed position + unsigned pos_nic; // NIC of last computed position + unsigned pos_rc; // Rc of last computed position + // data extracted from opstatus etc int adsb_version; // ADS-B version (from ADS-B operational status); -1 means no ADS-B messages seen heading_type_t adsb_hrd; // Heading Reference Direction setting (from ADS-B operational status) heading_type_t adsb_tah; // Track Angle / Heading setting (from ADS-B operational status) + data_validity nic_a_valid; + data_validity nic_c_valid; + data_validity nic_baro_valid; + data_validity nac_p_valid; + data_validity nac_v_valid; + data_validity sil_valid; + data_validity gva_valid; + data_validity sda_valid; + + unsigned nic_a : 1; // NIC supplement A from opstatus + unsigned nic_c : 1; // NIC supplement C from opstatus + unsigned nic_baro : 1; // NIC baro supplement from TSS or opstatus + unsigned nac_p : 4; // NACp from TSS or opstatus + unsigned nac_v : 3; // NACv from opstatus + unsigned sil : 2; // SIL from TS or opstatus + sil_type_t sil_type; // SIL supplement from TS or opstatus + unsigned gva : 2; // GVA from opstatus + unsigned sda : 2; // SDA from opstatus + int modeA_hit; // did our squawk match a possible mode A reply in the last check period? int modeC_hit; // did our altitude match a possible mode C reply in the last check period? - int fatsv_emitted_altitude; // last FA emitted altitude - int fatsv_emitted_altitude_gnss; // -"- GNSS altitude + int fatsv_emitted_altitude_baro; // last FA emitted altitude + int fatsv_emitted_altitude_geom; // -"- GNSS altitude int fatsv_emitted_baro_rate; // -"- barometric rate int fatsv_emitted_geom_rate; // -"- geometric rate float fatsv_emitted_track; // -"- true track @@ -187,22 +212,27 @@ struct aircraft { float fatsv_emitted_mag_heading; // -"- magnetic heading float fatsv_emitted_true_heading; // -"- true heading float fatsv_emitted_roll; // -"- roll angle - unsigned fatsv_emitted_speed; // -"- groundspeed - unsigned fatsv_emitted_speed_ias; // -"- IAS - unsigned fatsv_emitted_speed_tas; // -"- TAS + float fatsv_emitted_gs; // -"- groundspeed + unsigned fatsv_emitted_ias; // -"- IAS + unsigned fatsv_emitted_tas; // -"- TAS float fatsv_emitted_mach; // -"- Mach number airground_t fatsv_emitted_airground; // -"- air/ground state - unsigned fatsv_emitted_intent_altitude; // -"- intent altitude - float fatsv_emitted_intent_heading; // -"- intent heading - intent_modes_t fatsv_emitted_intent_modes; // -"- enabled modes - float fatsv_emitted_alt_setting; // -"- altimeter setting + unsigned fatsv_emitted_nav_altitude; // -"- target altitude + float fatsv_emitted_nav_heading; // -"- target heading + nav_modes_t fatsv_emitted_nav_modes; // -"- enabled navigation modes + float fatsv_emitted_nav_qnh; // -"- altimeter setting unsigned char fatsv_emitted_bds_10[7]; // -"- BDS 1,0 message unsigned char fatsv_emitted_bds_30[7]; // -"- BDS 3,0 message unsigned char fatsv_emitted_es_status[7]; // -"- ES operational status message unsigned char fatsv_emitted_es_acas_ra[7]; // -"- ES ACAS RA report message char fatsv_emitted_callsign[9]; // -"- callsign + addrtype_t fatsv_emitted_addrtype; // -"- address type (assumed ADSB_ICAO initially) + int fatsv_emitted_adsb_version; // -"- ADS-B version (assumed non-ADS-B initially) + unsigned fatsv_emitted_category; // -"- ADS-B emitter category (assumed A0 initially) + unsigned fatsv_emitted_squawk; // -"- squawk uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted + uint64_t fatsv_last_force_emit; // time (millis) we last emitted only-on-change data struct aircraft *next; // Next aircraft in our linked list From 079061df3c3b8048051941277fdb379862bb7962 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 7 Dec 2017 13:36:07 -0600 Subject: [PATCH 19/52] SIL stuff --- dump1090.h | 7 +++---- mode_s.c | 41 ++++++++++++++++++++++++++++++++--------- track.c | 4 ++-- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/dump1090.h b/dump1090.h index 55bcedd..6cc0275 100644 --- a/dump1090.h +++ b/dump1090.h @@ -178,7 +178,7 @@ typedef enum { } airground_t; typedef enum { - SIL_INVALID, SIL_PER_SAMPLE, SIL_PER_HOUR + SIL_INVALID, SIL_UNKNOWN, SIL_PER_SAMPLE, SIL_PER_HOUR } sil_type_t; typedef enum { @@ -509,7 +509,6 @@ struct modesMessage { unsigned nic_baro_valid : 1; unsigned nac_p_valid : 1; unsigned nac_v_valid : 1; - unsigned sil_valid : 1; unsigned gva_valid : 1; unsigned sda_valid : 1; @@ -521,8 +520,8 @@ struct modesMessage { unsigned nac_p : 4; // if nac_p_valid unsigned nac_v : 3; // if nac_v_valid - unsigned sil : 2; // if sil_valid - sil_type_t sil_type; // if sil_valid + unsigned sil : 2; // if sil_type != SIL_INVALID + sil_type_t sil_type; unsigned gva : 2; // if gva_valid diff --git a/mode_s.c b/mode_s.c index 3f30c59..9e5f206 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1103,9 +1103,8 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) mm->accuracy.nic_baro = getbit(me, 44); // 45-46: SIL - mm->accuracy.sil_valid = 1; mm->accuracy.sil = getbits(me, 45, 46); - mm->accuracy.sil_type = SIL_INVALID; + mm->accuracy.sil_type = SIL_UNKNOWN; // 47-51: reserved @@ -1170,9 +1169,8 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) mm->accuracy.nic_baro = getbit(me, 44); // 45-46: SIL - mm->accuracy.sil_valid = 1; mm->accuracy.sil = getbits(me, 45, 46); - mm->accuracy.sil_type = SIL_INVALID; + mm->accuracy.sil_type = SIL_UNKNOWN; // 47: mode bits validity if (getbit(me, 47)) { @@ -1241,7 +1239,7 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) mm->accuracy.nic_a = getbit(me, 44); mm->accuracy.nac_p_valid = 1; mm->accuracy.nac_p = getbits(me, 45, 48); - mm->accuracy.sil_valid = 1; + mm->accuracy.sil_type = SIL_UNKNOWN; mm->accuracy.sil = getbits(me, 51, 52); mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; @@ -1291,7 +1289,6 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf) mm->accuracy.nic_a = getbit(me, 44); mm->accuracy.nac_p_valid = 1; mm->accuracy.nac_p = getbits(me, 45, 48); - mm->accuracy.sil_valid = 1; mm->accuracy.sil = getbits(me, 51, 52); mm->accuracy.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR; mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE; @@ -1583,6 +1580,16 @@ static const char *nav_modes_to_string(nav_modes_t flags) return buf; } +static const char *sil_type_to_string(sil_type_t type) +{ + switch (type) { + case SIL_UNKNOWN: return "unknown type"; + case SIL_PER_HOUR: return "per flight hour"; + case SIL_PER_SAMPLE: return "per sample"; + default: return "invalid type"; + } +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -1959,10 +1966,26 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->accuracy.gva_valid) { printf(" GVA: %d\n", mm->accuracy.gva); } - if (mm->accuracy.sil_valid) { - printf(" SIL: %d (%s)\n", + if (mm->accuracy.sil_type != SIL_INVALID) { + const char *sil_description; + switch (mm->accuracy.sil) { + case 1: + sil_description = "p <= 0.1%"; + break; + case 2: + sil_description = "p <= 0.001%"; + break; + case 3: + sil_description = "p <= 0.00001%"; + break; + default: + sil_description = "p > 0.1%"; + break; + } + printf(" SIL: %d (%s, %s)\n", mm->accuracy.sil, - (mm->accuracy.sil_type == SIL_PER_HOUR ? "per hour" : "per sample")); + sil_description, + sil_type_to_string(mm->accuracy.sil_type)); } if (mm->accuracy.sda_valid) { printf(" SDA: %d\n", mm->accuracy.sda); diff --git a/track.c b/track.c index ecfd066..1f4ed8e 100644 --- a/track.c +++ b/track.c @@ -1000,9 +1000,9 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->nac_v = mm->accuracy.nac_v; } - if (mm->accuracy.sil_valid && accept_data(&a->sil_valid, mm->source)) { + if (mm->accuracy.sil_type != SIL_INVALID && accept_data(&a->sil_valid, mm->source)) { a->sil = mm->accuracy.sil; - if (mm->accuracy.sil_type != SIL_INVALID) { + if (a->sil_type == SIL_INVALID || mm->accuracy.sil_type != SIL_UNKNOWN) { a->sil_type = mm->accuracy.sil_type; } } From ea1e9c336bb2e18816a4eaf1b48a0b2238f9ff42 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 14:43:58 +0000 Subject: [PATCH 20/52] Decode/emit emergency/priority status --- dump1090.h | 14 ++++++++++++++ mode_s.c | 26 ++++++++++++++++++++++++-- net_io.c | 15 +++++++++++++++ track.c | 4 ++++ track.h | 3 +++ 5 files changed, 60 insertions(+), 2 deletions(-) diff --git a/dump1090.h b/dump1090.h index 6cc0275..c71f5f0 100644 --- a/dump1090.h +++ b/dump1090.h @@ -215,6 +215,18 @@ typedef enum { NAV_MODE_TCAS = 32 } nav_modes_t; +// Matches encoding of the ES type 28/1 emergency/priority status subfield +typedef enum { + EMERGENCY_NONE = 0, + EMERGENCY_GENERAL = 1, + EMERGENCY_LIFEGUARD = 2, + EMERGENCY_MINFUEL = 3, + EMERGENCY_NORDO = 4, + EMERGENCY_UNLAWFUL = 5, + EMERGENCY_DOWNED = 6, + EMERGENCY_RESERVED = 7 +} emergency_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -450,6 +462,7 @@ struct modesMessage { unsigned spi : 1; unsigned alert_valid : 1; unsigned alert : 1; + unsigned emergency_valid : 1; unsigned metype; // DF17/18 ME type unsigned mesub; // DF17/18 ME subtype @@ -486,6 +499,7 @@ struct modesMessage { unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits char callsign[9]; // 8 chars flight number, NUL-terminated unsigned category; // A0 - D7 encoded as a single hex byte + emergency_t emergency; // emergency/priority status // valid if cpr_valid cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B) diff --git a/mode_s.c b/mode_s.c index 9e5f206..54e9c74 100644 --- a/mode_s.c +++ b/mode_s.c @@ -986,7 +986,10 @@ static void decodeESAircraftStatus(struct modesMessage *mm, int check_imf) mm->mesub = getbits(me, 6, 8); if (mm->mesub == 1) { // Emergency status squawk field - int ID13Field = getbits(me, 12, 24); + mm->emergency_valid = 1; + mm->emergency = (emergency_t) getbits(me, 9, 11); + + unsigned ID13Field = getbits(me, 12, 24); if (ID13Field) { mm->squawk_valid = 1; mm->squawk = decodeID13Field(ID13Field); @@ -1130,7 +1133,9 @@ static void decodeESTargetStatus(struct modesMessage *mm, int check_imf) } - // 54-56: emergency/priority (ignored) + // 54-56: emergency/priority + mm->emergency_valid = 1; + mm->emergency = (emergency_t) getbits(me, 54, 56); } else if (mm->mesub == 1) { // Target state and status, V2 // 8: SIL unsigned is_fms = getbit(me, 9); @@ -1590,6 +1595,20 @@ static const char *sil_type_to_string(sil_type_t type) } } +static const char *emergency_to_string(emergency_t emergency) +{ + switch (emergency) { + case EMERGENCY_NONE: return "no emergency"; + case EMERGENCY_GENERAL: return "general emergency (7700)"; + case EMERGENCY_LIFEGUARD: return "lifeguard / medical emergency"; + case EMERGENCY_MINFUEL: return "minimum fuel"; + case EMERGENCY_NORDO: return "no communications (7600)"; + case EMERGENCY_UNLAWFUL: return "unlawful interference (7500)"; + case EMERGENCY_DOWNED: return "downed aircraft"; + default: return "reserved"; + } +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -2050,6 +2069,9 @@ void displayModesMessage(struct modesMessage *mm) { printf(" Nav modes: %s\n", nav_modes_to_string(mm->nav.modes)); } + if (mm->emergency_valid) { + printf(" Emergency/priority: %s\n", emergency_to_string(mm->emergency)); + } printf("\n"); fflush(stdout); diff --git a/net_io.c b/net_io.c index 1ba7635..b69da15 100644 --- a/net_io.c +++ b/net_io.c @@ -1977,6 +1977,20 @@ static const char *airground_string(airground_t ag) } } +static const char *emergency_enum_string(emergency_t emergency) +{ + switch (emergency) { + case EMERGENCY_NONE: return "none"; + case EMERGENCY_GENERAL: return "general"; + case EMERGENCY_LIFEGUARD: return "lifeguard"; + case EMERGENCY_MINFUEL: return "minfuel"; + case EMERGENCY_NORDO: return "nordo"; + case EMERGENCY_UNLAWFUL: return "unlawful"; + case EMERGENCY_DOWNED: return "downed"; + default: return "reserved"; + } +} + static void writeFATSVBanner() { char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); @@ -2152,6 +2166,7 @@ static void writeFATSV() 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_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(). diff --git a/track.c b/track.c index 1f4ed8e..dd1b0d0 100644 --- a/track.c +++ b/track.c @@ -876,6 +876,10 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->squawk = mm->squawk; } + if (mm->emergency_valid && accept_data(&a->emergency_valid, mm->source)) { + a->emergency = mm->emergency; + } + if (mm->altitude_geom_valid && accept_data(&a->altitude_geom_valid, mm->source)) { a->altitude_geom = altitude_to_feet(mm->altitude_geom, mm->altitude_geom_unit); } diff --git a/track.h b/track.h index f78af1a..40323d4 100644 --- a/track.h +++ b/track.h @@ -140,6 +140,9 @@ struct aircraft { data_validity squawk_valid; unsigned squawk; // Squawk + data_validity emergency_valid; + emergency_t emergency; // Emergency/priority status + unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte data_validity airground_valid; From a947163dee30bb2c6b7a0603f5cf0c63793be7c9 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 14:44:20 +0000 Subject: [PATCH 21/52] Fix ADSBv0 ground movement decoding --- mode_s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode_s.c b/mode_s.c index 54e9c74..33e0666 100644 --- a/mode_s.c +++ b/mode_s.c @@ -217,7 +217,7 @@ static float decodeMovementFieldV0(unsigned movement) { else if (movement >= 39) return 15 + (movement - 39 + 0.5) * 1; // 15 < gs <= 70 in 1kt steps else if (movement >= 13) return 2 + (movement - 13 + 0.5) * 0.50; // 2 < gs <= 15 in 0.5kt steps else if (movement >= 9) return 1 + (movement - 9 + 0.5) * 0.25; // 1 < gs <= 2 in 0.25kt steps - else if (movement >= 2) return 0.125 + (movement - 3 + 0.5) * 0.125; // 0.125 < gs <= 1 in 0.125kt step + else if (movement >= 2) return 0.125 + (movement - 2 + 0.5) * 0.125; // 0.125 < gs <= 1 in 0.125kt step // 1: stopped, gs < 0.125kt // 0: no data else return 0; From 63d1b4b553c72f8ea2d747bfecb9c81098279a64 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 14:45:18 +0000 Subject: [PATCH 22/52] Consistency in *_enum_string naming --- net_io.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net_io.c b/net_io.c index b69da15..a6be677 100644 --- a/net_io.c +++ b/net_io.c @@ -1167,14 +1167,14 @@ static char *append_nav_modes(char *p, char *end, nav_modes_t flags, const char return p; } -static const char *nav_modes_string(nav_modes_t flags) { +static const char *nav_modes_flags_string(nav_modes_t flags) { static char buf[256]; buf[0] = 0; append_nav_modes(buf, buf + sizeof(buf), flags, "", " "); return buf; } -static const char *addrtype_short_string(addrtype_t type) { +static const char *addrtype_enum_string(addrtype_t type) { switch (type) { case ADDR_ADSB_ICAO: return "adsb_icao"; @@ -1225,7 +1225,7 @@ char *generateAircraftJson(const char *url_path, int *len) { p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); if (a->addrtype != ADDR_ADSB_ICAO) - p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_short_string(a->addrtype)); + p += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_enum_string(a->addrtype)); if (a->adsb_version >= 0) p += snprintf(p, end-p, ",\"version\":%d", a->adsb_version); if (trackDataValid(&a->squawk_valid)) @@ -1830,7 +1830,7 @@ static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafiel 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) { - p = appendFATSV(p, end, "addrtype", "%s", addrtype_short_string(mm->addrtype)); + p = appendFATSV(p, end, "addrtype", "%s", addrtype_enum_string(mm->addrtype)); } p = safe_snprintf(p, end, "%s\t", datafield); @@ -1965,7 +1965,7 @@ static inline float heading_difference(float h1, float h2) return p; } -static const char *airground_string(airground_t ag) +static const char *airground_enum_string(airground_t ag) { switch (ag) { case AG_AIRBORNE: @@ -2122,7 +2122,7 @@ static void writeFATSV() // 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_short_string(a->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, "adsbVer", "%d", a->adsb_version); @@ -2138,7 +2138,7 @@ static void writeFATSV() // special cases if (airgroundValid) - p = appendFATSVMeta(p, end, "airGround", a, &a->airground_valid, "%s", airground_string(a->airground)); + 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) @@ -2164,7 +2164,7 @@ static void writeFATSV() 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, "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_string(a->nav_modes)); + 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)); From 571ea7ac4118668fdf8fa57e8fc07f655398a99c Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 14:46:33 +0000 Subject: [PATCH 23/52] Emit integrity info, when it changes. --- net_io.c | 28 +++++++++++++++++++++++++++- track.h | 13 ++++++++----- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/net_io.c b/net_io.c index a6be677..1568022 100644 --- a/net_io.c +++ b/net_io.c @@ -1991,6 +1991,16 @@ static const char *emergency_enum_string(emergency_t emergency) } } +static const char *sil_type_enum_string(sil_type_t type) +{ + switch (type) { + case SIL_UNKNOWN: return "unknown"; + case SIL_PER_HOUR: return "perhour"; + case SIL_PER_SAMPLE: return "persample"; + default: return "invalid"; + } +} + static void writeFATSVBanner() { char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); @@ -2130,6 +2140,18 @@ static void writeFATSV() 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 || a->sil_type != a->fatsv_emitted_sil_type)) { + p = appendFATSVMeta(p, end, "sil", a, &a->sil_valid, "{%u %s}", a->sil, 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 @@ -2147,7 +2169,6 @@ static void writeFATSV() 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, "nac_p", a, &a->nac_p_valid, "%u", a->nac_p); } p = appendFATSVMeta(p, end, "alt_gnss", a, &a->altitude_geom_valid, "%d", a->altitude_geom); @@ -2205,6 +2226,11 @@ static void writeFATSV() 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_last_emitted = now; if (forceEmit) { a->fatsv_last_force_emit = now; diff --git a/track.h b/track.h index 40323d4..3d7475c 100644 --- a/track.h +++ b/track.h @@ -197,11 +197,9 @@ struct aircraft { unsigned nic_c : 1; // NIC supplement C from opstatus unsigned nic_baro : 1; // NIC baro supplement from TSS or opstatus unsigned nac_p : 4; // NACp from TSS or opstatus - unsigned nac_v : 3; // NACv from opstatus - unsigned sil : 2; // SIL from TS or opstatus - sil_type_t sil_type; // SIL supplement from TS or opstatus - unsigned gva : 2; // GVA from opstatus - unsigned sda : 2; // SDA from opstatus + unsigned nac_v : 3; // NACv from airborne velocity or opstatus + unsigned sil : 2; // SIL from TSS or opstatus + sil_type_t sil_type; // SIL supplement from TSS or opstatus int modeA_hit; // did our squawk match a possible mode A reply in the last check period? int modeC_hit; // did our altitude match a possible mode C reply in the last check period? @@ -233,6 +231,11 @@ struct aircraft { int fatsv_emitted_adsb_version; // -"- ADS-B version (assumed non-ADS-B initially) unsigned fatsv_emitted_category; // -"- ADS-B emitter category (assumed A0 initially) unsigned fatsv_emitted_squawk; // -"- squawk + unsigned fatsv_emitted_nac_p; // -"- NACp + unsigned fatsv_emitted_nac_v; // -"- NACv + unsigned fatsv_emitted_sil; // -"- SIL + sil_type_t fatsv_emitted_sil_type; // -"- SIL supplement + unsigned fatsv_emitted_nic_baro; // -"- NICbaro uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted uint64_t fatsv_last_force_emit; // time (millis) we last emitted only-on-change data From 2ce2af16852be4260c61642db18ca831ad089ec3 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 14:47:08 +0000 Subject: [PATCH 24/52] Tweak when we allow updates to air/ground status. --- track.c | 11 +++++++++-- track.h | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/track.c b/track.c index dd1b0d0..22eba28 100644 --- a/track.c +++ b/track.c @@ -940,8 +940,15 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->geom_rate = mm->geom_rate; } - if (mm->airground != AG_INVALID && accept_data(&a->airground_valid, mm->source)) { - a->airground = mm->airground; + if (mm->airground != AG_INVALID) { + // If our current state is UNCERTAIN, accept new data as normal + // If our current state is certain but new data is not, only accept the uncertain state if the certain data has gone stale + if (mm->airground != AG_UNCERTAIN || + (mm->airground == AG_UNCERTAIN && !trackDataFresh(&a->airground_valid))) { + if (accept_data(&a->airground_valid, mm->source)) { + a->airground = mm->airground; + } + } } if (mm->callsign_valid && accept_data(&a->callsign_valid, mm->source)) { diff --git a/track.h b/track.h index 3d7475c..e52fefc 100644 --- a/track.h +++ b/track.h @@ -259,6 +259,12 @@ static inline int trackDataValid(const data_validity *v) return (v->source != SOURCE_INVALID && messageNow() < v->expires); } +/* is this bit of data fresh? */ +static inline int trackDataFresh(const data_validity *v) +{ + return (v->source != SOURCE_INVALID && messageNow() < v->stale); +} + /* what's the age of this data, in milliseconds? */ static inline uint64_t trackDataAge(const data_validity *v) { From 0c3671429bd481008aa9b3601c76704787c164e2 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 14:49:16 +0000 Subject: [PATCH 25/52] Category 0 means not set. --- track.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/track.h b/track.h index e52fefc..012868d 100644 --- a/track.h +++ b/track.h @@ -143,7 +143,7 @@ struct aircraft { data_validity emergency_valid; emergency_t emergency; // Emergency/priority status - unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte + unsigned category; // Aircraft category A0 - D7 encoded as a single hex byte. 00 = unset data_validity airground_valid; airground_t airground; // air/ground status From daebc372a996bd54002b737f6277c96787fa16ac Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 17:07:47 +0000 Subject: [PATCH 26/52] Write a version header on faup1090 startup --- faup1090.c | 1 + net_io.c | 20 ++++++++++++++++++++ net_io.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/faup1090.c b/faup1090.c index e476a7e..425bc92 100644 --- a/faup1090.c +++ b/faup1090.c @@ -204,6 +204,7 @@ 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/net_io.c b/net_io.c index 1568022..c0700b8 100644 --- a/net_io.c +++ b/net_io.c @@ -1786,6 +1786,26 @@ __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *en } #define TSV_MAX_PACKET_SIZE 400 +#define TSV_VERSION 2 + +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, "tsvVersion", "%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)); +} static void writeFATSVPositionUpdate(float lat, float lon, float alt) { diff --git a/net_io.h b/net_io.h index 2c39f6f..0dd3ea8 100644 --- a/net_io.h +++ b/net_io.h @@ -87,6 +87,8 @@ 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); From 44e8913c2d7f5aac549cd851ecd8c0d85be2ea0a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Jan 2018 17:13:34 +0000 Subject: [PATCH 27/52] Whitespace cleanups --- dump1090.c | 2 +- mode_s.c | 2 +- net_io.c | 16 ++++++++-------- track.c | 2 +- track.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dump1090.c b/dump1090.c index eae1223..d02ddff 100644 --- a/dump1090.c +++ b/dump1090.c @@ -371,7 +371,7 @@ void backgroundTasks(void) { trackPeriodicUpdate(); if (Modes.net) { - modesNetPeriodicWork(); + modesNetPeriodicWork(); } diff --git a/mode_s.c b/mode_s.c index 33e0666..2319593 100644 --- a/mode_s.c +++ b/mode_s.c @@ -716,7 +716,7 @@ static void decodeESIdentAndCategory(struct modesMessage *mm) mm->callsign[6] = ais_charset[getbits(me, 45, 50)]; mm->callsign[7] = ais_charset[getbits(me, 51, 56)]; mm->callsign[8] = 0; - + // A common failure mode seems to be to intermittently send // all zeros. Catch that here. mm->callsign_valid = (strcmp(mm->callsign, "@@@@@@@@") != 0); diff --git a/net_io.c b/net_io.c index c0700b8..7229194 100644 --- a/net_io.c +++ b/net_io.c @@ -1761,7 +1761,7 @@ __attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); return p; } - + __attribute__ ((format (printf,3,4))) static char *safe_snprintf(char *p, char *end, const char *format, ...) { va_list ap; @@ -1771,7 +1771,7 @@ __attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char return p; } - + __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...) { va_list ap; @@ -1780,8 +1780,8 @@ __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *en p = safe_snprintf(p, end, "%s\t", field); p = safe_vsnprintf(p, end, format, ap); p = safe_snprintf(p, end, "\t"); - - va_end(ap); + + va_end(ap); return p; } @@ -1957,17 +1957,17 @@ static inline float heading_difference(float h1, float h2) // expired data return p; } - + if (source->updated > messageNow()) { // data in the future return p; } - + if (source->updated < a->fatsv_last_emitted) { // not updated since last time return p; } - + uint64_t age = (messageNow() - source->updated) / 1000; if (age > 255) { // too old @@ -2075,7 +2075,7 @@ static void writeFATSV() // 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 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; diff --git a/track.c b/track.c index 22eba28..9fa626a 100644 --- a/track.c +++ b/track.c @@ -815,7 +815,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) } _messageNow = mm->sysTimestampMsg; - + // Lookup our aircraft or create a new one a = trackFindAircraft(mm->addr); if (!a) { // If it's a currently unknown aircraft.... diff --git a/track.h b/track.h index 012868d..721f7cc 100644 --- a/track.h +++ b/track.h @@ -73,7 +73,7 @@ // expired: data is not valid. typedef struct { uint64_t stale_interval; /* how long after an update until the data is stale */ - uint64_t expire_interval; /* how long after an update until the data expires */ + uint64_t expire_interval; /* how long after an update until the data expires */ datasource_t source; /* where the data came from */ uint64_t updated; /* when it arrived */ From 1d5db2fd7624547390c839a3c6c1358721dc19f4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 20 Feb 2018 00:08:26 +1300 Subject: [PATCH 28/52] Fix baro/geometric rate flag sense in airborne velocity messages --- mode_s.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mode_s.c b/mode_s.c index 2319593..9f943b4 100644 --- a/mode_s.c +++ b/mode_s.c @@ -836,15 +836,15 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf) // 37: vert rate sign // 38-46: vert rate magnitude unsigned vert_rate = getbits(me, 38, 46); - unsigned vert_rate_is_geom = getbit(me, 36); + unsigned vert_rate_is_baro = getbit(me, 36); if (vert_rate) { int rate = (vert_rate - 1) * (getbit(me, 37) ? -64 : 64); - if (vert_rate_is_geom) { - mm->geom_rate = rate; - mm->geom_rate_valid = 1; - } else { + if (vert_rate_is_baro) { mm->baro_rate = rate; mm->baro_rate_valid = 1; + } else { + mm->geom_rate = rate; + mm->geom_rate_valid = 1; } } From d6c06127674959be37fa3789f55a4fdf81fb7003 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 20 Feb 2018 00:24:39 +1300 Subject: [PATCH 29/52] Remove doubled-up banner/header code --- net_io.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/net_io.c b/net_io.c index 7229194..5cfc629 100644 --- a/net_io.c +++ b/net_io.c @@ -2021,39 +2021,15 @@ static const char *sil_type_enum_string(sil_type_t type) } } -static void writeFATSVBanner() -{ - char *p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); - if (!p) - return; - char *end = p + TSV_MAX_PACKET_SIZE; - - p = appendFATSV(p, end, "faup1090_format_version", "%s", "2"); - - --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)); -} - static void writeFATSV() { struct aircraft *a; static uint64_t next_update; - static int first_run = 1; if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) { return; // not enabled or no active connections } - if (first_run) { - writeFATSVBanner(); - first_run = 0; - } - uint64_t now = mstime(); if (now < next_update) { return; From 286d8e7a10779da4b24a92e62422ed2e888c6045 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 20 Feb 2018 00:25:15 +1300 Subject: [PATCH 30/52] tsvVersion -> tsv_version --- net_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net_io.c b/net_io.c index 5cfc629..bb3ae1a 100644 --- a/net_io.c +++ b/net_io.c @@ -1796,8 +1796,8 @@ void writeFATSVHeader() char *end = p + TSV_MAX_PACKET_SIZE; - p = appendFATSV(p, end, "clock", "%" PRIu64, mstime() / 1000); - p = appendFATSV(p, end, "tsvVersion", "%u", TSV_VERSION); + 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"); From aa78020410c68cceb1a49843930f492f4c0ca61c Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 20 Feb 2018 00:25:49 +1300 Subject: [PATCH 31/52] Emit changed emergency/priority status immediately --- net_io.c | 4 +++- track.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index bb3ae1a..51a7a61 100644 --- a/net_io.c +++ b/net_io.c @@ -2089,7 +2089,8 @@ static void writeFATSV() (callsignValid && strcmp(a->callsign, a->fatsv_emitted_callsign) != 0) || (airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) || (airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE) || - (squawkValid && a->squawk != a->fatsv_emitted_squawk); + (squawkValid && a->squawk != a->fatsv_emitted_squawk) || + (trackDataValid(&a->emergency_valid) && a->emergency != a->fatsv_emitted_emergency); uint64_t minAge; if (immediate) { @@ -2227,6 +2228,7 @@ static void writeFATSV() 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/track.h b/track.h index 721f7cc..05f264f 100644 --- a/track.h +++ b/track.h @@ -236,6 +236,7 @@ struct aircraft { unsigned fatsv_emitted_sil; // -"- SIL sil_type_t fatsv_emitted_sil_type; // -"- SIL supplement unsigned fatsv_emitted_nic_baro; // -"- NICbaro + emergency_t fatsv_emitted_emergency; // -"- emergency/priority status uint64_t fatsv_last_emitted; // time (millis) aircraft was last FA emitted uint64_t fatsv_last_force_emit; // time (millis) we last emitted only-on-change data From 7059c0f64a1bf4fb538419770dd26ec4db21a1be Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 20 Feb 2018 00:26:16 +1300 Subject: [PATCH 32/52] adsbVer -> adsb_version --- net_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index 51a7a61..ccb5640 100644 --- a/net_io.c +++ b/net_io.c @@ -2132,7 +2132,7 @@ static void writeFATSV() 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, "adsbVer", "%d", a->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); From a256833e17fa9bd02ee808e7b896e12e87153cbe Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 22 Feb 2018 07:42:04 +0000 Subject: [PATCH 33/52] Increase the max tsv output size --- net_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index ccb5640..d86a91e 100644 --- a/net_io.c +++ b/net_io.c @@ -1785,7 +1785,7 @@ __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *en return p; } -#define TSV_MAX_PACKET_SIZE 400 +#define TSV_MAX_PACKET_SIZE 600 #define TSV_VERSION 2 void writeFATSVHeader() From f1036702ce7fa40ea58a8949cca7bd6b2a63f412 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 22 Feb 2018 07:42:22 +0000 Subject: [PATCH 34/52] Use the time we last saw a message for data validity checks / data age info, rather than the time we eventually decide to emit it. --- net_io.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net_io.c b/net_io.c index d86a91e..84a006d 100644 --- a/net_io.c +++ b/net_io.c @@ -2038,8 +2038,6 @@ static void writeFATSV() // scan once a second at most next_update = now + 1000; - // Pretend we are "processing a message" so the validity checks work as expected - _messageNow = now; for (a = Modes.aircrafts; a; a = a->next) { if (a->messages < 2) // basic filter for bad decodes continue; @@ -2049,6 +2047,9 @@ static void writeFATSV() 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 From 5cdf9157db99ce7cf76e0248177f0b442252fa6a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 22 Feb 2018 07:45:00 +0000 Subject: [PATCH 35/52] Add gva / sda to tracking state --- track.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/track.h b/track.h index 05f264f..08ac084 100644 --- a/track.h +++ b/track.h @@ -200,6 +200,8 @@ struct aircraft { unsigned nac_v : 3; // NACv from airborne velocity or opstatus unsigned sil : 2; // SIL from TSS or opstatus sil_type_t sil_type; // SIL supplement from TSS or opstatus + unsigned gva : 2; // GVA from opstatus + unsigned sda : 2; // SDA from opstatus int modeA_hit; // did our squawk match a possible mode A reply in the last check period? int modeC_hit; // did our altitude match a possible mode C reply in the last check period? From abfb88b12451587805f4a12dcf3893ca660e2796 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 22 Feb 2018 07:45:32 +0000 Subject: [PATCH 36/52] Separate sil / sil_type to make compression a bit simpler --- net_io.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net_io.c b/net_io.c index 84a006d..32a7500 100644 --- a/net_io.c +++ b/net_io.c @@ -2144,8 +2144,11 @@ static void writeFATSV() 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 || a->sil_type != a->fatsv_emitted_sil_type)) { - p = appendFATSVMeta(p, end, "sil", a, &a->sil_valid, "{%u %s}", a->sil, sil_type_enum_string(a->sil_type)); + 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); From f323745ead5c9cb18f552c6df62296bb9bc19be3 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 23 Feb 2018 18:10:26 +0000 Subject: [PATCH 37/52] Don't track icao 000000 as an aircraft (but do still decode messages for it) --- track.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/track.c b/track.c index 9fa626a..b550e97 100644 --- a/track.c +++ b/track.c @@ -814,6 +814,11 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) return NULL; } + if (mm->addr == 0) { + // junk address, don't track it + return NULL; + } + _messageNow = mm->sysTimestampMsg; // Lookup our aircraft or create a new one From 04b940d45bd7d6dcfaf79221606471fcb28bcac4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 23 Feb 2018 18:10:49 +0000 Subject: [PATCH 38/52] Switch RC_UNKNOWN to 0 to make life a little easier for faup1090/piaware --- track.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/track.h b/track.h index 08ac084..daac95a 100644 --- a/track.h +++ b/track.h @@ -64,8 +64,8 @@ */ #define TRACK_MODEAC_MIN_MESSAGES 4 -/* Special value for Rc unknown (100NM) */ -#define RC_UNKNOWN 185200 +/* Special value for Rc unknown */ +#define RC_UNKNOWN 0 // data moves through three states: // fresh: data is valid. Updates from a less reliable source are not accepted. From 94824fbb98599360fcca511fc5b55517d0a579fb Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 23 Feb 2018 18:21:01 +0000 Subject: [PATCH 39/52] Avoid coredump on "untracked" aircraft (icao 000000) Clean up the first-message-squelch logic a little. --- mode_s.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mode_s.c b/mode_s.c index 9f943b4..cd6afc4 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2106,14 +2106,13 @@ void useModesMessage(struct modesMessage *mm) { // forward messages when we have seen two of them. if (Modes.net) { - if (Modes.net_verbatim || mm->msgtype == 32) { + if (Modes.net_verbatim || mm->msgtype == 32 || !a) { // Unconditionally send modesQueueOutput(mm, a); } else if (a->messages > 1) { - // If this is the second message, and we - // squelched the first message, then re-emit the - // first message now. - if (!Modes.net_verbatim && a && a->messages == 2) { + // Suppress the first message. When we receive a second message, + // emit the first two messages. + if (a->messages == 2) { modesQueueOutput(&a->first_message, a); } modesQueueOutput(mm, a); From 5a9350a6a1ebe5ad01851afad245485a942dc799 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 23 Feb 2018 19:02:02 +0000 Subject: [PATCH 40/52] Derive SPI / Alert from the airborne position message surveillance status field --- mode_s.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mode_s.c b/mode_s.c index cd6afc4..4093cc3 100644 --- a/mode_s.c +++ b/mode_s.c @@ -903,6 +903,26 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) unsigned char *me = mm->ME; // 6-7: surveillance status + switch (getbits(me, 6, 7)) { + case 0: + // no status + mm->alert_valid = mm->spi_valid = 1; + mm->alert = mm->spi = 0; + break; + case 1: // permanent alert + case 2: // temporary alert + mm->alert_valid = 1; + mm->alert = 1; + // states 1/2 override state 3, so we don't know SPI status here. + break; + case 3: // SPI + // we know there's no alert in this case + mm->alert_valid = mm->spi_valid = 1; + mm->alert = 0; + mm->spi = 1; + break; + } + // 8: IMF or NIC supplement-B if (check_imf) { From a8fe04f856fb7c75cf546121aeed790f484ec9ac Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 23 Feb 2018 19:02:33 +0000 Subject: [PATCH 41/52] Derive emergency status from squawk if not otherwise provided. --- track.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/track.c b/track.c index b550e97..caabcb1 100644 --- a/track.c +++ b/track.c @@ -879,6 +879,29 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) a->modeA_hit = 0; } a->squawk = mm->squawk; + + // Handle 7x00 without a corresponding emergency status + if (!mm->emergency_valid) { + emergency_t squawk_emergency; + switch (mm->squawk) { + case 0x7500: + squawk_emergency = EMERGENCY_UNLAWFUL; + break; + case 0x7600: + squawk_emergency = EMERGENCY_NORDO; + break; + case 0x7700: + squawk_emergency = EMERGENCY_GENERAL; + break; + default: + squawk_emergency = EMERGENCY_NONE; + break; + } + + if (squawk_emergency != EMERGENCY_NONE && accept_data(&a->emergency_valid, mm->source)) { + a->emergency = squawk_emergency; + } + } } if (mm->emergency_valid && accept_data(&a->emergency_valid, mm->source)) { From 0608e319656422dde791000f73d98c18c6117a12 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Mar 2018 20:09:23 +0000 Subject: [PATCH 42/52] Move some static functions earlier --- net_io.c | 80 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/net_io.c b/net_io.c index 32a7500..a0260fa 100644 --- a/net_io.c +++ b/net_io.c @@ -1072,6 +1072,22 @@ static int decodeHexMessage(struct client *c, char *hex) { useModesMessage(&mm); return (0); } + +__attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap) +{ + p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); + return p; +} + + __attribute__ ((format (printf,3,4))) static char *safe_snprintf(char *p, char *end, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); + va_end(ap); + return p; +} + // //========================================================================= // @@ -1197,6 +1213,30 @@ static const char *addrtype_enum_string(addrtype_t type) { } } +static const char *emergency_enum_string(emergency_t emergency) +{ + switch (emergency) { + case EMERGENCY_NONE: return "none"; + case EMERGENCY_GENERAL: return "general"; + case EMERGENCY_LIFEGUARD: return "lifeguard"; + case EMERGENCY_MINFUEL: return "minfuel"; + case EMERGENCY_NORDO: return "nordo"; + case EMERGENCY_UNLAWFUL: return "unlawful"; + case EMERGENCY_DOWNED: return "downed"; + default: return "reserved"; + } +} + +static const char *sil_type_enum_string(sil_type_t type) +{ + switch (type) { + case SIL_UNKNOWN: return "unknown"; + case SIL_PER_HOUR: return "perhour"; + case SIL_PER_SAMPLE: return "persample"; + default: return "invalid"; + } +} + char *generateAircraftJson(const char *url_path, int *len) { uint64_t now = mstime(); struct aircraft *a; @@ -1756,22 +1796,6 @@ static void modesReadFromClient(struct client *c) { } } -__attribute__ ((format (printf,3,0))) static char *safe_vsnprintf(char *p, char *end, const char *format, va_list ap) -{ - p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); - return p; -} - - __attribute__ ((format (printf,3,4))) static char *safe_snprintf(char *p, char *end, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - p += vsnprintf(p < end ? p : NULL, p < end ? (size_t)(end - p) : 0, format, ap); - va_end(ap); - return p; -} - - __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *end, const char *field, const char *format, ...) { va_list ap; @@ -1997,30 +2021,6 @@ static const char *airground_enum_string(airground_t ag) } } -static const char *emergency_enum_string(emergency_t emergency) -{ - switch (emergency) { - case EMERGENCY_NONE: return "none"; - case EMERGENCY_GENERAL: return "general"; - case EMERGENCY_LIFEGUARD: return "lifeguard"; - case EMERGENCY_MINFUEL: return "minfuel"; - case EMERGENCY_NORDO: return "nordo"; - case EMERGENCY_UNLAWFUL: return "unlawful"; - case EMERGENCY_DOWNED: return "downed"; - default: return "reserved"; - } -} - -static const char *sil_type_enum_string(sil_type_t type) -{ - switch (type) { - case SIL_UNKNOWN: return "unknown"; - case SIL_PER_HOUR: return "perhour"; - case SIL_PER_SAMPLE: return "persample"; - default: return "invalid"; - } -} - static void writeFATSV() { struct aircraft *a; From d8f568e0ce76c85d1eab6b15935319752fd03b4e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Mar 2018 20:10:50 +0000 Subject: [PATCH 43/52] Switch everything over to safe_snprintf Rework the aircraft json overflow/resize case Add more fields to the json, reorder to match header ordering --- net_io.c | 377 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 215 insertions(+), 162 deletions(-) diff --git a/net_io.c b/net_io.c index a0260fa..f4a68fd 100644 --- a/net_io.c +++ b/net_io.c @@ -1106,7 +1106,7 @@ static const char *jsonEscapeString(const char *str) { *out++ = '\\'; *out++ = ch; } else if (ch < 32 || ch > 127) { - out += snprintf(out, end - out, "\\u%04x", ch); + out = safe_snprintf(out, end, "\\u%04x", ch); } else { *out++ = ch; } @@ -1118,36 +1118,66 @@ static const char *jsonEscapeString(const char *str) { static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t source) { - p += snprintf(p, end-p, "["); - if (a->squawk_valid.source == source) - p += snprintf(p, end-p, "\"squawk\","); + p = safe_snprintf(p, end, "["); + + char *start = p; if (a->callsign_valid.source == source) - p += snprintf(p, end-p, "\"callsign\","); - if (a->position_valid.source == source) - p += snprintf(p, end-p, "\"lat\",\"lon\","); + p = safe_snprintf(p, end, "\"callsign\","); if (a->altitude_baro_valid.source == source) - p += snprintf(p, end-p, "\"altitude\","); + p = safe_snprintf(p, end, "\"altitude\","); if (a->altitude_geom_valid.source == source) - p += snprintf(p, end-p, "\"alt_geom\","); - if (a->track_valid.source == source) - p += snprintf(p, end-p, "\"track\","); - if (a->mag_heading_valid.source == source) - p += snprintf(p, end-p, "\"mag_heading\","); - if (a->true_heading_valid.source == source) - p += snprintf(p, end-p, "\"true_heading\","); + p = safe_snprintf(p, end, "\"alt_geom\","); if (a->gs_valid.source == source) - p += snprintf(p, end-p, "\"gs\","); + p = safe_snprintf(p, end, "\"gs\","); if (a->ias_valid.source == source) - p += snprintf(p, end-p, "\"ias\","); + p = safe_snprintf(p, end, "\"ias\","); if (a->tas_valid.source == source) - p += snprintf(p, end-p, "\"tas\","); + p = safe_snprintf(p, end, "\"tas\","); + if (a->mach_valid.source == source) + p = safe_snprintf(p, end, "\"mach\","); + if (a->track_valid.source == source) + p = safe_snprintf(p, end, "\"track\","); + if (a->track_rate_valid.source == source) + p = safe_snprintf(p, end, "\"track_rate\","); + if (a->roll_valid.source == source) + p = safe_snprintf(p, end, "\"roll\","); + if (a->mag_heading_valid.source == source) + p = safe_snprintf(p, end, "\"mag_heading\","); + if (a->true_heading_valid.source == source) + p = safe_snprintf(p, end, "\"true_heading\","); if (a->baro_rate_valid.source == source) - p += snprintf(p, end-p, "\"baro_rate\","); + p = safe_snprintf(p, end, "\"baro_rate\","); if (a->geom_rate_valid.source == source) - p += snprintf(p, end-p, "\"geom_rate\","); - if (p[-1] != '[') + p = safe_snprintf(p, end, "\"geom_rate\","); + if (a->squawk_valid.source == source) + p = safe_snprintf(p, end, "\"squawk\","); + if (a->emergency_valid.source == source) + 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_heading_valid.source == source) + p = safe_snprintf(p, end, "\"nav_heading\","); + if (a->nav_modes_valid.source == source) + p = safe_snprintf(p, end, "\"nav_modes\","); + if (a->position_valid.source == source) + p = safe_snprintf(p, end, "\"lat\",\"lon\",\"nic\",\"rc\","); + if (a->nic_baro_valid.source == source) + p = safe_snprintf(p, end, "\"nic_baro\","); + if (a->nac_p_valid.source == source) + p = safe_snprintf(p, end, "\"nac_p\","); + if (a->nac_v_valid.source == source) + p = safe_snprintf(p, end, "\"nac_v\","); + if (a->sil_valid.source == source) + p = safe_snprintf(p, end, "\"sil\",\"sil_type\","); + if (a->gva_valid.source == source) + p = safe_snprintf(p, end, "\"gva\","); + if (a->sda_valid.source == source) + p = safe_snprintf(p, end, "\"sda\","); + if (start > p) --p; - p += snprintf(p, end-p, "]"); + p = safe_snprintf(p, end, "]"); return p; } @@ -1173,11 +1203,11 @@ static char *append_nav_modes(char *p, char *end, nav_modes_t flags, const char } if (!first) { - p += snprintf(p, end-p, "%s", sep); + p = safe_snprintf(p, end, "%s", sep); } first = 0; - p += snprintf(p, end-p, "%s%s%s", quote, nav_modes_names[i].name, quote); + p = safe_snprintf(p, end, "%s%s%s", quote, nav_modes_names[i].name, quote); } return p; @@ -1240,18 +1270,21 @@ static const char *sil_type_enum_string(sil_type_t type) char *generateAircraftJson(const char *url_path, int *len) { uint64_t now = mstime(); struct aircraft *a; - int buflen = 1024; // The initial buffer is incremented as needed + int buflen = 32768; // The initial buffer is resized as needed char *buf = (char *) malloc(buflen), *p = buf, *end = buf+buflen; + char *line_start; int first = 1; MODES_NOTUSED(url_path); - p += snprintf(p, end-p, - "{ \"now\" : %.1f,\n" - " \"messages\" : %u,\n" - " \"aircraft\" : [", - now / 1000.0, - Modes.stats_current.messages_total + Modes.stats_alltime.messages_total); + _messageNow = now; + + p = safe_snprintf(p, end, + "{ \"now\" : %.1f,\n" + " \"messages\" : %u,\n" + " \"aircraft\" : [", + 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 @@ -1263,82 +1296,102 @@ char *generateAircraftJson(const char *url_path, int *len) { else *p++ = ','; - p += snprintf(p, end-p, "\n {\"hex\":\"%s%06x\"", (a->addr & MODES_NON_ICAO_ADDRESS) ? "~" : "", a->addr & 0xFFFFFF); + 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 += snprintf(p, end-p, ",\"type\":\"%s\"", addrtype_enum_string(a->addrtype)); - if (a->adsb_version >= 0) - p += snprintf(p, end-p, ",\"version\":%d", a->adsb_version); - if (trackDataValid(&a->squawk_valid)) - p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->squawk); + p = safe_snprintf(p, end, ",\"type\":\"%s\"", addrtype_enum_string(a->addrtype)); if (trackDataValid(&a->callsign_valid)) - p += snprintf(p, end-p, ",\"flight\":\"%s\"", jsonEscapeString(a->callsign)); - if (trackDataValid(&a->position_valid)) - p += snprintf(p, end-p, ",\"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); + 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 += snprintf(p, end-p, ",\"altitude\":\"ground\""); + p = safe_snprintf(p, end, ",\"altitude\":\"ground\""); else { if (trackDataValid(&a->altitude_baro_valid)) - p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude_baro); + p = safe_snprintf(p, end, ",\"altitude\":%d", a->altitude_baro); if (trackDataValid(&a->altitude_geom_valid)) - p += snprintf(p, end-p, ",\"alt_geom\":%d", a->altitude_geom); + p = safe_snprintf(p, end, ",\"alt_geom\":%d", a->altitude_geom); } - if (trackDataValid(&a->baro_rate_valid)) - p += snprintf(p, end-p, ",\"baro_rate\":%d", a->baro_rate); - if (trackDataValid(&a->geom_rate_valid)) - p += snprintf(p, end-p, ",\"geom_rate\":%d", a->geom_rate); - if (trackDataValid(&a->track_valid)) - p += snprintf(p, end-p, ",\"track\":%.1f", a->track); - if (trackDataValid(&a->track_rate_valid)) - p += snprintf(p, end-p, ",\"track_rate\":%.2f", a->track_rate); - if (trackDataValid(&a->mag_heading_valid)) - p += snprintf(p, end-p, ",\"mag_heading\":%.1f", a->mag_heading); - if (trackDataValid(&a->true_heading_valid)) - p += snprintf(p, end-p, ",\"true_heading\":%.1f", a->true_heading); if (trackDataValid(&a->gs_valid)) - p += snprintf(p, end-p, ",\"gs\":%.1f", a->gs); + p = safe_snprintf(p, end, ",\"gs\":%.1f", a->gs); if (trackDataValid(&a->ias_valid)) - p += snprintf(p, end-p, ",\"ias\":%u", a->ias); + p = safe_snprintf(p, end, ",\"ias\":%u", a->ias); if (trackDataValid(&a->tas_valid)) - p += snprintf(p, end-p, ",\"tas\":%u", a->tas); + p = safe_snprintf(p, end, ",\"tas\":%u", a->tas); if (trackDataValid(&a->mach_valid)) - p += snprintf(p, end-p, ",\"mach\":%.3f", a->mach); + 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 += snprintf(p, end-p, ",\"roll\":%.1f", a->roll); + 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 += snprintf(p, end-p, ",\"category\":\"%02X\"", a->category); - if (trackDataValid(&a->nav_altitude_valid)) - p += snprintf(p, end-p, ",\"nav_alt\":%d", a->nav_altitude); - if (trackDataValid(&a->nav_heading_valid)) - p += snprintf(p, end-p, ",\"nav_heading\":%.1f", a->nav_heading); - if (trackDataValid(&a->nav_modes_valid)) { - p += snprintf(p, end-p, ",\"nav_modes\":["); - p = append_nav_modes(p, end, a->nav_modes, "\"", ","); - p += snprintf(p, end-p, "]"); - } + p = safe_snprintf(p, end, ",\"category\":\"%02X\"", a->category); if (trackDataValid(&a->nav_qnh_valid)) - p += snprintf(p, end-p, ",\"nav_qnh\":%.1f", a->nav_qnh); + 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_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); - p += snprintf(p, end-p, ",\"mlat\":"); + + p = safe_snprintf(p, end, ",\"mlat\":"); p = append_flags(p, end, a, SOURCE_MLAT); - p += snprintf(p, end-p, ",\"tisb\":"); + p = safe_snprintf(p, end, ",\"tisb\":"); p = append_flags(p, end, a, SOURCE_TISB); - p += snprintf(p, end-p, ",\"messages\":%ld,\"seen\":%.1f,\"rssi\":%.1f}", + 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 we're getting near the end of the buffer, expand it. - if ((end - p) < 512) { - int used = p - buf; + if (p >= end) { + // overran the buffer + int used = line_start - buf; buflen *= 2; buf = (char *) realloc(buf, buflen); p = buf+used; end = buf + buflen; + goto retry; } } - p += snprintf(p, end-p, "\n ]\n}\n"); + p = safe_snprintf(p, end, "\n ]\n}\n"); *len = p-buf; return buf; } @@ -1350,61 +1403,61 @@ static char * appendStatsJson(char *p, { int i; - p += snprintf(p, end-p, - "\"%s\":{\"start\":%.1f,\"end\":%.1f", - key, - st->start / 1000.0, - st->end / 1000.0); + p = safe_snprintf(p, end, + "\"%s\":{\"start\":%.1f,\"end\":%.1f", + key, + st->start / 1000.0, + st->end / 1000.0); if (!Modes.net_only) { - p += snprintf(p, end-p, - ",\"local\":{\"samples_processed\":%llu" - ",\"samples_dropped\":%llu" - ",\"modeac\":%u" - ",\"modes\":%u" - ",\"bad\":%u" - ",\"unknown_icao\":%u", - (unsigned long long)st->samples_processed, - (unsigned long long)st->samples_dropped, - st->demod_modeac, - st->demod_preambles, - st->demod_rejected_bad, - st->demod_rejected_unknown_icao); + p = safe_snprintf(p, end, + ",\"local\":{\"samples_processed\":%llu" + ",\"samples_dropped\":%llu" + ",\"modeac\":%u" + ",\"modes\":%u" + ",\"bad\":%u" + ",\"unknown_icao\":%u", + (unsigned long long)st->samples_processed, + (unsigned long long)st->samples_dropped, + st->demod_modeac, + st->demod_preambles, + st->demod_rejected_bad, + st->demod_rejected_unknown_icao); for (i=0; i <= Modes.nfix_crc; ++i) { - if (i == 0) p += snprintf(p, end-p, ",\"accepted\":[%u", st->demod_accepted[i]); - else p += snprintf(p, end-p, ",%u", st->demod_accepted[i]); + if (i == 0) p = safe_snprintf(p, end, ",\"accepted\":[%u", st->demod_accepted[i]); + else p = safe_snprintf(p, end, ",%u", st->demod_accepted[i]); } - p += snprintf(p, end-p, "]"); + p = safe_snprintf(p, end, "]"); if (st->signal_power_sum > 0 && st->signal_power_count > 0) - p += snprintf(p, end-p,",\"signal\":%.1f", 10 * log10(st->signal_power_sum / st->signal_power_count)); + p = safe_snprintf(p, end, ",\"signal\":%.1f", 10 * log10(st->signal_power_sum / st->signal_power_count)); if (st->noise_power_sum > 0 && st->noise_power_count > 0) - p += snprintf(p, end-p,",\"noise\":%.1f", 10 * log10(st->noise_power_sum / st->noise_power_count)); + p = safe_snprintf(p, end, ",\"noise\":%.1f", 10 * log10(st->noise_power_sum / st->noise_power_count)); if (st->peak_signal_power > 0) - p += snprintf(p, end-p,",\"peak_signal\":%.1f", 10 * log10(st->peak_signal_power)); + p = safe_snprintf(p, end, ",\"peak_signal\":%.1f", 10 * log10(st->peak_signal_power)); - p += snprintf(p, end-p,",\"strong_signals\":%d}", st->strong_signal_count); + p = safe_snprintf(p, end, ",\"strong_signals\":%d}", st->strong_signal_count); } if (Modes.net) { - p += snprintf(p, end-p, - ",\"remote\":{\"modeac\":%u" - ",\"modes\":%u" - ",\"bad\":%u" - ",\"unknown_icao\":%u", - st->remote_received_modeac, - st->remote_received_modes, - st->remote_rejected_bad, - st->remote_rejected_unknown_icao); + p = safe_snprintf(p, end, + ",\"remote\":{\"modeac\":%u" + ",\"modes\":%u" + ",\"bad\":%u" + ",\"unknown_icao\":%u", + st->remote_received_modeac, + st->remote_received_modes, + st->remote_rejected_bad, + st->remote_rejected_unknown_icao); for (i=0; i <= Modes.nfix_crc; ++i) { - if (i == 0) p += snprintf(p, end-p, ",\"accepted\":[%u", st->remote_accepted[i]); - else p += snprintf(p, end-p, ",%u", st->remote_accepted[i]); + if (i == 0) p = safe_snprintf(p, end, ",\"accepted\":[%u", st->remote_accepted[i]); + else p = safe_snprintf(p, end, ",%u", st->remote_accepted[i]); } - p += snprintf(p, end-p, "]}"); + p = safe_snprintf(p, end, "]}"); } { @@ -1412,47 +1465,47 @@ static char * appendStatsJson(char *p, uint64_t reader_cpu_millis = (uint64_t)st->reader_cpu.tv_sec*1000UL + st->reader_cpu.tv_nsec/1000000UL; uint64_t background_cpu_millis = (uint64_t)st->background_cpu.tv_sec*1000UL + st->background_cpu.tv_nsec/1000000UL; - p += snprintf(p, end-p, - ",\"cpr\":{\"surface\":%u" - ",\"airborne\":%u" - ",\"global_ok\":%u" - ",\"global_bad\":%u" - ",\"global_range\":%u" - ",\"global_speed\":%u" - ",\"global_skipped\":%u" - ",\"local_ok\":%u" - ",\"local_aircraft_relative\":%u" - ",\"local_receiver_relative\":%u" - ",\"local_skipped\":%u" - ",\"local_range\":%u" - ",\"local_speed\":%u" - ",\"filtered\":%u}" - ",\"altitude_suppressed\":%u" - ",\"cpu\":{\"demod\":%llu,\"reader\":%llu,\"background\":%llu}" - ",\"tracks\":{\"all\":%u" - ",\"single_message\":%u}" - ",\"messages\":%u}", - st->cpr_surface, - st->cpr_airborne, - st->cpr_global_ok, - st->cpr_global_bad, - st->cpr_global_range_checks, - st->cpr_global_speed_checks, - st->cpr_global_skipped, - st->cpr_local_ok, - st->cpr_local_aircraft_relative, - st->cpr_local_receiver_relative, - st->cpr_local_skipped, - st->cpr_local_range_checks, - st->cpr_local_speed_checks, - st->cpr_filtered, - st->suppressed_altitude_messages, - (unsigned long long)demod_cpu_millis, - (unsigned long long)reader_cpu_millis, - (unsigned long long)background_cpu_millis, - st->unique_aircraft, - st->single_message_aircraft, - st->messages_total); + p = safe_snprintf(p, end, + ",\"cpr\":{\"surface\":%u" + ",\"airborne\":%u" + ",\"global_ok\":%u" + ",\"global_bad\":%u" + ",\"global_range\":%u" + ",\"global_speed\":%u" + ",\"global_skipped\":%u" + ",\"local_ok\":%u" + ",\"local_aircraft_relative\":%u" + ",\"local_receiver_relative\":%u" + ",\"local_skipped\":%u" + ",\"local_range\":%u" + ",\"local_speed\":%u" + ",\"filtered\":%u}" + ",\"altitude_suppressed\":%u" + ",\"cpu\":{\"demod\":%llu,\"reader\":%llu,\"background\":%llu}" + ",\"tracks\":{\"all\":%u" + ",\"single_message\":%u}" + ",\"messages\":%u}", + st->cpr_surface, + st->cpr_airborne, + st->cpr_global_ok, + st->cpr_global_bad, + st->cpr_global_range_checks, + st->cpr_global_speed_checks, + st->cpr_global_skipped, + st->cpr_local_ok, + st->cpr_local_aircraft_relative, + st->cpr_local_receiver_relative, + st->cpr_local_skipped, + st->cpr_local_range_checks, + st->cpr_local_speed_checks, + st->cpr_filtered, + st->suppressed_altitude_messages, + (unsigned long long)demod_cpu_millis, + (unsigned long long)reader_cpu_millis, + (unsigned long long)background_cpu_millis, + st->unique_aircraft, + st->single_message_aircraft, + st->messages_total); } return p; @@ -1464,22 +1517,22 @@ char *generateStatsJson(const char *url_path, int *len) { MODES_NOTUSED(url_path); - p += snprintf(p, end-p, "{\n"); + p = safe_snprintf(p, end, "{\n"); p = appendStatsJson(p, end, &Modes.stats_current, "latest"); - p += snprintf(p, end-p, ",\n"); + p = safe_snprintf(p, end, ",\n"); p = appendStatsJson(p, end, &Modes.stats_1min[Modes.stats_latest_1min], "last1min"); - p += snprintf(p, end-p, ",\n"); + p = safe_snprintf(p, end, ",\n"); p = appendStatsJson(p, end, &Modes.stats_5min, "last5min"); - p += snprintf(p, end-p, ",\n"); + p = safe_snprintf(p, end, ",\n"); p = appendStatsJson(p, end, &Modes.stats_15min, "last15min"); - p += snprintf(p, end-p, ",\n"); + p = safe_snprintf(p, end, ",\n"); add_stats(&Modes.stats_alltime, &Modes.stats_current, &add); p = appendStatsJson(p, end, &add, "total"); - p += snprintf(p, end-p, "\n}\n"); + p = safe_snprintf(p, end, "\n}\n"); assert(p <= end); From 213d769bf92041149c1539f25d9063a066f7bf6e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 8 Mar 2018 17:34:34 +0000 Subject: [PATCH 44/52] Tweaks to json output / webmap handling of new fields (untested) --- net_io.c | 4 +-- public_html/index.html | 16 ++++++------ public_html/planeObject.js | 51 ++++++++++++++++++++++++++++++++------ public_html/script.js | 16 ++++++------ 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/net_io.c b/net_io.c index f4a68fd..75e5db7 100644 --- a/net_io.c +++ b/net_io.c @@ -1304,10 +1304,10 @@ char *generateAircraftJson(const char *url_path, int *len) { 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, ",\"altitude\":\"ground\""); + p = safe_snprintf(p, end, ",\"alt_baro\":\"ground\""); else { if (trackDataValid(&a->altitude_baro_valid)) - p = safe_snprintf(p, end, ",\"altitude\":%d", a->altitude_baro); + 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); } diff --git a/public_html/index.html b/public_html/index.html index 2c60ce1..b8f656d 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -106,7 +106,7 @@ Groundspeed:
- n/a + n/a
@@ -211,15 +211,15 @@
-
AP alt:
-
-
AP heading:
-
+
Nav alt:
+
+
Nav heading:
+
-
AP modes:
-
-
Alt setting:
+
Nav modes:
+
+
Nav QNH:
diff --git a/public_html/planeObject.js b/public_html/planeObject.js index b24e9b3..c211f89 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -11,22 +11,29 @@ function PlaneObject(icao) { // Basic location information this.altitude = null; + this.alt_baro = null; this.alt_geom = null; + + this.speed = null; this.gs = null; this.ias = null; this.tas = null; + this.track = null; this.track_rate = null; this.mag_heading = null; this.true_heading = null; this.mach = null; this.roll = null; - this.intent_alt = null; - this.intent_heading = null; - this.intent_modes = null; - this.alt_setting = null; + this.nav_alt = null; + this.nav_heading = null; + this.nav_modes = null; + this.nav_qnh = null; + this.baro_rate = null; this.geom_rate = null; + this.vert_rate = null; + this.version = null; this.prev_position = null; @@ -429,10 +436,10 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { // simple fields - var fields = ["altitude", "alt_geom", "gs", "ias", "tas", "track", + var fields = ["alt_baro", "alt_geom", "gs", "ias", "tas", "track", "track_rate", "mag_heading", "true_heading", "mach", - "roll", "intent_alt", "intent_heading", "intent_modes", - "alt_setting", "baro_rate", "geom_rate", + "roll", "nav_altitude", "nav_heading", "nav_modes", + "nav_qnh", "baro_rate", "geom_rate", "squawk", "category", "version"]; for (var i = 0; i < fields.length; ++i) { @@ -473,6 +480,36 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { } } } + + // Pick an altitude + if ('alt_baro' in data) { + this.altitude = data.alt_baro; + } else if ('alt_geom' in data) { + this.altitude = data.alt_geom; + } else { + this.altitude = null; + } + + // Pick vertical rate from either baro or geom rate + // geometric rate is generally more reliable (smoothed etc) + if ('geom_rate' in data) { + this.vert_rate = data.geom_rate; + } else if ('baro_rate' in data) { + this.vert_rate = data.baro_rate; + } else { + this.vert_rate = null; + } + + // Pick a speed + if ('gs' in data) { + this.speed = data.gs; + } else if ('tas' in data) { + this.speed = data.tas; + } else if ('ias' in data) { + this.speed = data.ias; + } else { + this.speed = null; + } }; PlaneObject.prototype.updateTick = function(receiver_timestamp, last_timestamp) { diff --git a/public_html/script.js b/public_html/script.js index 2dea753..c7fb70e 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -872,7 +872,7 @@ function refreshSelected() { // emerg.className = 'hidden'; // } - $("#selected_altitude").text(format_altitude_long(selected.altitude, selected.baro_rate, DisplayUnits)); + $("#selected_altitude").text(format_altitude_long(selected.altitude, selected.vert_rate, DisplayUnits)); if (selected.squawk === null || selected.squawk === '0000') { $('#selected_squawk').text('n/a'); @@ -880,8 +880,8 @@ function refreshSelected() { $('#selected_squawk').text(selected.squawk); } - $('#selected_speed').text(format_speed_long(selected.gs, DisplayUnits)); - $('#selected_vertical_rate').text(format_vert_rate_long(selected.baro_rate, DisplayUnits)); + $('#selected_gs').text(format_speed_long(selected.gs, DisplayUnits)); + $('#selected_vertical_rate').text(format_vert_rate_long(selected.vert_rate, DisplayUnits)); $('#selected_icao').text(selected.icao.toUpperCase()); $('#airframes_post_icao').attr('value',selected.icao); $('#selected_track').text(format_track_long(selected.track)); @@ -1010,9 +1010,9 @@ function refreshHighlighted() { } - $('#highlighted_speed').text(format_speed_long(highlighted.gs, DisplayUnits)); + $('#highlighted_speed').text(format_speed_long(highlighted.speed, DisplayUnits)); - $("#highlighted_altitude").text(format_altitude_long(highlighted.altitude, highlighted.baro_rate, DisplayUnits)); + $("#highlighted_altitude").text(format_altitude_long(highlighted.altitude, highlighted.vert_rate, DisplayUnits)); $('#highlighted_icao').text(highlighted.icao.toUpperCase()); @@ -1081,9 +1081,9 @@ function refreshTableInfo() { tableplane.tr.cells[3].textContent = (tableplane.registration !== null ? tableplane.registration : ""); tableplane.tr.cells[4].textContent = (tableplane.icaotype !== null ? tableplane.icaotype : ""); tableplane.tr.cells[5].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); - tableplane.tr.cells[6].innerHTML = format_altitude_brief(tableplane.altitude, tableplane.baro_rate, DisplayUnits); + tableplane.tr.cells[6].innerHTML = format_altitude_brief(tableplane.altitude, tableplane.vert_rate, DisplayUnits); tableplane.tr.cells[7].textContent = format_speed_brief(tableplane.gs, DisplayUnits); - tableplane.tr.cells[8].textContent = format_vert_rate_brief(tableplane.baro_rate, DisplayUnits); + tableplane.tr.cells[8].textContent = format_vert_rate_brief(tableplane.vert_rate, DisplayUnits); tableplane.tr.cells[9].textContent = format_distance_brief(tableplane.sitedist, DisplayUnits); tableplane.tr.cells[10].textContent = format_track_brief(tableplane.track); tableplane.tr.cells[11].textContent = tableplane.messages; @@ -1134,7 +1134,7 @@ function sortByAircraftType() { sortBy('icaotype', compareAlpha, func function sortBySquawk() { sortBy('squawk', compareAlpha, function(x) { return x.squawk; }); } function sortByAltitude() { sortBy('altitude',compareNumeric, function(x) { return (x.altitude == "ground" ? -1e9 : x.altitude); }); } function sortBySpeed() { sortBy('speed', compareNumeric, function(x) { return x.gs; }); } -function sortByVerticalRate() { sortBy('vert_rate', compareNumeric, function(x) { return x.baro_rate; }); } +function sortByVerticalRate() { sortBy('vert_rate', compareNumeric, function(x) { return x.vert_rate; }); } function sortByDistance() { sortBy('sitedist',compareNumeric, function(x) { return x.sitedist; }); } function sortByTrack() { sortBy('track', compareNumeric, function(x) { return x.track; }); } function sortByMsgs() { sortBy('msgs', compareNumeric, function(x) { return x.messages; }); } From d267d2257a0d30affaeabf3456dfc94159a1612e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 8 Mar 2018 21:15:41 +0000 Subject: [PATCH 45/52] Fix tisb/mlat json flag formatting --- net_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index 75e5db7..689e328 100644 --- a/net_io.c +++ b/net_io.c @@ -1175,7 +1175,7 @@ static char *append_flags(char *p, char *end, struct aircraft *a, datasource_t s p = safe_snprintf(p, end, "\"gva\","); if (a->sda_valid.source == source) p = safe_snprintf(p, end, "\"sda\","); - if (start > p) + if (p != start) --p; p = safe_snprintf(p, end, "]"); return p; From 0b9f7e4cef606672441c6699632b4fd63ad1abcc Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 8 Mar 2018 21:41:29 +0000 Subject: [PATCH 46/52] Fix up display of nav settings in skyview --- public_html/index.html | 4 ++-- public_html/planeObject.js | 2 +- public_html/script.js | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public_html/index.html b/public_html/index.html index b8f656d..ba582ce 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -212,7 +212,7 @@
Nav alt:
-
+
Nav heading:
@@ -220,7 +220,7 @@
Nav modes:
Nav QNH:
-
+
diff --git a/public_html/planeObject.js b/public_html/planeObject.js index c211f89..db9c56c 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -25,7 +25,7 @@ function PlaneObject(icao) { this.true_heading = null; this.mach = null; this.roll = null; - this.nav_alt = null; + this.nav_altitude = null; this.nav_heading = null; this.nav_modes = null; this.nav_qnh = null; diff --git a/public_html/script.js b/public_html/script.js index c7fb70e..4afbbea 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -955,17 +955,17 @@ function refreshSelected() { $('#selected_track_rate').text(selected.track_rate.toFixed(2)); } $('#selected_geom_rate').text(format_vert_rate_long(selected.geom_rate, DisplayUnits)); - if (selected.alt_setting == null) { - $('#selected_alt_setting').text("n/a"); + if (selected.nav_qnh == null) { + $('#selected_nav_qnh').text("n/a"); } else { - $('#selected_alt_setting').text(selected.alt_setting.toFixed(1) + " hPa"); + $('#selected_nav_qnh').text(selected.nav_qnh.toFixed(1) + " hPa"); } - $('#selected_intent_alt').text(format_altitude_long(selected.intent_alt, 0, DisplayUnits)); - $('#selected_intent_heading').text(format_track_long(selected.intent_heading)); - if (selected.intent_modes == null) { - $('#selected_intent_modes').text("n/a"); + $('#selected_nav_altitude').text(format_altitude_long(selected.nav_altitude, 0, DisplayUnits)); + $('#selected_nav_heading').text(format_track_long(selected.nav_heading)); + if (selected.nav_modes == null) { + $('#selected_nav_modes').text("n/a"); } else { - $('#selected_intent_modes').text(selected.intent_modes.join()); + $('#selected_nav_modes').text(selected.nav_modes.join()); } if (selected.version == null) { From d5eccc30a471a2335b4eef5e9160ebddba9d3d5b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 9 May 2018 14:57:29 +0100 Subject: [PATCH 47/52] If we see a bad NIC supplementary bits combination, return RC_UNKNOWN --- track.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/track.c b/track.c index caabcb1..a14474c 100644 --- a/track.c +++ b/track.c @@ -733,7 +733,7 @@ static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, un } else if (nic_a && nic_b) { return 1112; // 1111.2m, 0.6NM } else { - return 1112; // bad combination, assume worst Rc + return RC_UNKNOWN; // bad combination } } else if (version == 1) { if (nic_a) { From 7749316540d7e7276316126c63aadc9ca1b08b7f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 9 May 2018 14:57:52 +0100 Subject: [PATCH 48/52] Fix 10NM Rc value --- track.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/track.c b/track.c index a14474c..83d37df 100644 --- a/track.c +++ b/track.c @@ -765,7 +765,7 @@ static unsigned compute_rc(unsigned metype, unsigned version, unsigned nic_a, un return 14816; // 8NM } } else { - return 18510; // 10NM + return 18520; // 10NM } case 17: // airborne From 9efd6b34700c3578f83251b8a331047f553b4bf2 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 9 May 2018 15:08:40 +0100 Subject: [PATCH 49/52] Mapzen is shutting down, remove the mapzen support --- public_html/config.js | 9 ---- public_html/layers.js | 100 ------------------------------------------ 2 files changed, 109 deletions(-) diff --git a/public_html/config.js b/public_html/config.js index 347c947..8b34bc2 100644 --- a/public_html/config.js +++ b/public_html/config.js @@ -118,12 +118,3 @@ ChartBundleLayers = false; // BingMapsAPIKey = "your key here"; // BingMapsAPIKey = null; - -// Provide a Mapzen API key here to enable the Mapzen vector tile layer. -// You can obtain a free key at https://mapzen.com/developers/ -// (you need a "vector tiles" key) -// -// Be sure to quote your key: -// MapzenAPIKey = "your key here"; -// -MapzenAPIKey = null; diff --git a/public_html/layers.js b/public_html/layers.js index 1a04475..8dd9218 100644 --- a/public_html/layers.js +++ b/public_html/layers.js @@ -37,10 +37,6 @@ function createBaseLayers() { })); } - if (MapzenAPIKey) { - world.push(createMapzenLayer()); - } - if (ChartBundleLayers) { var chartbundleTypes = { sec: "Sectional Charts", @@ -105,99 +101,3 @@ function createBaseLayers() { return layers; } - -function createMapzenLayer() { - // draw earth with a fat stroke; - // force water above earth - - var earthStyle = new ol.style.Style({ - fill: new ol.style.Fill({ - color: '#a06000' - }), - stroke: new ol.style.Stroke({ - color: '#a06000', - width: 5.0 - }), - zIndex: 0 - }); - - var waterStyle = new ol.style.Style({ - fill: new ol.style.Fill({ - color: '#0040a0' - }), - stroke: new ol.style.Stroke({ - color: '#0040a0', - width: 1.0 - }), - zIndex: 1 - }); - - var boundaryStyle = new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: '#804000', - width: 2.0 - }), - zIndex: 2 - }); - - var dashedBoundaryStyle = new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: '#804000', - width: 1.0, - lineDash: [4, 4], - }), - zIndex: 2 - }); - - var styleMap = { - earth: earthStyle, - - water: waterStyle, - basin: waterStyle, - dock: waterStyle, - lake: waterStyle, - ocean: waterStyle, - riverbank: waterStyle, - river: waterStyle, - - country: boundaryStyle, - disputed: dashedBoundaryStyle, - indefinite: dashedBoundaryStyle, - indeterminate: dashedBoundaryStyle, - line_of_control: dashedBoundaryStyle - }; - - return new ol.layer.VectorTile({ - name: 'mapzen_vector', - title: 'Mapzen coastlines and water', - type: 'base', - renderMode: 'image', - renderOrder: function(a,b) { - return a.get('sort_key') - b.get('sort_key'); - }, - source: new ol.source.VectorTile({ - url: '//vector.mapzen.com/osm/earth,water,boundaries/{z}/{x}/{y}.topojson?api_key=' + MapzenAPIKey, - format: new ol.format.TopoJSON(), - attributions: [ - new ol.Attribution({ - html: 'Tiles courtesy of Mapzen' - }), - new ol.Attribution({ - html: '© OpenStreetMap contributors' - }) - ], - - tileGrid: ol.tilegrid.createXYZ({ - preload: 3, - maxZoom: 14, - tileSize: [512, 512] - }), - - wrapX: true - }), - - style: function (feature) { - return (styleMap[feature.get('kind')]); - } - }); -} From 084acd295128ecf4e6ce93ce1fc2a664efe97ca7 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 9 May 2018 15:16:05 +0100 Subject: [PATCH 50/52] Remove chartbundle WAC, add helicopter chart. (See https://github.com/mutability/dump1090/pull/212) --- public_html/layers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public_html/layers.js b/public_html/layers.js index 8dd9218..431e9af 100644 --- a/public_html/layers.js +++ b/public_html/layers.js @@ -41,7 +41,7 @@ function createBaseLayers() { var chartbundleTypes = { sec: "Sectional Charts", tac: "Terminal Area Charts", - wac: "World Aeronautical Charts", + hel: "Helicopter Charts", enrl: "IFR Enroute Low Charts", enra: "IFR Area Charts", enrh: "IFR Enroute High Charts" From c9e729a48a726152672f0def82ee5ca7c32601ec Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 9 May 2018 15:20:45 +0100 Subject: [PATCH 51/52] Hide extended data fields by default --- public_html/config.js | 5 +++++ public_html/index.html | 2 ++ public_html/script.js | 6 +++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/public_html/config.js b/public_html/config.js index 8b34bc2..5a134e3 100644 --- a/public_html/config.js +++ b/public_html/config.js @@ -118,3 +118,8 @@ ChartBundleLayers = false; // BingMapsAPIKey = "your key here"; // BingMapsAPIKey = null; + +// Turn on display of extra Mode S EHS / ADS-B v1/v2 data +// This is not polished yet (and so is disabled by default), +// currently it's just a data dump of the new fields with no UX work. +ExtendedData = false; diff --git a/public_html/index.html b/public_html/index.html index ba582ce..dd25799 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -172,6 +172,7 @@
+ AirFrames.org diff --git a/public_html/script.js b/public_html/script.js index 4afbbea..43be896 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -197,7 +197,11 @@ function initialize() { refreshClock(); $("#loader").removeClass("hidden"); - + + if (ExtendedData) { + $("#extendedData").removeClass("hidden"); + } + // Set up map/sidebar splitter $("#sidebar_container").resizable({handles: {w: '#splitter'}}); From c9997a783f862e3329c8c375e004b30de111653d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 9 May 2018 15:33:26 +0100 Subject: [PATCH 52/52] Also turn on extended data with #extended in the URL --- public_html/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public_html/script.js b/public_html/script.js index 43be896..73f89e4 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -198,7 +198,7 @@ function initialize() { $("#loader").removeClass("hidden"); - if (ExtendedData) { + if (ExtendedData || window.location.hash == '#extended') { $("#extendedData").removeClass("hidden"); }