From 5d681f4819d3d3f12b664ea549a0b3437628d913 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 16 Sep 2016 11:19:26 +0100 Subject: [PATCH 1/8] faup1090: Don't bother with the tisb field now that we have addrtype. (cherry picked from commit df9648911e10680b92641405037d1efaccc85ced) --- net_io.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/net_io.c b/net_io.c index 74d2a28..49c3af3 100644 --- a/net_io.c +++ b/net_io.c @@ -1820,8 +1820,6 @@ static void writeFATSV() char *p, *end; - int used_tisb = 0; - if (a->messages < 2) // basic filter for bad decodes continue; @@ -1932,18 +1930,11 @@ static void writeFATSV() break; } - if (a->callsign_valid.source == SOURCE_TISB) { - used_tisb = 1; - } - useful = 1; } if (trackDataValidEx(&a->squawk_valid, now, 15000, SOURCE_MODE_S) && a->squawk_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->squawk); - if (a->squawk_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } @@ -1953,89 +1944,59 @@ static void writeFATSV() 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; - if (a->altitude_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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 (a->altitude_gnss_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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 (a->speed_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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 (a->speed_ias_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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 (a->speed_tas_valid.source == SOURCE_TISB) { - used_tisb = 1; - } 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); - if (a->position_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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 (a->heading_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\theading_magnetic\t%d", a->heading); a->fatsv_emitted_heading_magnetic = a->heading_magnetic; - if (a->heading_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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; - if (a->airground_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } 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); - if (a->category_valid.source == SOURCE_TISB) { - used_tisb = 1; - } useful = 1; } @@ -2045,10 +2006,6 @@ static void writeFATSV() continue; } - if (used_tisb) { - p += snprintf(p, bufsize(p,end), "\ttisb\t1"); - } - p += snprintf(p, bufsize(p,end), "\n"); if (p <= end) From f6075212f4f9a96dc775651f6e21203c53944e75 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 22 Sep 2016 13:53:01 +0100 Subject: [PATCH 2/8] faup1090: Reinstate TIS-B reporting as just the address type is not sufficient if there is both TIS-B and Mode S for the same aircraft. (cherry picked from commit 32c8358bc15e075aab07d39c9cbd53e1aebf5c45) --- net_io.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/net_io.c b/net_io.c index 49c3af3..f1a7d7a 100644 --- a/net_io.c +++ b/net_io.c @@ -1772,6 +1772,21 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) } } +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_POSITION = 128, + TISB_HEADING = 256, + TISB_HEADING_MAGNETIC = 512, + TISB_AIRGROUND = 1024, + TISB_CATEGORY = 2048 +} tisb_flags; + static inline unsigned unsigned_difference(unsigned v1, unsigned v2) { return (v1 > v2) ? (v1 - v2) : (v2 - v1); @@ -1817,6 +1832,7 @@ static void writeFATSV() int useful = 0; int changed = 0; + tisb_flags tisb = 0; char *p, *end; @@ -1931,11 +1947,13 @@ static void writeFATSV() } useful = 1; + tisb |= (a->callsign_valid.source == SOURCE_TISB) ? TISB_IDENT : 0; } if (trackDataValidEx(&a->squawk_valid, now, 15000, 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; } // only emit alt, speed, latlon, track if they have been received since the last time @@ -1945,59 +1963,69 @@ 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; } 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; useful = 1; + tisb |= (a->altitude_gnss_valid.source == SOURCE_TISB) ? TISB_ALTITUDE_GNSS : 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; 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; 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; useful = 1; + tisb |= (a->speed_tas_valid.source == SOURCE_TISB) ? TISB_SPEED_TAS : 0; } 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; } 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; useful = 1; + tisb |= (a->heading_valid.source == SOURCE_TISB) ? TISB_HEADING : 0; } if (headingMagValid && a->heading_magnetic_valid.updated > a->fatsv_last_emitted) { p += snprintf(p, bufsize(p,end), "\theading_magnetic\t%d", a->heading); a->fatsv_emitted_heading_magnetic = a->heading_magnetic; useful = 1; + tisb |= (a->heading_magnetic_valid.source == SOURCE_TISB) ? TISB_HEADING_MAGNETIC : 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 we didn't get anything interesting, bail out. @@ -2006,6 +2034,10 @@ static void writeFATSV() continue; } + if (tisb != 0) { + p += snprintf(p, bufsize(p,end), "\ttisb\t%d", (int)tisb); + } + p += snprintf(p, bufsize(p,end), "\n"); if (p <= end) From 7cd66c36d2cbdb806b3d091e524e08cc999ec62c Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 22 Sep 2016 15:33:21 +0100 Subject: [PATCH 3/8] Increase the TSV packet size to accomodate all the extra data recently added. (cherry picked from commit abc30ff6567d13fb24b4f25cbeadac2896fff6ca) --- net_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index f1a7d7a..53bd578 100644 --- a/net_io.c +++ b/net_io.c @@ -1681,7 +1681,7 @@ static void modesReadFromClient(struct client *c) { } } -#define TSV_MAX_PACKET_SIZE 180 +#define TSV_MAX_PACKET_SIZE 275 static void writeFATSVEventMessage(struct modesMessage *mm, const char *datafield, unsigned char *data, size_t len) { From dda541188213b0fd474c15d058c7e6904e448ae4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 24 Sep 2016 16:09:38 +0100 Subject: [PATCH 4/8] faup1090: Track Comm-B ACAS RA separately to ES ACAS RA as it's useful to see both reported. Fix repeated reporting of ES ACAS RA reports. (cherry picked from commit 14a003710e68be97e75fabf7e794fe718145da4a) --- net_io.c | 4 ++-- track.c | 4 +++- track.h | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/net_io.c b/net_io.c index 53bd578..a16d436 100644 --- a/net_io.c +++ b/net_io.c @@ -1754,10 +1754,10 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a) case 17: case 18: // DF 17/18: extended squitter - if (mm->metype == 28 && mm->mesub == 2 && memcmp(&mm->ME[1], &a->fatsv_emitted_bds_30[1], 6) != 0) { + if (mm->metype == 28 && mm->mesub == 2 && memcmp(mm->ME, &a->fatsv_emitted_es_acas_ra, 7) != 0) { // type 28 subtype 2: ACAS RA report // first byte has the type/subtype, remaining bytes match the BDS 3,0 format - memcpy(a->fatsv_emitted_bds_30, &mm->ME[1], 6); + memcpy(a->fatsv_emitted_es_acas_ra, mm->ME, 7); writeFATSVEventMessage(mm, "es_acas_ra", mm->ME, 7); } else if (mm->metype == 31 && (mm->mesub == 0 || mm->mesub == 1) && memcmp(mm->ME, a->fatsv_emitted_es_status, 7) != 0) { // aircraft operational status diff --git a/track.c b/track.c index 7fff6f7..27172f8 100644 --- a/track.c +++ b/track.c @@ -71,8 +71,10 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) { a->signalLevel[i] = 1e-5; a->signalNext = 0; - // start off with the "last emitted" ACAS RA being blank (just the BDS 3,0 code) + // 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; // mm->msgtype 32 is used to represent Mode A/C. These values can never change, so // set them once here during initialisation, and don't bother to set them every diff --git a/track.h b/track.h index 574c29f..9326f10 100644 --- a/track.h +++ b/track.h @@ -150,6 +150,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_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 617a71e36b921954c3b27d121ddb96e4d04bd73b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 1 Oct 2016 13:11:14 +0100 Subject: [PATCH 5/8] Fix a stray extra comma in port 30003 output if only GNSS altitude was available. (cherry picked from commit 6f2e4fa891700e63ef43fcb5c1f4a82a8c35be7f) --- net_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index a16d436..e2f5ea1 100644 --- a/net_io.c +++ b/net_io.c @@ -594,7 +594,7 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { } else if (trackDataValid(&a->gnss_delta_valid)) { p += sprintf(p, ",%d", mm->altitude - a->gnss_delta); } else { - p += sprintf(p, ",,"); + p += sprintf(p, ","); } } } else { From 52fe01c8602b3faf6e41ac372cd7a932d7b946a7 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 2 Oct 2016 00:16:29 +0100 Subject: [PATCH 6/8] Track CPR encoding type directly rather than inferring it from airground. (airground can change even while airborne if SPI/Alert status gets set) --- dump1090.h | 5 +++++ mode_s.c | 22 ++++++++++++++++++---- track.c | 12 ++++++------ track.h | 4 ++-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/dump1090.h b/dump1090.h index b1cba8f..c3059c3 100644 --- a/dump1090.h +++ b/dump1090.h @@ -198,6 +198,10 @@ typedef enum { SIL_PER_SAMPLE, SIL_PER_HOUR } sil_type_t; +typedef enum { + CPR_SURFACE, CPR_AIRBORNE, CPR_COARSE +} cpr_type_t; + #define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses #define MODES_DEBUG_DEMOD (1<<0) @@ -461,6 +465,7 @@ struct modesMessage { // valid if category_valid 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 diff --git a/mode_s.c b/mode_s.c index 1580ba0..2c61980 100644 --- a/mode_s.c +++ b/mode_s.c @@ -903,6 +903,7 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf) mm->cpr_odd = getbit(me, 22); mm->cpr_nucp = (14 - mm->metype); mm->cpr_valid = 1; + mm->cpr_type = CPR_SURFACE; unsigned movement = getbits(me, 6, 12); if (movement > 0 && movement < 125) { @@ -948,6 +949,7 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf) } else { // Otherwise, assume it's valid. mm->cpr_valid = 1; + mm->cpr_type = CPR_AIRBORNE; mm->cpr_odd = getbit(me, 22); if (mm->metype == 18 || mm->metype == 22) @@ -1386,6 +1388,19 @@ static const char *addrtype_to_string(addrtype_t type) { } } +static const char *cpr_type_to_string(cpr_type_t type) { + switch (type) { + case CPR_SURFACE: + return "Surface"; + case CPR_AIRBORNE: + return "Airborne"; + case CPR_COARSE: + return "TIS-B Coarse"; + default: + return "unknown CPR type"; + } +} + static void print_hex_bytes(unsigned char *data, size_t len) { size_t i; for (i = 0; i < len; ++i) { @@ -1676,12 +1691,11 @@ void displayModesMessage(struct modesMessage *mm) { mm->category); } - if (mm->msgtype == 17 || mm->msgtype == 18) { - } - if (mm->cpr_valid) { - printf(" CPR odd flag: %s\n" + printf(" CPR type: %s\n" + " CPR odd flag: %s\n" " CPR NUCp/NIC: %u\n", + cpr_type_to_string(mm->cpr_type), mm->cpr_odd ? "odd" : "even", mm->cpr_nucp); diff --git a/track.c b/track.c index 27172f8..cf8353a 100644 --- a/track.c +++ b/track.c @@ -260,7 +260,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now { int result; int fflag = mm->cpr_odd; - int surface = (mm->airground == AG_GROUND); + 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 @@ -341,7 +341,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now, double range_limit = 0; int result; int fflag = mm->cpr_odd; - int surface = (mm->airground == AG_GROUND); + int surface = (mm->cpr_type == CPR_SURFACE); *nuc = mm->cpr_nucp; @@ -426,7 +426,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t unsigned new_nuc = 0; int surface; - surface = (mm->airground == AG_GROUND); + surface = (mm->cpr_type == CPR_SURFACE); if (surface) { ++Modes.stats_current.cpr_surface; @@ -446,7 +446,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t // If we have enough recent data, try global CPR if (trackDataValid(&a->cpr_odd_valid) && trackDataValid(&a->cpr_even_valid) && a->cpr_odd_valid.source == a->cpr_even_valid.source && - a->cpr_odd_airground == a->cpr_even_airground && + 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); @@ -607,7 +607,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm) // CPR, even if (mm->cpr_valid && !mm->cpr_odd && accept_data(&a->cpr_even_valid, mm->source, now)) { - a->cpr_even_airground = mm->airground; + 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; @@ -615,7 +615,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)) { - a->cpr_odd_airground = mm->airground; + 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; diff --git a/track.h b/track.h index 9326f10..85db656 100644 --- a/track.h +++ b/track.h @@ -119,13 +119,13 @@ struct aircraft { airground_t airground; // air/ground status data_validity cpr_odd_valid; // Last seen even CPR message - airground_t cpr_odd_airground; + cpr_type_t cpr_odd_type; unsigned cpr_odd_lat; unsigned cpr_odd_lon; unsigned cpr_odd_nuc; data_validity cpr_even_valid; // Last seen odd CPR message - airground_t cpr_even_airground; + cpr_type_t cpr_even_type; unsigned cpr_even_lat; unsigned cpr_even_lon; unsigned cpr_even_nuc; From 344415b91f018e7fd3719fe639888505c54792ad Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 2 Oct 2016 13:27:13 +0100 Subject: [PATCH 7/8] Add a port-30003 regression diff script. --- tools/fuzzy-30003-matcher.py | 204 +++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100755 tools/fuzzy-30003-matcher.py diff --git a/tools/fuzzy-30003-matcher.py b/tools/fuzzy-30003-matcher.py new file mode 100755 index 0000000..255166a --- /dev/null +++ b/tools/fuzzy-30003-matcher.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 + +# +# Regression testing helper: takes a 3.0.5 port-30003 output file +# and a 3.1.0 port-30003 output file and generates a diff, after +# dealing with the known formatting / data differences + +import csv +from contextlib import closing + +horizon=5 + +def fuzzy_match_details(l1, l2): + _, _, type1, _, _, addr1, _, _, _, _, _, cs1, alt1, gs1, hdg1, lat1, lon1, vr1, sq1, change1, emerg1, spi1, aog1 = l1 + _, _, type2, _, _, addr2, _, _, _, _, _, cs2, alt2, gs2, hdg2, lat2, lon2, vr2, sq2, change2, emerg2, spi2, aog2 = l2 + + if addr1 != addr2: + return (False, 'adr') + + if type1 != type2: + # 3.0.5: reports DF17 surface/airborne with no position as type 7 + # 3.1.0: reports DF17 surface/airborne with no position as type 2/3 + if type1 != '7': + return (False, 'typ') + if type2 != '2' and type2 != '3': + return (False, 'typ') + if lat1 != '' or lon1 != '': + return (False, 'typ') + + if alt1 != alt2: + # 3.0.5: omits altitude in DF17 if no position was decoded + # 3.1.0: includes it + if type1 != '7' or alt1 != '' or alt2 == '': + return (False, 'alt') + + if gs1 != gs2: + # 3.0.5: truncates computed GS + # 3.1.0: rounds computed GS + if gs1 == '' or gs2 == '' or abs(int(gs1) - int(gs2)) > 1: + return (False, 'gs ') + if hdg1 != hdg2: + # 3.0.5: truncates computed heading + # 3.1.0: rounds computed heading + if hdg1 == '' or hdg2 == '': + return (False, 'hdg') + delta = abs(int(hdg1) - int(hdg2)) + if delta > 180: + delta = 360 - delta + if delta > 1: + return False + + if lat1 != lat2: + return (False, 'lat') + if lon1 != lon2: + return (False, 'lon') + if vr1 != vr2: + return (False, 'vr ') + + if sq1 != sq2: + # 3.0.5: strips leading zeros + # 3.1.0: preserves leading zeros + if ('0' + sq1) != sq2 and ('00' + sq1) != sq2 and ('000' + sq1) != sq2: + return (False, 'sqk') + + # 3.1.0: only reports these when available + if change1 != change2: + if change1 != '0' or change2 != '': + return (False, 'chg') + if emerg1 != emerg2: + if emerg1 != '0' or emerg2 != '': + return (False, 'emg') + if spi1 != spi2: + if spi1 != '0' or spi2 != '': + return (False, 'spi') + + if aog1 != aog2: + # 3.1.0: different rules for when AOG is reported + if aog1 != '' and aog2 != '': + return (False, 'aog') + + return (True, None) + +def fuzzy_match(l1, l2): + return fuzzy_match_details(l1, l2)[0] + +def fuzzy_match_reason(l1, l2): + return fuzzy_match_details(l1, l2)[1] + +def next_line(reader, queue): + if queue: + return queue.pop() + line = next(reader, None) + if line is None or len(line) == 0: + return None + else: + return [reader.line_num] + line + +def unpush_line(queue, line): + queue.insert(0, line) + +def csv_diff(path1, path2): + diffs = [] + q1 = [] + q2 = [] + + with closing(open(path1, 'r')) as f1, closing(open(path2, 'r')) as f2: + r1 = csv.reader(f1) + r2 = csv.reader(f2) + + l1 = next_line(r1, q1) + l2 = next_line(r2, q2) + + while (l1 is not None) or (l2 is not None): + if l1 is None: + yield ('+', None, l2) + l2 = next_line(r2, q2) + continue + + if l2 is None: + yield ('-', l1, None) + l1 = next_line(r1, q1) + continue + + if fuzzy_match(l1, l2): + yield (' ', l1, l2) + l1 = next_line(r1, q1) + l2 = next_line(r2, q2) + continue + + #print('mismatch:', l1, l2) + + save_1 = [] + save_2 = [] + + found = False + for i in range(horizon): + next_l2 = next_line(r2, q2) + if next_l2 is not None: + if fuzzy_match(l1, next_l2): + # skip l2 and any lines in save_2 + # continue with l1 and next_l2 + yield('+', None, l2) + for l in save_2: + yield('+', None, l) + l2 = next_l2 + q1.extend(reversed(save_1)) + found = True + break + else: + save_2.append(next_l2) + + next_l1 = next_line(r1, q1) + if next_l1 is not None: + if fuzzy_match(next_l1, l2): + # skip l1 and any lines in save_1 + # continue with next_l1 and l2 + yield('-', l1, None) + for l in save_1: + yield('-', l, None) + l1 = next_l1 + q2.extend(reversed(save_2)) + found = True + break + else: + save_1.append(next_l1) + + if found: + #print('new l1:', l1) + #print('new l2:', l2) + #print('new q1:') + #for q in q1: print(q) + #print('new q2:') + #for q in q2: print(q) + continue + + #print('lookahead: nothing likely') + + q1.extend(reversed(save_1)) + q2.extend(reversed(save_2)) + yield ('*', l1, l2) + l1 = next_line(r1, q1) + l2 = next_line(r1, q2) + +def format_line(line): + line_num = line[0] + subrow = line[1:3] + line[5:6] + line[11:] + return str(line_num) + ': ' + ','.join(subrow) + +if __name__ == '__main__': + import sys + for action, old, new in csv_diff(sys.argv[1], sys.argv[2]): + if action == ' ': + if False: print (' ' + format_line(new)) + elif action == '*': + reason = fuzzy_match_reason(old, new) + print ('< ' + reason + ' ' + format_line(old)) + print ('> ' + reason + ' ' + format_line(new)) + elif action == '-': + # 3.0.5: emits lines for all-zero messages + # 3.1.0: doesn't + if old[5] != '000000': + print ('- ' + format_line(old)) + elif action == '+': + print ('+ ' + format_line(new)) From 2b47fe5b6886c07a202d2d366bf23e995ba70c44 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 14 Oct 2016 21:29:03 +0100 Subject: [PATCH 8/8] Fix knots -> km/h conversion in interactive mode. Fixes #153 --- interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactive.c b/interactive.c index 98e4c3f..6d3fa5c 100644 --- a/interactive.c +++ b/interactive.c @@ -64,7 +64,7 @@ static int convert_altitude(int ft) static int convert_speed(int kts) { if (Modes.metric) - return (kts / 1.852); + return (kts * 1.852); else return kts; }