Merge remote-tracking branch 'upstream/master' into dev
This commit is contained in:
commit
d3fad32d5e
|
@ -195,6 +195,10 @@ typedef enum {
|
||||||
SIL_PER_SAMPLE, SIL_PER_HOUR
|
SIL_PER_SAMPLE, SIL_PER_HOUR
|
||||||
} sil_type_t;
|
} 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_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses
|
||||||
|
|
||||||
#define MODES_DEBUG_DEMOD (1<<0)
|
#define MODES_DEBUG_DEMOD (1<<0)
|
||||||
|
@ -455,6 +459,7 @@ struct modesMessage {
|
||||||
// valid if category_valid
|
// valid if category_valid
|
||||||
unsigned category; // A0 - D7 encoded as a single hex byte
|
unsigned category; // A0 - D7 encoded as a single hex byte
|
||||||
// valid if cpr_valid
|
// 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_lat; // Non decoded latitude.
|
||||||
unsigned cpr_lon; // Non decoded longitude.
|
unsigned cpr_lon; // Non decoded longitude.
|
||||||
unsigned cpr_nucp; // NUCp/NIC value implied by message type
|
unsigned cpr_nucp; // NUCp/NIC value implied by message type
|
||||||
|
|
|
@ -66,7 +66,7 @@ static int convert_altitude(int ft)
|
||||||
static int convert_speed(int kts)
|
static int convert_speed(int kts)
|
||||||
{
|
{
|
||||||
if (Modes.metric)
|
if (Modes.metric)
|
||||||
return (kts / 1.852);
|
return (kts * 1.852);
|
||||||
else
|
else
|
||||||
return kts;
|
return kts;
|
||||||
}
|
}
|
||||||
|
|
22
mode_s.c
22
mode_s.c
|
@ -903,6 +903,7 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf)
|
||||||
mm->cpr_odd = getbit(me, 22);
|
mm->cpr_odd = getbit(me, 22);
|
||||||
mm->cpr_nucp = (14 - mm->metype);
|
mm->cpr_nucp = (14 - mm->metype);
|
||||||
mm->cpr_valid = 1;
|
mm->cpr_valid = 1;
|
||||||
|
mm->cpr_type = CPR_SURFACE;
|
||||||
|
|
||||||
unsigned movement = getbits(me, 6, 12);
|
unsigned movement = getbits(me, 6, 12);
|
||||||
if (movement > 0 && movement < 125) {
|
if (movement > 0 && movement < 125) {
|
||||||
|
@ -948,6 +949,7 @@ static void decodeESAirbornePosition(struct modesMessage *mm, int check_imf)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, assume it's valid.
|
// Otherwise, assume it's valid.
|
||||||
mm->cpr_valid = 1;
|
mm->cpr_valid = 1;
|
||||||
|
mm->cpr_type = CPR_AIRBORNE;
|
||||||
mm->cpr_odd = getbit(me, 22);
|
mm->cpr_odd = getbit(me, 22);
|
||||||
|
|
||||||
if (mm->metype == 18 || mm->metype == 22)
|
if (mm->metype == 18 || mm->metype == 22)
|
||||||
|
@ -1388,6 +1390,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) {
|
static void print_hex_bytes(unsigned char *data, size_t len) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
|
@ -1678,12 +1693,11 @@ void displayModesMessage(struct modesMessage *mm) {
|
||||||
mm->category);
|
mm->category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm->msgtype == 17 || mm->msgtype == 18) {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mm->cpr_valid) {
|
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 NUCp/NIC: %u\n",
|
||||||
|
cpr_type_to_string(mm->cpr_type),
|
||||||
mm->cpr_odd ? "odd" : "even",
|
mm->cpr_odd ? "odd" : "even",
|
||||||
mm->cpr_nucp);
|
mm->cpr_nucp);
|
||||||
|
|
||||||
|
|
204
tools/fuzzy-30003-matcher.py
Executable file
204
tools/fuzzy-30003-matcher.py
Executable file
|
@ -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))
|
12
track.c
12
track.c
|
@ -255,7 +255,7 @@ static int doGlobalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
int fflag = mm->cpr_odd;
|
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
|
*nuc = (a->cpr_even_nuc < a->cpr_odd_nuc ? a->cpr_even_nuc : a->cpr_odd_nuc); // worst of the two positions
|
||||||
|
|
||||||
|
@ -336,7 +336,7 @@ static int doLocalCPR(struct aircraft *a, struct modesMessage *mm, uint64_t now,
|
||||||
double range_limit = 0;
|
double range_limit = 0;
|
||||||
int result;
|
int result;
|
||||||
int fflag = mm->cpr_odd;
|
int fflag = mm->cpr_odd;
|
||||||
int surface = (mm->airground == AG_GROUND);
|
int surface = (mm->cpr_type == CPR_SURFACE);
|
||||||
|
|
||||||
*nuc = mm->cpr_nucp;
|
*nuc = mm->cpr_nucp;
|
||||||
|
|
||||||
|
@ -421,7 +421,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t
|
||||||
unsigned new_nuc = 0;
|
unsigned new_nuc = 0;
|
||||||
int surface;
|
int surface;
|
||||||
|
|
||||||
surface = (mm->airground == AG_GROUND);
|
surface = (mm->cpr_type == CPR_SURFACE);
|
||||||
|
|
||||||
if (surface) {
|
if (surface) {
|
||||||
++Modes.stats_current.cpr_surface;
|
++Modes.stats_current.cpr_surface;
|
||||||
|
@ -441,7 +441,7 @@ static void updatePosition(struct aircraft *a, struct modesMessage *mm, uint64_t
|
||||||
// If we have enough recent data, try global CPR
|
// If we have enough recent data, try global CPR
|
||||||
if (trackDataValid(&a->cpr_odd_valid) && trackDataValid(&a->cpr_even_valid) &&
|
if (trackDataValid(&a->cpr_odd_valid) && trackDataValid(&a->cpr_even_valid) &&
|
||||||
a->cpr_odd_valid.source == a->cpr_even_valid.source &&
|
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) {
|
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, now, &new_lat, &new_lon, &new_nuc);
|
||||||
|
@ -609,7 +609,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
|
|
||||||
// CPR, even
|
// 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, now)) {
|
||||||
a->cpr_even_airground = mm->airground;
|
a->cpr_even_type = mm->cpr_type;
|
||||||
a->cpr_even_lat = mm->cpr_lat;
|
a->cpr_even_lat = mm->cpr_lat;
|
||||||
a->cpr_even_lon = mm->cpr_lon;
|
a->cpr_even_lon = mm->cpr_lon;
|
||||||
a->cpr_even_nuc = mm->cpr_nucp;
|
a->cpr_even_nuc = mm->cpr_nucp;
|
||||||
|
@ -617,7 +617,7 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
|
||||||
|
|
||||||
// CPR, odd
|
// 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, now)) {
|
||||||
a->cpr_odd_airground = mm->airground;
|
a->cpr_odd_type = mm->cpr_type;
|
||||||
a->cpr_odd_lat = mm->cpr_lat;
|
a->cpr_odd_lat = mm->cpr_lat;
|
||||||
a->cpr_odd_lon = mm->cpr_lon;
|
a->cpr_odd_lon = mm->cpr_lon;
|
||||||
a->cpr_odd_nuc = mm->cpr_nucp;
|
a->cpr_odd_nuc = mm->cpr_nucp;
|
||||||
|
|
4
track.h
4
track.h
|
@ -123,13 +123,13 @@ struct aircraft {
|
||||||
airground_t airground; // air/ground status
|
airground_t airground; // air/ground status
|
||||||
|
|
||||||
data_validity cpr_odd_valid; // Last seen even CPR message
|
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_lat;
|
||||||
unsigned cpr_odd_lon;
|
unsigned cpr_odd_lon;
|
||||||
unsigned cpr_odd_nuc;
|
unsigned cpr_odd_nuc;
|
||||||
|
|
||||||
data_validity cpr_even_valid; // Last seen odd CPR message
|
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_lat;
|
||||||
unsigned cpr_even_lon;
|
unsigned cpr_even_lon;
|
||||||
unsigned cpr_even_nuc;
|
unsigned cpr_even_nuc;
|
||||||
|
|
Loading…
Reference in a new issue