diff --git a/python/cpr.py b/python/cpr.py index 5639d2b..7ee4f00 100755 --- a/python/cpr.py +++ b/python/cpr.py @@ -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]) diff --git a/python/modes_flightgear.py b/python/modes_flightgear.py index 9feb6b6..54a0551 100755 --- a/python/modes_flightgear.py +++ b/python/modes_flightgear.py @@ -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 diff --git a/python/modes_parse.py b/python/modes_parse.py index 16046ea..3fe6e25 100644 --- a/python/modes_parse.py +++ b/python/modes_parse.py @@ -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"], diff --git a/python/modes_print.py b/python/modes_print.py index d620d47..c755eef 100644 --- a/python/modes_print.py +++ b/python/modes_print.py @@ -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) diff --git a/python/modes_sbs1.py b/python/modes_sbs1.py index 0f97d36..ca38407 100644 --- a/python/modes_sbs1.py +++ b/python/modes_sbs1.py @@ -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 diff --git a/python/modes_sql.py b/python/modes_sql.py index 88d2033..2c42899 100644 --- a/python/modes_sql.py +++ b/python/modes_sql.py @@ -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 - - - -