New parser works. TCAS untested. Extra info in print. Fixed ground_track printing error. Surface reports suppressed due to possible CPR bug. Not all code paths tested.

This commit is contained in:
Nick Foster 2012-06-26 23:27:58 -07:00
parent b05bea9618
commit 28824cb0b2
6 changed files with 68 additions and 87 deletions

View File

@ -29,11 +29,10 @@ from modes_exceptions import *
latz = 15
def nbits(surface):
return 17
# if surface == 1:
# return 19
# else:
# return 17
if surface == 1:
return 19
else:
return 17
def nz(ctype):
return 4 * latz - ctype
@ -97,18 +96,14 @@ def cpr_resolve_local(my_location, encoded_location, ctype, surface):
def cpr_resolve_global(evenpos, oddpos, mostrecent, surface):
dlateven = dlat(0, surface)
dlatodd = dlat(1, surface)
if surface is True:
scalar = float(2**19)
else:
scalar = float(2**17)
evenpos = [float(evenpos[0]), float(evenpos[1])]
oddpos = [float(oddpos[0]), float(oddpos[1])]
j = math.floor(((nz(1)*evenpos[0] - nz(0)*oddpos[0])/scalar) + 0.5) #latitude index
j = math.floor(((nz(1)*evenpos[0] - nz(0)*oddpos[0])/2**17) + 0.5) #latitude index
rlateven = dlateven * ((j % nz(0))+evenpos[0]/scalar)
rlatodd = dlatodd * ((j % nz(1))+ oddpos[0]/scalar)
rlateven = dlateven * ((j % nz(0))+evenpos[0]/2**17)
rlatodd = dlatodd * ((j % nz(1))+ oddpos[0]/2**17)
#limit to -90, 90
if rlateven > 270.0:
@ -131,14 +126,14 @@ def cpr_resolve_global(evenpos, oddpos, mostrecent, surface):
nlthing = nl(rlat)
ni = max(nlthing - mostrecent, 1)
m = math.floor(((evenpos[1]*(nlthing-1)-oddpos[1]*(nlthing))/scalar)+0.5) #longitude index
m = math.floor(((evenpos[1]*(nlthing-1)-oddpos[1]*(nlthing))/2**17)+0.5) #longitude index
if mostrecent == 0:
enclon = evenpos[1]
else:
enclon = oddpos[1]
rlon = dl * (((ni+m) % ni)+enclon/2**nbits(surface))
rlon = dl * (((ni+m) % ni)+enclon/2**17)
if rlon > 180:
rlon = rlon - 360.0
@ -172,8 +167,6 @@ def range_bearing(loc_a, loc_b):
bearing += 360.0
rnge = math.hypot(distance_East,distance_North)
return [rnge, bearing]
class cpr_decoder:
@ -203,32 +196,30 @@ class cpr_decoder:
#okay, let's traverse the lists and weed out those entries that are older than 15 minutes, as they're unlikely to be useful.
self.weed_poslists()
if surface==1:
validrange = 45
else:
validrange = 180
if icao24 in self.lkplist:
#do emitter-centered local decoding
[decoded_lat, decoded_lon] = cpr_resolve_local(self.lkplist[icao24][0:2], [encoded_lat, encoded_lon], cpr_format, surface)
self.lkplist[icao24] = [decoded_lat, decoded_lon, time.time()] #update the local position for next time
elif ((icao24 in self.evenlist) and (icao24 in self.oddlist) and abs(self.evenlist[icao24][2] - self.oddlist[icao24][2]) < 10):
elif (icao24 in self.evenlist) \
and (icao24 in self.oddlist) \
and (abs(self.evenlist[icao24][2] - self.oddlist[icao24][2]) < 10) \
and (surface == 0):
newer = (self.oddlist[icao24][2] - self.evenlist[icao24][2]) > 0 #figure out which report is newer
[decoded_lat, decoded_lon] = cpr_resolve_global(self.evenlist[icao24][0:2], self.oddlist[icao24][0:2], newer, surface) #do a global decode
[decoded_lat, decoded_lon] = cpr_resolve_global(self.evenlist[icao24][0:2], self.oddlist[icao24][0:2], newer, surface) #do a global decode
self.lkplist[icao24] = [decoded_lat, decoded_lon, time.time()]
else:
raise CPRNoPositionError
#so we really can't guarantee that local decoding will work unless you are POSITIVE that you can't hear more than 180nm out.
#this will USUALLY work, but you can't guarantee it!
# elif self.my_location is not None: #if we have a location, use it
# [local_lat, local_lon] = cpr_resolve_local(self.my_location, [encoded_lat, encoded_lon], cpr_format, surface) #try local decoding
# [rnge, bearing] = range_bearing(self.my_location, [local_lat, local_lon])
# if rnge < validrange: #if the local decoding can be guaranteed valid
# self.lkplist[icao24] = [local_lat, local_lon, time.time()] #update the local position for next time
# [decoded_lat, decoded_lon] = [local_lat, local_lon]
#elif surface == 1 and self.my_location is not None:
# [local_lat, local_lon] = cpr_resolve_local(self.my_location, [encoded_lat, encoded_lon], cpr_format, surface) #try local decoding
# [rnge, bearing] = range_bearing(self.my_location, [local_lat, local_lon])
# if rnge < validrange: #if the local decoding can be guaranteed valid
# self.lkplist[icao24] = [local_lat, local_lon, time.time()] #update the local position for next time
# [decoded_lat, decoded_lon] = [local_lat, local_lon]
else:
raise CPRNoPositionError
if self.my_location is not None:
[rnge, bearing] = range_bearing(self.my_location, [decoded_lat, decoded_lon])

View File

@ -41,8 +41,8 @@ class modes_flightgear(modes_parse.modes_parse):
self.callsigns[icao24] = [ident, actype]
elif 5 <= subtype <= 8: #BDS0,6 pos
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
self.positions[icao24] = [decoded_lat, decoded_lon, altitude]
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
self.positions[icao24] = [decoded_lat, decoded_lon, 0]
self.update(icao24)
elif 9 <= subtype <= 18: #BDS0,5 pos

View File

@ -130,7 +130,7 @@ class me_reply(data_field):
elif ftc == 28:
return 0x61
else:
return NoHandlerError
return NoHandlerError(ftc)
def get_numbits(self):
return 56
@ -163,7 +163,7 @@ class mb_reply(data_field):
bds1 = self.get_bits(33,4)
bds2 = self.get_bits(37,4)
if bds1 not in (0,1,2,3) or bds2 not in (0,):
raise NoHandlerError
raise NoHandlerError(bds1)
return int(bds1)
def get_numbits(self):
@ -263,9 +263,9 @@ class modes_parse:
encoded_lon = data["me"]["lon"]
encoded_lat = data["me"]["lat"]
cpr_format = data["me"]["cpr"]
altitude = decode_alt(data["me"]["alt"], False)
ground_track = data["me"]["gtk"] * 360. / 128
[decoded_lat, decoded_lon, rnge, bearing] = self.cpr.decode(icao24, encoded_lat, encoded_lon, cpr_format, 1)
return [altitude, decoded_lat, decoded_lon, rnge, bearing]
return [ground_track, decoded_lat, decoded_lon, rnge, bearing]
def parseBDS09_0(self, data):
#0: ["sub", "dew", "vew", "dns", "vns", "str", "tr", "svr", "vr"],

View File

@ -60,7 +60,7 @@ class modes_output_print(modes_parse.modes_parse):
output += "No handler for message type %i from %x (but it's in modes_parse)" % (msgtype, ecc)
print output
except NoHandlerError as e:
output += "No handler for message type %i from %x" % (msgtype, ecc)
output += "No handler for message type %s from %x" % (e.msgtype, ecc)
print output
except MetricAltError:
pass
@ -71,7 +71,6 @@ class modes_output_print(modes_parse.modes_parse):
[vs, cc, sl, ri, altitude] = self.parse0(shortdata)
retstr = "Type 0 (short A-A surveillance) from %x at %ift" % (ecc, altitude)
# the ri values below 9 are used for other things. might want to print those someday.
if ri == 0:
retstr += " (No TCAS)"
elif ri == 2:
@ -129,7 +128,7 @@ class modes_output_print(modes_parse.modes_parse):
def print11(self, data, ecc):
[icao24, interrogator, ca] = self.parse11(data, ecc)
retstr = "Type 11 (all call reply) from %x in reply to interrogator %i" % (icao24, interrogator)
retstr = "Type 11 (all call reply) from %x in reply to interrogator %i with capability level %i" % (icao24, interrogator, ca+1)
return retstr
def print17(self, data):
@ -143,8 +142,8 @@ class modes_output_print(modes_parse.modes_parse):
retstr = "Type 17 subtype 04 (ident) from %x type %s ident %s" % (icao24, typestring, msg)
elif subtype >= 5 and subtype <= 8:
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
retstr = "Type 17 subtype 06 (surface report) from %x at (%.6f, %.6f)" % (icao24, decoded_lat, decoded_lon)
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
retstr = "Type 17 subtype 06 (surface report) from %x at (%.6f, %.6f) ground track %i" % (icao24, decoded_lat, decoded_lon, ground_track)
if rnge is not None and bearing is not None:
retstr += " (%.2f @ %.0f)" % (rnge, bearing)

View File

@ -103,25 +103,23 @@ class modes_output_sbs1(modes_parse.modes_parse):
def parse(self, message):
#assembles a SBS-1-style output string from the received message
[msgtype, shortdata, longdata, parity, ecc, reference, timestamp] = message.split()
[data, ecc, reference, timestamp] = message.split()
shortdata = long(shortdata, 16)
longdata = long(longdata, 16)
parity = long(parity, 16)
data = modes_parse.modes_reply(long(data, 16))
ecc = long(ecc, 16)
msgtype = int(msgtype)
msgtype = data["df"]
outmsg = None
if msgtype == 0:
outmsg = self.pp0(shortdata, ecc)
outmsg = self.pp0(data, ecc)
elif msgtype == 4:
outmsg = self.pp4(shortdata, ecc)
outmsg = self.pp4(data, ecc)
elif msgtype == 5:
outmsg = self.pp5(shortdata, ecc)
outmsg = self.pp5(data, ecc)
elif msgtype == 11:
outmsg = self.pp11(shortdata, ecc)
outmsg = self.pp11(data, ecc)
elif msgtype == 17:
outmsg = self.pp17(shortdata, longdata)
outmsg = self.pp17(data)
else:
raise NoHandlerError(msgtype)
return outmsg
@ -147,7 +145,7 @@ class modes_output_sbs1(modes_parse.modes_parse):
def pp5(self, shortdata, ecc):
# I'm not sure what to do with the identiifcation shortdata & 0x1FFF
[datestr, timestr] = self.current_time()
[fs, dr, um] = self.parse5(shortdata)
[fs, dr, um, ident] = self.parse5(shortdata)
aircraft_id = self.get_aircraft_id(ecc)
retstr = "MSG,6,0,%i,%06X,%i,%s,%s,%s,%s,,,,,,,,," % (aircraft_id, ecc, aircraft_id+100, datestr, timestr, datestr, timestr)
return retstr + self.decode_fs(fs) + "\n"
@ -158,10 +156,10 @@ class modes_output_sbs1(modes_parse.modes_parse):
aircraft_id = self.get_aircraft_id(icao24)
return "MSG,8,0,%i,%06X,%i,%s,%s,%s,%s,,,,,,,,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr)
def pp17(self, shortdata, longdata):
icao24 = shortdata & 0xFFFFFF
def pp17(self, data):
icao24 = data["aa"]
aircraft_id = self.get_aircraft_id(icao24)
subtype = (longdata >> 51) & 0x1F
subtype = data["me"]["ftc"]
retstr = None
#we'll get better timestamps later, hopefully with actual VRT time
@ -170,23 +168,22 @@ class modes_output_sbs1(modes_parse.modes_parse):
if subtype >= 1 and subtype <= 4:
# Aircraft Identification
(msg, typestring) = self.parseBDS08(shortdata, longdata)
(msg, typestring) = self.parseBDS08(data)
retstr = "MSG,1,0,%i,%06X,%i,%s,%s,%s,%s,%s,,,,,,,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, msg)
elif subtype >= 5 and subtype <= 8:
# Surface position measurement
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(shortdata, longdata)
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
altitude = 0
if decoded_lat is None: #no unambiguously valid position available
retstr = None
else:
retstr = "MSG,2,0,%i,%06X,%i,%s,%s,%s,%s,,%i,,,%.5f,%.5f,,,,0,0,0\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, altitude, decoded_lat, decoded_lon)
elif subtype >= 9 and subtype <= 18 and subtype != 15:
elif subtype >= 9 and subtype <= 18:
# Airborne position measurements
# WRONG (rnge, bearing), is this still true?
# i'm eliminating type 15 records because they don't appear to be
# valid position reports.
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(shortdata, longdata)
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(data)
if decoded_lat is None: #no unambiguously valid position available
retstr = None
else:
@ -195,13 +192,13 @@ class modes_output_sbs1(modes_parse.modes_parse):
elif subtype == 19:
# Airborne velocity measurements
# WRONG (heading, vert_spd), Is this still true?
subsubtype = (longdata >> 48) & 0x07
subsubtype = data["me"]["bds09"]["sub"]
if subsubtype == 0:
[velocity, heading, vert_spd] = self.parseBDS09_0(shortdata, longdata)
[velocity, heading, vert_spd] = self.parseBDS09_0(data)
retstr = "MSG,4,0,%i,%06X,%i,%s,%s,%s,%s,,,%.1f,%.1f,,,%i,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, velocity, heading, vert_spd)
elif subsubtype == 1:
[velocity, heading, vert_spd] = self.parseBDS09_1(shortdata, longdata)
elif 1 <= subsubtype <= 2:
[velocity, heading, vert_spd] = self.parseBDS09_1(data)
retstr = "MSG,4,0,%i,%06X,%i,%s,%s,%s,%s,,,%.1f,%.1f,,,%i,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, velocity, heading, vert_spd)
return retstr

View File

@ -72,59 +72,53 @@ class modes_output_sql(modes_parse.modes_parse):
def make_insert_query(self, message):
#assembles a SQL query tailored to our database
#this version ignores anything that isn't Type 17 for now, because we just don't care
[msgtype, shortdata, longdata, parity, ecc, reference, timestamp] = message.split()
[data, ecc, reference, timestamp] = message.split()
shortdata = long(shortdata, 16)
longdata = long(longdata, 16)
parity = long(parity, 16)
data = modes_parse.modes_reply(long(data, 16))
ecc = long(ecc, 16)
# reference = float(reference)
msgtype = int(msgtype)
query = None
msgtype = data["df"]
if msgtype == 17:
query = self.sql17(shortdata, longdata)
query = self.sql17(data)
return query
def sql17(self, shortdata, longdata):
icao24 = shortdata & 0xFFFFFF
subtype = (longdata >> 51) & 0x1F
def sql17(self, data):
icao24 = data["aa"]
subtype = data["me"]["ftc"]
retstr = None
if subtype == 4:
(msg, typename) = self.parseBDS08(shortdata, longdata)
(msg, typename) = self.parseBDS08(data)
retstr = "INSERT OR REPLACE INTO ident (icao, ident) VALUES (" + "%i" % icao24 + ", '" + msg + "')"
elif subtype >= 5 and subtype <= 8:
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(shortdata, longdata)
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
altitude = 0
if decoded_lat is None: #no unambiguously valid position available
retstr = None
else:
retstr = "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (" + "%i" % icao24 + ", datetime('now'), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
elif subtype >= 9 and subtype <= 18 and subtype != 15: #i'm eliminating type 15 records because they don't appear to be valid position reports.
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(shortdata, longdata)
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(data)
if decoded_lat is None: #no unambiguously valid position available
retstr = None
else:
retstr = "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (" + "%i" % icao24 + ", datetime('now'), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
elif subtype == 19:
subsubtype = (longdata >> 48) & 0x07
subsubtype = data["me"]["bds09"]["sub"]
if subsubtype == 0:
[velocity, heading, vert_spd] = self.parseBDS09_0(shortdata, longdata)
[velocity, heading, vert_spd] = self.parseBDS09_0(data)
retstr = "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")";
elif subsubtype == 1:
[velocity, heading, vert_spd] = self.parseBDS09_1(shortdata, longdata)
elif 1 <= subsubtype <= 2:
[velocity, heading, vert_spd] = self.parseBDS09_1(data)
retstr = "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")";
return retstr