Merge branch 'parse'
Conflicts: python/modes_sql.py
This commit is contained in:
commit
a39b07c372
@ -29,6 +29,7 @@ from string import split, join
|
||||
import air_modes
|
||||
import gnuradio.gr.gr_threading as _threading
|
||||
import csv
|
||||
from air_modes.modes_exceptions import *
|
||||
|
||||
class top_block_runner(_threading.Thread):
|
||||
def __init__(self, tb):
|
||||
@ -87,7 +88,7 @@ class adsb_rx_block (gr.top_block):
|
||||
|
||||
self.u.set_gain_mode(0) #manual gain mode
|
||||
if options.gain is None:
|
||||
options.gain = 49
|
||||
options.gain = 34
|
||||
|
||||
self.u.set_gain(options.gain)
|
||||
print "Gain is %i" % self.u.get_gain()
|
||||
@ -184,6 +185,12 @@ if __name__ == '__main__':
|
||||
outputs = [] #registry of plugin output functions
|
||||
updates = [] #registry of plugin update functions
|
||||
|
||||
if options.raw is True:
|
||||
rawport = air_modes.modes_raw_server()
|
||||
outputs.append(rawport.output)
|
||||
outputs.append(printraw)
|
||||
updates.append(rawport.add_pending_conns)
|
||||
|
||||
if options.kml is not None:
|
||||
#we spawn a thread to run every 30 seconds (or whatever) to generate KML
|
||||
kmlgen = air_modes.modes_kml(options.kml, my_position) #create a KML generating thread
|
||||
@ -197,12 +204,6 @@ if __name__ == '__main__':
|
||||
if options.no_print is not True:
|
||||
outputs.append(air_modes.modes_output_print(my_position).parse)
|
||||
|
||||
if options.raw is True:
|
||||
rawport = air_modes.modes_raw_server()
|
||||
outputs.append(rawport.output)
|
||||
outputs.append(printraw)
|
||||
updates.append(rawport.add_pending_conns)
|
||||
|
||||
if options.multiplayer is not None:
|
||||
[fghost, fgport] = options.multiplayer.split(':')
|
||||
fgout = air_modes.modes_flightgear(my_position, fghost, int(fgport))
|
||||
@ -225,7 +226,10 @@ if __name__ == '__main__':
|
||||
msg = queue.delete_head() #blocking read
|
||||
|
||||
for out in outputs:
|
||||
out(msg.to_string())
|
||||
try:
|
||||
out(msg.to_string())
|
||||
except ADSBError:
|
||||
pass
|
||||
|
||||
elif runner.done:
|
||||
raise KeyboardInterrupt
|
||||
|
@ -166,7 +166,7 @@ int air_modes_slicer::work(int noutput_items,
|
||||
}
|
||||
if(zeroes) {continue;} //toss it
|
||||
|
||||
rx_packet.message_type = (rx_packet.data[0] >> 3) & 0x1F; //get the message type for the parser to conveniently use, and to make decisions on ECC methods
|
||||
rx_packet.message_type = (rx_packet.data[0] >> 3) & 0x1F; //get the message type to make decisions on ECC methods
|
||||
|
||||
if(rx_packet.type == Short_Packet && rx_packet.message_type != 11 && rx_packet.numlowconf > 0) {continue;}
|
||||
if(rx_packet.message_type == 11 && rx_packet.numlowconf >= 10) {continue;}
|
||||
@ -178,34 +178,9 @@ int air_modes_slicer::work(int noutput_items,
|
||||
//crc for the other short packets is usually nonzero, so they can't really be trusted that far
|
||||
if(rx_packet.crc && (rx_packet.message_type == 11 || rx_packet.message_type == 17)) {continue;}
|
||||
|
||||
//we no longer attempt to brute force error correct via syndrome. it really only gets you 1% additional returns,
|
||||
//at the expense of a lot of CPU time and complexity
|
||||
|
||||
//we'll replicate some data by sending the message type as the first field, followed by the first 8+24=32 bits of the packet, followed by
|
||||
//56 long packet data bits if applicable (zero-padded if not), followed by crc
|
||||
|
||||
d_payload.str("");
|
||||
d_payload << std::dec << std::setw(2) << std::setfill('0') << rx_packet.message_type << std::hex << " ";
|
||||
for(int m = 0; m < 4; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
d_payload << " ";
|
||||
if(packet_length == 112) {
|
||||
for(int m = 4; m < 11; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
d_payload << " ";
|
||||
for(int m = 11; m < 14; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
} else {
|
||||
for(int m = 4; m < 11; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(0);
|
||||
}
|
||||
d_payload << " ";
|
||||
for(int m = 4; m < 7; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
for(int m = 0; m < packet_length/8; m++) {
|
||||
d_payload << std::hex << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
|
||||
d_payload << " " << std::setw(6) << rx_packet.crc << " " << std::dec << rx_packet.reference_level
|
||||
|
@ -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:
|
||||
@ -204,31 +197,29 @@ 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])
|
||||
@ -274,12 +265,14 @@ if __name__ == '__main__':
|
||||
|
||||
for i in range(0, rounds):
|
||||
decoder = cpr_decoder(None)
|
||||
ac_lat = random.uniform(-85, 85)
|
||||
ac_lon = random.uniform(-180,180)
|
||||
even_lat = random.uniform(-85, 85)
|
||||
even_lon = random.uniform(-180,180)
|
||||
odd_lat = even_lat + 1e-2
|
||||
odd_lon = min(even_lon + 1e-2, 180)
|
||||
|
||||
#encode that position
|
||||
(evenenclat, evenenclon) = cpr_encode(ac_lat, ac_lon, False, False)
|
||||
(oddenclat, oddenclon) = cpr_encode(ac_lat, ac_lon, True, False)
|
||||
(evenenclat, evenenclon) = cpr_encode(even_lat, even_lon, False, False)
|
||||
(oddenclat, oddenclon) = cpr_encode(odd_lat, odd_lon, True, False)
|
||||
|
||||
#perform a global decode
|
||||
icao = random.randint(0, 0xffffff)
|
||||
@ -300,17 +293,22 @@ if __name__ == '__main__':
|
||||
|
||||
#print "Lat: %f Lon: %f" % (ac_lat, ac_lon)
|
||||
|
||||
if abs(odddeclat - ac_lat) > threshold or abs(odddeclon - ac_lon) > threshold:
|
||||
#print "odddeclat: %f ac_lat: %f" % (odddeclat, ac_lat)
|
||||
#print "odddeclon: %f ac_lon: %f" % (odddeclon, ac_lon)
|
||||
if abs(odddeclat - odd_lat) > threshold or abs(odddeclon - odd_lon) > threshold:
|
||||
print "odddeclat: %f odd_lat: %f" % (odddeclat, odd_lat)
|
||||
print "odddeclon: %f odd_lon: %f" % (odddeclon, odd_lon)
|
||||
raise Exception("CPR test failure: global decode error greater than threshold")
|
||||
|
||||
nexteven_lat = odd_lat + 1e-2
|
||||
nexteven_lon = min(odd_lon + 1e-2, 180)
|
||||
|
||||
(nexteven_enclat, nexteven_enclon) = cpr_encode(nexteven_lat, nexteven_lon, False, False)
|
||||
|
||||
try:
|
||||
(evendeclat, evendeclon) = cpr_resolve_local([ac_lat, ac_lon], [evenenclat, evenenclon], False, False)
|
||||
(evendeclat, evendeclon) = cpr_resolve_local([even_lat, even_lon], [nexteven_enclat, nexteven_enclon], False, False)
|
||||
except CPRNoPositionError:
|
||||
raise Exception("CPR test failure: local decode failure to resolve")
|
||||
|
||||
if abs(evendeclat - ac_lat) > threshold or abs(evendeclon - ac_lon) > threshold:
|
||||
if abs(evendeclat - nexteven_lat) > threshold or abs(evendeclon - nexteven_lon) > threshold:
|
||||
raise Exception("CPR test failure: local decode error greater than threshold")
|
||||
|
||||
print "CPR test successful. There were %i boundary straddles over %i rounds." % (bs, rounds)
|
||||
|
@ -40,3 +40,8 @@ class CPRNoPositionError(ADSBError):
|
||||
|
||||
class CPRBoundaryStraddleError(CPRNoPositionError):
|
||||
pass
|
||||
|
||||
class FieldNotInPacket(ParserError):
|
||||
def __init__(self, item):
|
||||
self.item = item
|
||||
|
||||
|
@ -27,39 +27,39 @@ class modes_flightgear(modes_parse.modes_parse):
|
||||
self.sock.connect((self.hostname, self.port))
|
||||
|
||||
def output(self, message):
|
||||
[msgtype, shortdata, longdata, parity, ecc, reference, timestamp] = message.split()
|
||||
shortdata = long(shortdata, 16)
|
||||
longdata = long(longdata, 16)
|
||||
msgtype = int(msgtype)
|
||||
[data, ecc, reference, timestamp] = message.split()
|
||||
data = modes_parse.modes_reply(long(data, 16))
|
||||
|
||||
try:
|
||||
msgtype = data["df"]
|
||||
if msgtype == 17: #ADS-B report
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
subtype = (longdata >> 51) & 0x1F
|
||||
if subtype == 4: #ident packet
|
||||
(ident, actype) = self.parseBDS08(shortdata, longdata)
|
||||
icao24 = data["aa"]
|
||||
bdsreg = data["me"].get_type()
|
||||
if bdsreg == 0x08: #ident packet
|
||||
(ident, actype) = self.parseBDS08(data)
|
||||
#select model based on actype
|
||||
self.callsigns[icao24] = [ident, actype]
|
||||
|
||||
elif 5 <= subtype <= 8: #BDS0,6 pos
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(shortdata, longdata)
|
||||
elif bdsreg == 0x06: #BDS0,6 pos
|
||||
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
|
||||
self.positions[icao24] = [decoded_lat, decoded_lon, 0]
|
||||
self.update(icao24)
|
||||
|
||||
elif bdsreg == 0x05: #BDS0,5 pos
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(data)
|
||||
self.positions[icao24] = [decoded_lat, decoded_lon, altitude]
|
||||
self.update(icao24)
|
||||
|
||||
elif 9 <= subtype <= 18: #BDS0,5 pos
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(shortdata, longdata)
|
||||
self.positions[icao24] = [decoded_lat, decoded_lon, altitude]
|
||||
self.update(icao24)
|
||||
|
||||
elif subtype == 19: #velocity
|
||||
subsubtype = (longdata >> 48) & 0x07
|
||||
if subsubtype == 0:
|
||||
[velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(shortdata, longdata)
|
||||
elif subsubtype == 1:
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_1(shortdata, longdata)
|
||||
turnrate = 0
|
||||
elif bdsreg == 0x09: #velocity
|
||||
subtype = data["bds09"].get_type()
|
||||
if subtype == 0:
|
||||
[velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(data)
|
||||
elif subtype == 1:
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_1(data)
|
||||
turnrate = 0
|
||||
else:
|
||||
return
|
||||
|
||||
self.velocities[icao24] = [velocity, heading, vert_spd, turnrate]
|
||||
|
||||
except ADSBError:
|
||||
@ -73,7 +73,7 @@ class modes_flightgear(modes_parse.modes_parse):
|
||||
if complete:
|
||||
print "FG update: %s" % (self.callsigns[icao24][0])
|
||||
msg = fg_posmsg(self.callsigns[icao24][0],
|
||||
None,#TODO FIX should populate ac type; this isn't pressing bc it appears nobody populates this field
|
||||
self.callsigns[icao24][1],
|
||||
self.positions[icao24][0],
|
||||
self.positions[icao24][1],
|
||||
self.positions[icao24][2],
|
||||
@ -134,14 +134,14 @@ modelmap = { None: 'Aircraft/777-200/Models/777-200ER.xml'
|
||||
}
|
||||
|
||||
class fg_posmsg(fg_header):
|
||||
def __init__(self, callsign, model, lat, lon, alt, hdg, vel, vs, turnrate):
|
||||
def __init__(self, callsign, modelname, lat, lon, alt, hdg, vel, vs, turnrate):
|
||||
#from the above, calculate valid FGFS mp vals
|
||||
#this is the translation layer between ADS-B and FGFS
|
||||
fg_header.__init__(self)
|
||||
self.callsign = callsign
|
||||
if self.callsign is None:
|
||||
self.callsign = "UNKNOWN"
|
||||
self.modelname = model
|
||||
self.modelname = modelname
|
||||
if self.modelname not in modelmap:
|
||||
#this should keep people on their toes when strange aircraft types are seen
|
||||
self.model = 'Aircraft/santa/Models/santa.xml'
|
||||
|
@ -19,7 +19,6 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
|
||||
import time, os, sys
|
||||
from string import split, join
|
||||
from altitude import decode_alt
|
||||
@ -27,88 +26,226 @@ import cpr
|
||||
import math
|
||||
from modes_exceptions import *
|
||||
|
||||
#this implements a packet class which can retrieve its own fields.
|
||||
class data_field:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.fields = self.parse()
|
||||
|
||||
types = { }
|
||||
offset = 1 #field offset applied to all fields. used for offsetting
|
||||
#subtypes to reconcile with the spec. Really just for readability.
|
||||
|
||||
#get a particular field from the data
|
||||
def __getitem__(self, fieldname):
|
||||
mytype = self.get_type()
|
||||
if mytype in self.types:
|
||||
if fieldname in self.fields: #verify it exists in this packet type
|
||||
return self.fields[fieldname]
|
||||
else:
|
||||
raise FieldNotInPacket(fieldname)
|
||||
else:
|
||||
raise NoHandlerError(mytype)
|
||||
|
||||
#grab all the fields in the packet as a dict
|
||||
#done once on init so you don't have to iterate down every time you grab a field
|
||||
def parse(self):
|
||||
fields = {}
|
||||
mytype = self.get_type()
|
||||
if mytype in self.types:
|
||||
for field in self.types[mytype]:
|
||||
bits = self.types[self.get_type()][field]
|
||||
if len(bits) == 3:
|
||||
obj = bits[2](self.get_bits(bits[0], bits[1]))
|
||||
fields.update(obj.parse())
|
||||
fields.update({field: obj})
|
||||
else:
|
||||
fields.update({field: self.get_bits(bits[0], bits[1])})
|
||||
else:
|
||||
raise NoHandlerError(mytype)
|
||||
return fields
|
||||
|
||||
def get_type(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_numbits(self):
|
||||
raise NotImplementedError
|
||||
|
||||
#retrieve bits from data given the offset and number of bits.
|
||||
#the offset is both left-justified (LSB) and starts at 1, to
|
||||
#correspond to the Mode S spec. Blame them.
|
||||
def get_bits(self, *args):
|
||||
startbit = args[0]
|
||||
num = args[1]
|
||||
bits = 0
|
||||
try:
|
||||
bits = (self.data \
|
||||
>> (self.get_numbits() - startbit - num + self.offset)) \
|
||||
& ((1 << num) - 1)
|
||||
#the exception handler catches instances when you try to shift more than
|
||||
#the number of bits. this can happen when a garbage packet gets through
|
||||
#which reports itself as a short packet but of type long.
|
||||
#TODO: should find more productive way to throw this out
|
||||
except ValueError:
|
||||
pass
|
||||
#print "Short packet received for long packet type: %x" % self.data
|
||||
return bits
|
||||
|
||||
class bds09_reply(data_field):
|
||||
offset = 6
|
||||
types = { #BDS0,9 subtype 0
|
||||
0: {"sub": (6,3), "dew": (10,1), "vew": (11,11), "dns": (22,1),
|
||||
"vns": (23,11), "str": (34,1), "tr": (35,6), "dvr": (41,1),
|
||||
"vr": (42,9)},
|
||||
#BDS0,9 subtypes 1-2 (differ only in velocity encoding)
|
||||
1: {"sub": (6,3), "icf": (9,1), "ifr": (10,1), "nuc": (11,3),
|
||||
"dew": (14,1), "vew": (15,10), "dns": (25,1), "vns": (26,10),
|
||||
"vrsrc": (36,1), "dvr": (37,1), "vr": (38,9), "dhd": (49,1), "hd": (50,6)},
|
||||
#BDS0,9 subtype 3-4 (airspeed and heading)
|
||||
3: {"sub": (6,3), "icf": (9,1), "ifr": (10,1), "nuc": (11,3), "mhs": (14,1),
|
||||
"hdg": (15,10), "ast": (25,1), "spd": (26,10), "vrsrc": (36,1),
|
||||
"dvr": (37,1), "vr": (38,9), "dhd": (49,1), "hd": (50,6)}
|
||||
}
|
||||
|
||||
def get_type(self):
|
||||
sub = self.get_bits(6,3)
|
||||
if sub == 0:
|
||||
return 0
|
||||
if 1 <= sub <= 2:
|
||||
return 1
|
||||
if 3 <= sub <= 4:
|
||||
return 3
|
||||
|
||||
def get_numbits(self):
|
||||
return 51
|
||||
|
||||
#type 17 extended squitter data
|
||||
class me_reply(data_field):
|
||||
#types in this format are listed by BDS register
|
||||
#TODO: add comments explaining these fields
|
||||
types = { 0x05: {"ftc": (1,5), "ss": (6,2), "saf": (8,1), "alt": (9,12), "time": (21,1), "cpr": (22,1), "lat": (23,17), "lon": (40,17)}, #airborne position
|
||||
0x06: {"ftc": (1,5), "mvt": (6,7), "gts": (13,1), "gtk": (14,7), "time": (21,1), "cpr": (22,1), "lat": (23,17), "lon": (40,17)}, #surface position
|
||||
0x07: {"ftc": (1,5),}, #TODO extended squitter status
|
||||
0x08: {"ftc": (1,5), "cat": (6,3), "ident": (9,48)}, #extended squitter identification and type
|
||||
0x09: {"ftc": (1,5), "bds09": (6,51, bds09_reply)},
|
||||
#0x0A: data link capability report
|
||||
#0x17: common usage capability report
|
||||
#0x18-0x1F: Mode S specific services capability report
|
||||
#0x20: aircraft identification
|
||||
0x61: {"ftc": (1,5), "eps": (9,3)}
|
||||
}
|
||||
|
||||
#maps ftc to BDS register
|
||||
def get_type(self):
|
||||
ftc = self.get_bits(1,5)
|
||||
if 1 <= ftc <= 4:
|
||||
return 0x08
|
||||
elif 5 <= ftc <= 8:
|
||||
return 0x06
|
||||
elif 9 <= ftc <= 18 and ftc != 15: #FTC 15 does not appear to be valid
|
||||
return 0x05
|
||||
elif ftc == 19:
|
||||
return 0x09
|
||||
elif ftc == 28:
|
||||
return 0x61
|
||||
else:
|
||||
return NoHandlerError(ftc)
|
||||
|
||||
def get_numbits(self):
|
||||
return 56
|
||||
|
||||
#resolves the TCAS reply types from TTI info
|
||||
class tcas_reply(data_field):
|
||||
offset = 61
|
||||
types = { 0: {"tti": (61,2)}, #UNKNOWN
|
||||
1: {"tti": (61,2), "tid": (63,26)},
|
||||
2: {"tti": (61,2), "tida": (63,13), "tidr": (76,7), "tidb": (83,6)}
|
||||
}
|
||||
def get_type(self):
|
||||
return self.get_bits(61,2)
|
||||
|
||||
def get_numbits(self):
|
||||
return 28
|
||||
|
||||
#extended squitter types 20,21 MB subfield
|
||||
class mb_reply(data_field):
|
||||
offset = 33 #fields offset by 33 to match documentation
|
||||
#types are based on bds1 subfield
|
||||
types = { 0: {"bds1": (33,4), "bds2": (37,4)}, #TODO
|
||||
1: {"bds1": (33,4), "bds2": (37,4), "cfs": (41,4), "acs": (45,20), "bcs": (65,16), "ecs": (81,8)},
|
||||
2: {"bds1": (33,4), "bds2": (37,4), "ais": (41,48)},
|
||||
3: {"bds1": (33,4), "bds2": (37,4), "ara": (41,14), "rac": (55,4), "rat": (59,1),
|
||||
"mte": (60,1), "tcas": (61, 28, tcas_reply)}
|
||||
}
|
||||
|
||||
def get_type(self):
|
||||
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(bds1)
|
||||
return int(bds1)
|
||||
|
||||
def get_numbits(self):
|
||||
return 56
|
||||
|
||||
# #type MV (extended squitter type 16) subfields
|
||||
# mv_fields = { "ara": (41,14), "mte": (60,1), "rac": (55,4), "rat": (59,1),
|
||||
# "vds": (33,8), "vds1": (33,4), "vds2": (37,4)
|
||||
# }
|
||||
|
||||
#the whole Mode S packet type
|
||||
class modes_reply(data_field):
|
||||
types = { 0: {"df": (1,5), "vs": (6,1), "cc": (7,1), "sl": (9,3), "ri": (14,4), "ac": (20,13), "ap": (33,24)},
|
||||
4: {"df": (1,5), "fs": (6,3), "dr": (9,5), "um": (14,6), "ac": (20,13), "ap": (33,24)},
|
||||
5: {"df": (1,5), "fs": (6,3), "dr": (9,5), "um": (14,6), "id": (20,13), "ap": (33,24)},
|
||||
11: {"df": (1,5), "ca": (6,3), "aa": (9,24), "pi": (33,24)},
|
||||
16: {"df": (1,5), "vs": (6,1), "sl": (9,3), "ri": (14,4), "ac": (20,13), "mv": (33,56), "ap": (88,24)},
|
||||
17: {"df": (1,5), "ca": (6,3), "aa": (9,24), "me": (33,56, me_reply), "pi": (88,24)},
|
||||
20: {"df": (1,5), "fs": (6,3), "dr": (9,5), "um": (14,6), "ac": (20,13), "mb": (33,56, mb_reply), "ap": (88,24)},
|
||||
21: {"df": (1,5), "fs": (6,3), "dr": (9,5), "um": (14,6), "id": (20,13), "mb": (33,56, mb_reply), "ap": (88,24)},
|
||||
24: {"df": (1,5), "ke": (6,1), "nd": (7,4), "md": (11,80), "ap": (88,24)}
|
||||
}
|
||||
|
||||
def is_long(self):
|
||||
return self.data > (1 << 56)
|
||||
|
||||
def get_numbits(self):
|
||||
return 112 if self.is_long() else 56
|
||||
|
||||
def get_type(self):
|
||||
return self.get_bits(1,5)
|
||||
|
||||
class modes_parse:
|
||||
def __init__(self, mypos):
|
||||
self.my_location = mypos
|
||||
self.cpr = cpr.cpr_decoder(self.my_location)
|
||||
|
||||
def parse0(self, shortdata):
|
||||
vs = bool(shortdata >> 26 & 0x1) #ground sensor -- airborne when 0
|
||||
cc = bool(shortdata >> 25 & 0x1) #crosslink capability, binary
|
||||
sl = shortdata >> 21 & 0x07 #operating sensitivity of onboard TCAS system. 0 means no TCAS sensitivity reported, 1-7 give TCAS sensitivity
|
||||
ri = shortdata >> 15 & 0x0F #speed coding: 0 = no onboard TCAS, 1 = NA, 2 = TCAS w/inhib res, 3 = TCAS w/vert only, 4 = TCAS w/vert+horiz, 5-7 = NA, 8 = no max A/S avail,
|
||||
#9 = A/S <= 75kt, 10 = A/S (75-150]kt, 11 = (150-300]kt, 12 = (300-600]kt, 13 = (600-1200]kt, 14 = >1200kt, 15 = NA
|
||||
def parse0(self, data):
|
||||
altitude = decode_alt(data["ac"], True)
|
||||
return [data["vs"], data["cc"], data["sl"], data["ri"], altitude]
|
||||
|
||||
altitude = decode_alt(shortdata & 0x1FFF, True) #bit 13 is set for type 0
|
||||
def parse4(self, data):
|
||||
altitude = decode_alt(data["ac"], True)
|
||||
return [data["fs"], data["dr"], data["um"], altitude]
|
||||
|
||||
return [vs, cc, sl, ri, altitude]
|
||||
def parse5(self, data):
|
||||
return [data["fs"], data["dr"], data["um"], data["id"]]
|
||||
|
||||
def parse4(self, shortdata):
|
||||
fs = shortdata >> 24 & 0x07 #flight status: 0 is airborne normal, 1 is ground normal, 2 is airborne alert, 3 is ground alert, 4 is alert SPI, 5 is normal SPI
|
||||
dr = shortdata >> 19 & 0x1F #downlink request: 0 means no req, bit 0 is Comm-B msg rdy bit, bit 1 is TCAS info msg rdy, bit 2 is Comm-B bcast #1 msg rdy, bit2+bit0 is Comm-B bcast #2 msg rdy,
|
||||
#bit2+bit1 is TCAS info and Comm-B bcast #1 msg rdy, bit2+bit1+bit0 is TCAS info and Comm-B bcast #2 msg rdy, 8-15 N/A, 16-31 req to send N-15 segments
|
||||
um = shortdata >> 13 & 0x3F #transponder status readouts, no decoding information available
|
||||
|
||||
altitude = decode_alt(shortdata & 0x1FFF, True)
|
||||
|
||||
return [fs, dr, um, altitude]
|
||||
|
||||
|
||||
|
||||
def parse5(self, shortdata):
|
||||
fs = shortdata >> 24 & 0x07 #flight status: 0 is airborne normal, 1 is ground normal, 2 is airborne alert, 3 is ground alert, 4 is alert SPI, 5 is normal SPI
|
||||
dr = shortdata >> 19 & 0x1F #downlink request: 0 means no req, bit 0 is Comm-B msg rdy bit, bit 1 is TCAS info msg rdy, bit 2 is Comm-B bcast #1 msg rdy, bit2+bit0 is Comm-B bcast #2 msg rdy,
|
||||
#bit2+bit1 is TCAS info and Comm-B bcast #1 msg rdy, bit2+bit1+bit0 is TCAS info and Comm-B bcast #2 msg rdy, 8-15 N/A, 16-31 req to send N-15 segments
|
||||
um = shortdata >> 13 & 0x3F #transponder status readouts, no decoding information available
|
||||
|
||||
return [fs, dr, um]
|
||||
|
||||
def parse11(self, shortdata, ecc):
|
||||
def parse11(self, data, ecc):
|
||||
interrogator = ecc & 0x0F
|
||||
|
||||
ca = shortdata >> 13 & 0x3F #capability
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
|
||||
return [icao24, interrogator, ca]
|
||||
|
||||
#the Type 17 subtypes are:
|
||||
#0: No position information
|
||||
#1: Identification (Category set D)
|
||||
#2: Identification (Category set C)
|
||||
#3: "" (B)
|
||||
#4: "" (A)
|
||||
#5: Surface position accurate to 7.5m
|
||||
#6: "" to 25m
|
||||
#7: "" to 185.2m (0.1nm)
|
||||
#8: "" above 185.2m
|
||||
#9: Airborne position to 7.5m
|
||||
#10-18: Same with less accuracy
|
||||
#19: Airborne velocity
|
||||
#20: Airborne position w/GNSS height above earth
|
||||
#21: same to 25m
|
||||
#22: same above 25m
|
||||
#23: Reserved
|
||||
#24: Reserved for surface system status
|
||||
#25-27: Reserved
|
||||
#28: Extended squitter aircraft status
|
||||
#29: Current/next trajectory change point
|
||||
#30: Aircraft operational coordination
|
||||
#31: Aircraft operational status
|
||||
return [data["aa"], interrogator, data["ca"]]
|
||||
|
||||
categories = [["NO INFO", "RESERVED", "RESERVED", "RESERVED", "RESERVED", "RESERVED", "RESERVED", "RESERVED"],\
|
||||
["NO INFO", "SURFACE EMERGENCY VEHICLE", "SURFACE SERVICE VEHICLE", "FIXED OBSTRUCTION", "RESERVED", "RESERVED", "RESERVED"],\
|
||||
["NO INFO", "SURFACE EMERGENCY VEHICLE", "SURFACE SERVICE VEHICLE", "FIXED OBSTRUCTION", "CLUSTER OBSTRUCTION", "LINE OBSTRUCTION", "RESERVED"],\
|
||||
["NO INFO", "GLIDER", "BALLOON/BLIMP", "PARACHUTE", "ULTRALIGHT", "RESERVED", "UAV", "SPACECRAFT"],\
|
||||
["NO INFO", "LIGHT", "SMALL", "LARGE", "LARGE HIGH VORTEX", "HEAVY", "HIGH PERFORMANCE", "ROTORCRAFT"]]
|
||||
|
||||
def parseBDS08(self, shortdata, longdata):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
subtype = (longdata >> 51) & 0x1F
|
||||
category = (longdata >> 48) & 0x07
|
||||
catstring = self.categories[subtype-1][category]
|
||||
def parseBDS08(self, data):
|
||||
catstring = self.categories[data["ftc"]-1][data["cat"]]
|
||||
|
||||
msg = ""
|
||||
for i in range(0, 8):
|
||||
msg += self.charmap( longdata >> (42-6*i) & 0x3F)
|
||||
msg += self.charmap(data["ident"] >> (42-6*i) & 0x3F)
|
||||
return (msg, catstring)
|
||||
|
||||
def charmap(self, d):
|
||||
@ -123,16 +260,13 @@ class modes_parse:
|
||||
|
||||
return retval
|
||||
|
||||
def parseBDS05(self, shortdata, longdata):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
def parseBDS05(self, data):
|
||||
icao24 = data["aa"]
|
||||
|
||||
encoded_lon = longdata & 0x1FFFF
|
||||
encoded_lat = (longdata >> 17) & 0x1FFFF
|
||||
cpr_format = (longdata >> 34) & 1
|
||||
|
||||
enc_alt = (longdata >> 36) & 0x0FFF
|
||||
|
||||
altitude = decode_alt(enc_alt, False)
|
||||
encoded_lon = data["lon"]
|
||||
encoded_lat = data["lat"]
|
||||
cpr_format = data["cpr"]
|
||||
altitude = decode_alt(data["alt"], False)
|
||||
|
||||
[decoded_lat, decoded_lon, rnge, bearing] = self.cpr.decode(icao24, encoded_lat, encoded_lon, cpr_format, 0)
|
||||
|
||||
@ -140,35 +274,30 @@ class modes_parse:
|
||||
|
||||
|
||||
#welp turns out it looks like there's only 17 bits in the BDS0,6 ground packet after all.
|
||||
def parseBDS06(self, shortdata, longdata):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
|
||||
encoded_lon = longdata & 0x1FFFF
|
||||
encoded_lat = (longdata >> 17) & 0x1FFFF
|
||||
cpr_format = (longdata >> 34) & 1
|
||||
|
||||
altitude = 0
|
||||
def parseBDS06(self, data):
|
||||
icao24 = data["aa"]
|
||||
|
||||
encoded_lon = data["lon"]
|
||||
encoded_lat = data["lat"]
|
||||
cpr_format = data["cpr"]
|
||||
ground_track = data["gtk"] * 360. / 128
|
||||
[decoded_lat, decoded_lon, rnge, bearing] = self.cpr.decode(icao24, encoded_lat, encoded_lon, cpr_format, 1)
|
||||
return [ground_track, decoded_lat, decoded_lon, rnge, bearing]
|
||||
|
||||
return [altitude, decoded_lat, decoded_lon, rnge, bearing]
|
||||
|
||||
def parseBDS09_0(self, shortdata, longdata):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
vert_spd = ((longdata >> 6) & 0x1FF) * 32
|
||||
ud = bool((longdata >> 15) & 1)
|
||||
def parseBDS09_0(self, data):
|
||||
#0: ["sub", "dew", "vew", "dns", "vns", "str", "tr", "svr", "vr"],
|
||||
vert_spd = data["vr"] * 32
|
||||
ud = bool(data["dvr"])
|
||||
if ud:
|
||||
vert_spd = 0 - vert_spd
|
||||
turn_rate = (longdata >> 16) & 0x3F
|
||||
turn_rate = turn_rate * 15/62
|
||||
rl = bool((longdata >> 22) & 1)
|
||||
turn_rate = data["tr"] * 15/62
|
||||
rl = data["str"]
|
||||
if rl:
|
||||
turn_rate = 0 - turn_rate
|
||||
ns_vel = (longdata >> 23) & 0x7FF - 1
|
||||
ns = bool((longdata >> 34) & 1)
|
||||
ew_vel = (longdata >> 35) & 0x7FF - 1
|
||||
ew = bool((longdata >> 46) & 1)
|
||||
subtype = (longdata >> 48) & 0x07
|
||||
ns_vel = data["vns"] - 1
|
||||
ns = bool(data["dns"])
|
||||
ew_vel = data["vew"] - 1
|
||||
ew = bool(data["dew"])
|
||||
|
||||
velocity = math.hypot(ns_vel, ew_vel)
|
||||
if ew:
|
||||
@ -181,31 +310,25 @@ class modes_parse:
|
||||
|
||||
return [velocity, heading, vert_spd, turn_rate]
|
||||
|
||||
def parseBDS09_1(self, shortdata, longdata):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
alt_geo_diff = longdata & 0x7F - 1
|
||||
above_below = bool((longdata >> 7) & 1)
|
||||
def parseBDS09_1(self, data):
|
||||
#1: ["sub", "icf", "ifr", "nuc", "dew", "vew", "dns", "vns", "vrsrc", "dvr", "vr", "dhd", "hd"],
|
||||
alt_geo_diff = data["hd"] * 25
|
||||
above_below = bool(data["dhd"])
|
||||
if above_below:
|
||||
alt_geo_diff = 0 - alt_geo_diff;
|
||||
vert_spd = float((longdata >> 10) & 0x1FF - 1)
|
||||
ud = bool((longdata >> 19) & 1)
|
||||
vert_spd = float(data["vr"] - 1) * 64
|
||||
ud = bool(data["dvr"])
|
||||
if ud:
|
||||
vert_spd = 0 - vert_spd
|
||||
vert_src = bool((longdata >> 20) & 1)
|
||||
ns_vel = float((longdata >> 21) & 0x3FF - 1)
|
||||
ns = bool((longdata >> 31) & 1)
|
||||
ew_vel = float((longdata >> 32) & 0x3FF - 1)
|
||||
ew = bool((longdata >> 42) & 1)
|
||||
subtype = (longdata >> 48) & 0x07
|
||||
|
||||
|
||||
vert_src = bool(data["vrsrc"])
|
||||
ns_vel = float(data["vns"])
|
||||
ns = bool(data["dns"])
|
||||
ew_vel = float(data["vew"])
|
||||
ew = bool(data["dew"])
|
||||
subtype = data["sub"]
|
||||
if subtype == 0x02:
|
||||
ns_vel *= 4
|
||||
ew_vel *= 4
|
||||
|
||||
|
||||
vert_spd *= 64
|
||||
alt_geo_diff *= 25
|
||||
ns_vel <<= 2
|
||||
ew_vel <<= 2
|
||||
|
||||
velocity = math.hypot(ns_vel, ew_vel)
|
||||
if ew:
|
||||
@ -222,3 +345,62 @@ class modes_parse:
|
||||
|
||||
return [velocity, heading, vert_spd]
|
||||
|
||||
def parseBDS09_3(self, data):
|
||||
#3: {"sub", "icf", "ifr", "nuc", "mhs", "hdg", "ast", "spd", "vrsrc",
|
||||
# "dvr", "vr", "dhd", "hd"}
|
||||
mag_hdg = data["mhs"] * 360. / 1024
|
||||
vel_src = "TAS" if data["ast"] == 1 else "IAS"
|
||||
vel = data["spd"]
|
||||
if data["sub"] == 4:
|
||||
vel *= 4
|
||||
vert_spd = float(data["vr"] - 1) * 64
|
||||
if data["dvr"] == 1:
|
||||
vert_spd = 0 - vert_spd
|
||||
geo_diff = float(data["hd"] - 1) * 25
|
||||
return [mag_hdg, vel_src, vel, vert_spd, geo_diff]
|
||||
|
||||
|
||||
def parseBDS62(self, data):
|
||||
eps_strings = ["NO EMERGENCY", "GENERAL EMERGENCY", "LIFEGUARD/MEDICAL", "FUEL EMERGENCY",
|
||||
"NO COMMUNICATIONS", "UNLAWFUL INTERFERENCE", "RESERVED", "RESERVED"]
|
||||
return eps_strings[data["eps"]]
|
||||
|
||||
def parseMB_id(self, data): #bds1 == 2, bds2 == 0
|
||||
msg = ""
|
||||
for i in range(0, 8):
|
||||
msg += self.charmap( data["ais"] >> (42-6*i) & 0x3F)
|
||||
return (msg)
|
||||
|
||||
def parseMB_TCAS_resolutions(self, data):
|
||||
#these are LSB because the ICAO are asshats
|
||||
ara_bits = {41: "CLIMB", 42: "DON'T DESCEND", 43: "DON'T DESCEND >500FPM", 44: "DON'T DESCEND >1000FPM",
|
||||
45: "DON'T DESCEND >2000FPM", 46: "DESCEND", 47: "DON'T CLIMB", 48: "DON'T CLIMB >500FPM",
|
||||
49: "DON'T CLIMB >1000FPM", 50: "DON'T CLIMB >2000FPM", 51: "TURN LEFT", 52: "TURN RIGHT",
|
||||
53: "DON'T TURN LEFT", 54: "DON'T TURN RIGHT"}
|
||||
rac_bits = {55: "DON'T DESCEND", 56: "DON'T CLIMB", 57: "DON'T TURN LEFT", 58: "DON'T TURN RIGHT"}
|
||||
ara = data["ara"]
|
||||
#check to see which bits are set
|
||||
resolutions = ""
|
||||
for bit, name in ara_bits:
|
||||
if ara & (1 << (54-bit)):
|
||||
resolutions += " " + name
|
||||
complements = ""
|
||||
for bit, name in rac_bits:
|
||||
if rac & (1 << (58-bit)):
|
||||
complements += " " + name
|
||||
return (resolutions, complements)
|
||||
|
||||
#rat is 1 if resolution advisory terminated <18s ago
|
||||
#mte is 1 if multiple threats indicated
|
||||
#tti is threat type: 1 if ID, 2 if range/brg/alt
|
||||
#tida is threat altitude in Mode C format
|
||||
def parseMB_TCAS_threatid(self, data): #bds1==3, bds2==0, TTI==1
|
||||
#3: {"bds1": (33,4), "bds2": (37,4), "ara": (41,14), "rac": (55,4), "rat": (59,1),
|
||||
# "mte": (60,1), "tti": (61,2), "tida": (63,13), "tidr": (76,7), "tidb": (83,6)}
|
||||
(resolutions, complements) = self.parseMB_TCAS_resolutions(data)
|
||||
return (resolutions, complements, data["rat"], data["mte"], data["tid"])
|
||||
|
||||
def parseMB_TCAS_threatloc(self, data): #bds1==3, bds2==0, TTI==2
|
||||
(resolutions, complements) = self.parseMB_TCAS_resolutions(data)
|
||||
threat_alt = decode_alt(data["tida"], True)
|
||||
return (resolutions, complements, data["rat"], data["mte"], threat_alt, data["tidr"], data["tidb"])
|
||||
|
@ -30,16 +30,12 @@ class modes_output_print(modes_parse.modes_parse):
|
||||
modes_parse.modes_parse.__init__(self, mypos)
|
||||
|
||||
def parse(self, message):
|
||||
[msgtype, shortdata, longdata, parity, ecc, reference, timestamp] = message.split()
|
||||
shortdata = long(shortdata, 16)
|
||||
longdata = long(longdata, 16)
|
||||
parity = long(parity, 16)
|
||||
[data, ecc, reference, timestamp] = message.split()
|
||||
|
||||
ecc = long(ecc, 16)
|
||||
reference = float(reference)
|
||||
timestamp = float(timestamp)
|
||||
|
||||
msgtype = int(msgtype)
|
||||
|
||||
#TODO this is suspect
|
||||
if reference == 0.0:
|
||||
refdb = -150.0
|
||||
@ -48,19 +44,25 @@ class modes_output_print(modes_parse.modes_parse):
|
||||
output = "(%.0f %.10f) " % (refdb, timestamp);
|
||||
|
||||
try:
|
||||
data = modes_parse.modes_reply(long(data, 16))
|
||||
msgtype = data["df"]
|
||||
if msgtype == 0:
|
||||
output += self.print0(shortdata, ecc)
|
||||
output += self.print0(data, ecc)
|
||||
elif msgtype == 4:
|
||||
output += self.print4(shortdata, ecc)
|
||||
output += self.print4(data, ecc)
|
||||
elif msgtype == 5:
|
||||
output += self.print5(shortdata, ecc)
|
||||
output += self.print5(data, ecc)
|
||||
elif msgtype == 11:
|
||||
output += self.print11(shortdata, ecc)
|
||||
output += self.print11(data, ecc)
|
||||
elif msgtype == 17:
|
||||
output += self.print17(shortdata, longdata)
|
||||
output += self.print17(data)
|
||||
elif msgtype == 20 or msgtype == 21:
|
||||
output += self.print20(data, ecc)
|
||||
else:
|
||||
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 " + str(e.msgtype) + " from %x" % ecc
|
||||
output += "No handler for message type %s from %x" % (e.msgtype, ecc)
|
||||
print output
|
||||
except MetricAltError:
|
||||
pass
|
||||
@ -70,15 +72,22 @@ class modes_output_print(modes_parse.modes_parse):
|
||||
def print0(self, shortdata, ecc):
|
||||
[vs, cc, sl, ri, altitude] = self.parse0(shortdata)
|
||||
|
||||
retstr = "Type 0 (short A-A surveillance) from " + "%x" % ecc + " at " + str(altitude) + "ft"
|
||||
# the ri values below 9 are used for other things. might want to print those someday.
|
||||
if ri == 9:
|
||||
retstr = retstr + " (speed <75kt)"
|
||||
retstr = "Type 0 (short A-A surveillance) from %x at %ift" % (ecc, altitude)
|
||||
if ri == 0:
|
||||
retstr += " (No TCAS)"
|
||||
elif ri == 2:
|
||||
retstr += " (TCAS resolution inhibited)"
|
||||
elif ri == 3:
|
||||
retstr += " (Vertical TCAS resolution only)"
|
||||
elif ri == 4:
|
||||
retstr += " (Full TCAS resolution)"
|
||||
elif ri == 9:
|
||||
retstr += " (speed <75kt)"
|
||||
elif ri > 9:
|
||||
retstr = retstr + " (speed " + str(75 * (1 << (ri-10))) + "-" + str(75 * (1 << (ri-9))) + "kt)"
|
||||
retstr += " (speed %i-%ikt)" % (75 * (1 << (ri-10)), 75 * (1 << (ri-9)))
|
||||
|
||||
if vs is True:
|
||||
retstr = retstr + " (aircraft is on the ground)"
|
||||
retstr += " (aircraft is on the ground)"
|
||||
|
||||
return retstr
|
||||
|
||||
@ -86,83 +95,133 @@ class modes_output_print(modes_parse.modes_parse):
|
||||
|
||||
[fs, dr, um, altitude] = self.parse4(shortdata)
|
||||
|
||||
retstr = "Type 4 (short surveillance altitude reply) from " + "%x" % ecc + " at " + str(altitude) + "ft"
|
||||
retstr = "Type 4 (short surveillance altitude reply) from %x at %ift" % (ecc, altitude)
|
||||
|
||||
if fs == 1:
|
||||
retstr = retstr + " (aircraft is on the ground)"
|
||||
retstr += " (aircraft is on the ground)"
|
||||
elif fs == 2:
|
||||
retstr = retstr + " (AIRBORNE ALERT)"
|
||||
retstr += " (AIRBORNE ALERT)"
|
||||
elif fs == 3:
|
||||
retstr = retstr + " (GROUND ALERT)"
|
||||
retstr += " (GROUND ALERT)"
|
||||
elif fs == 4:
|
||||
retstr = retstr + " (SPI ALERT)"
|
||||
retstr += " (SPI ALERT)"
|
||||
elif fs == 5:
|
||||
retstr = retstr + " (SPI)"
|
||||
retstr += " (SPI)"
|
||||
|
||||
return retstr
|
||||
|
||||
def print5(self, shortdata, ecc):
|
||||
[fs, dr, um] = self.parse5(shortdata)
|
||||
[fs, dr, um, ident] = self.parse5(shortdata)
|
||||
|
||||
retstr = "Type 5 (short surveillance ident reply) from " + "%x" % ecc + " with ident " + str(shortdata & 0x1FFF)
|
||||
retstr = "Type 5 (short surveillance ident reply) from %x with ident %i" % (ecc, ident)
|
||||
|
||||
if fs == 1:
|
||||
retstr = retstr + " (aircraft is on the ground)"
|
||||
retstr += " (aircraft is on the ground)"
|
||||
elif fs == 2:
|
||||
retstr = retstr + " (AIRBORNE ALERT)"
|
||||
retstr += " (AIRBORNE ALERT)"
|
||||
elif fs == 3:
|
||||
retstr = retstr + " (GROUND ALERT)"
|
||||
retstr += " (GROUND ALERT)"
|
||||
elif fs == 4:
|
||||
retstr = retstr + " (SPI ALERT)"
|
||||
retstr += " (SPI ALERT)"
|
||||
elif fs == 5:
|
||||
retstr = retstr + " (SPI)"
|
||||
retstr += " (SPI)"
|
||||
|
||||
return retstr
|
||||
|
||||
def print11(self, shortdata, ecc):
|
||||
[icao24, interrogator, ca] = self.parse11(shortdata, ecc)
|
||||
|
||||
retstr = "Type 11 (all call reply) from " + "%x" % icao24 + " in reply to interrogator " + str(interrogator)
|
||||
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 with capability level %i" % (icao24, interrogator, ca+1)
|
||||
return retstr
|
||||
|
||||
def print17(self, shortdata, longdata):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
subtype = (longdata >> 51) & 0x1F;
|
||||
def print17(self, data):
|
||||
icao24 = data["aa"]
|
||||
bdsreg = data["me"].get_type()
|
||||
|
||||
retstr = None
|
||||
|
||||
if 1 <= subtype <= 4:
|
||||
(msg, typestring) = self.parseBDS08(shortdata, longdata)
|
||||
retstr = "Type 17 subtype 04 (ident) from " + "%x" % icao24 + " of type " + typestring + " with ident " + msg
|
||||
if bdsreg == 0x08:
|
||||
(msg, typestring) = self.parseBDS08(data)
|
||||
retstr = "Type 17 BDS0,8 (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(shortdata, longdata)
|
||||
if decoded_lat is not None:
|
||||
retstr = "Type 17 subtype 06 (surface report) from " + "%x" % icao24 + " at (" + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
if rnge is not None and bearing is not None:
|
||||
retstr += " (" + "%.2f" % rnge + " @ " + "%.0f" % bearing + ")"
|
||||
elif bdsreg == 0x06:
|
||||
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data)
|
||||
retstr = "Type 17 BDS0,6 (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)
|
||||
|
||||
elif subtype >= 9 and subtype <= 18:
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(shortdata, longdata)
|
||||
if decoded_lat is not None:
|
||||
retstr = "Type 17 subtype 05 (position report) from " + "%x" % icao24 + " at (" + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
if rnge is not None and bearing is not None:
|
||||
retstr += " (" + "%.2f" % rnge + " @ " + "%.0f" % bearing + ")"
|
||||
retstr += " at " + str(altitude) + "ft"
|
||||
elif bdsreg == 0x05:
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(data)
|
||||
retstr = "Type 17 BDS0,5 (position report) from %x at (%.6f, %.6f)" % (icao24, decoded_lat, decoded_lon)
|
||||
if rnge is not None and bearing is not None:
|
||||
retstr += " (" + "%.2f" % rnge + " @ " + "%.0f" % bearing + ")"
|
||||
retstr += " at " + str(altitude) + "ft"
|
||||
|
||||
elif subtype == 19:
|
||||
subsubtype = (longdata >> 48) & 0x07
|
||||
if subsubtype == 0:
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_0(shortdata, longdata)
|
||||
retstr = "Type 17 subtype 09-0 (track report) from " + "%x" % icao24 + " with velocity " + "%.0f" % velocity + "kt heading " + "%.0f" % heading + " VS " + "%.0f" % vert_spd
|
||||
|
||||
elif subsubtype == 1:
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_1(shortdata, longdata)
|
||||
retstr = "Type 17 subtype 09-1 (track report) from " + "%x" % icao24 + " with velocity " + "%.0f" % velocity + "kt heading " + "%.0f" % heading + " VS " + "%.0f" % vert_spd
|
||||
elif bdsreg == 0x09:
|
||||
subtype = data["bds09"].get_type()
|
||||
if subtype == 0:
|
||||
[velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(data)
|
||||
retstr = "Type 17 BDS0,9-%i (track report) from %x with velocity %.0fkt heading %.0f VS %.0f turn rate %.0f" \
|
||||
% (subtype, icao24, velocity, heading, vert_spd, turnrate)
|
||||
elif subtype == 1:
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_1(data)
|
||||
retstr = "Type 17 BDS0,9-%i (track report) from %x with velocity %.0fkt heading %.0f VS %.0f" % (subtype, icao24, velocity, heading, vert_spd)
|
||||
elif subtype == 3:
|
||||
[mag_hdg, vel_src, vel, vert_spd, geo_diff] = self.parseBDS09_3(data)
|
||||
retstr = "Type 17 BDS0,9-%i (air course report) from %x with %s %.0fkt magnetic heading %.0f VS %.0f geo. diff. from baro. alt. %.0fft" \
|
||||
% (subtype, icao24, vel_src, vel, mag_hdg, vert_spd, geo_diff)
|
||||
|
||||
else:
|
||||
retstr = "Type 17 subtype 09-%i" % (subsubtype) + " not implemented"
|
||||
retstr = "Type 17 BDS0,9-%i from %x not implemented" % (subtype, icao24)
|
||||
|
||||
elif bdsreg == 0x62:
|
||||
emerg_str = self.parseBDS62(data)
|
||||
retstr = "Type 17 BDS6,2 (emergency) from %x type %s" % (icao24, emerg_str)
|
||||
|
||||
else:
|
||||
retstr = "Type 17 subtype " + str(subtype) + " not implemented"
|
||||
retstr = "Type 17 subtype %i from %x not implemented" % (subtype, icao24)
|
||||
|
||||
return retstr
|
||||
|
||||
def print20(self, data, ecc):
|
||||
msgtype = data["df"]
|
||||
if(msgtype == 20):
|
||||
[fs, dr, um, alt] = self.parse4(data)
|
||||
else:
|
||||
[fs, dr, um, ident] = self.parse5(data)
|
||||
bds1 = data["bds1"]
|
||||
bds2 = data["bds2"]
|
||||
|
||||
if bds2 != 0:
|
||||
retstr = "No handler for BDS2 == %i from %x" % (bds2, ecc)
|
||||
|
||||
elif bds1 == 0:
|
||||
retstr = "No handler for BDS1 == 0 from %x" % ecc
|
||||
elif bds1 == 1:
|
||||
retstr = "Type 20 link capability report from %x: ACS: 0x%x, BCS: 0x%x, ECS: 0x%x, continues %i" \
|
||||
% (ecc, data["acs"], data["bcs"], data["ecs"], data["cfs"])
|
||||
elif bds1 == 2:
|
||||
retstr = "Type 20 identification from %x with text %s" % (ecc, self.parseMB_id(data))
|
||||
elif bds2 == 3:
|
||||
retstr = "Type 20 TCAS report from %x: " % ecc
|
||||
tti = data["tti"]
|
||||
if tti == 1:
|
||||
(resolutions, complements, rat, mte, threat_id) = self.parseMB_TCAS_threatid(data)
|
||||
retstr += "threat ID: %x advised: %s complement: %s" % (threat_id, resolutions, complements)
|
||||
elif tti == 2:
|
||||
(resolutions, complements, rat, mte, threat_alt, threat_range, threat_bearing) = self.parseMB_TCAS_threatloc(data)
|
||||
retstr += "range: %i bearing: %i alt: %i advised: %s complement: %s" % (threat_range, threat_bearing, threat_alt, resolutions, complements)
|
||||
else:
|
||||
retstr += " (no handler for TTI=%i)" % tti
|
||||
if mte == 1:
|
||||
retstr += " (multiple threats)"
|
||||
if rat == 1:
|
||||
retstr += " (resolved)"
|
||||
else:
|
||||
retstr = "No handler for BDS1 == %i from %x" % (bds1, ecc)
|
||||
|
||||
# if(msgtype == 20):
|
||||
# retstr += " at %ift" % altitude
|
||||
# else:
|
||||
# retstr += " ident %x" % ident
|
||||
|
||||
return retstr
|
||||
|
@ -104,25 +104,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
|
||||
@ -148,7 +146,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"
|
||||
@ -159,50 +157,46 @@ 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
|
||||
bdsreg = data["me"].get_type()
|
||||
|
||||
retstr = None
|
||||
#we'll get better timestamps later, hopefully with actual VRT time
|
||||
#in them
|
||||
[datestr, timestr] = self.current_time()
|
||||
|
||||
if subtype >= 1 and subtype <= 4:
|
||||
if bdsreg == 0x08:
|
||||
# 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:
|
||||
elif bdsreg == 0x06:
|
||||
# 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 bdsreg == 0x05:
|
||||
# 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:
|
||||
retstr = "MSG,3,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 == 19:
|
||||
elif bdsreg == 0x09:
|
||||
# Airborne velocity measurements
|
||||
# WRONG (heading, vert_spd), Is this still true?
|
||||
subsubtype = (longdata >> 48) & 0x07
|
||||
if subsubtype == 0:
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_0(shortdata, longdata)
|
||||
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)
|
||||
subtype = data["bds09"].get_type()
|
||||
if subtype == 0 or subtype == 1:
|
||||
parser = self.parseBDS09_0 if subtype == 0 else self.parseBDS09_1
|
||||
[velocity, heading, vert_spd] = parser(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
|
||||
|
@ -73,59 +73,54 @@ 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"]
|
||||
bdsreg = data["me"].get_type()
|
||||
|
||||
retstr = None
|
||||
|
||||
if subtype == 4:
|
||||
(msg, typename) = self.parseBDS08(shortdata, longdata)
|
||||
if bdsreg == 0x08:
|
||||
(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)
|
||||
elif bdsreg == 0x06:
|
||||
[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)
|
||||
elif bdsreg == 0x05:
|
||||
[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
|
||||
if subsubtype == 0:
|
||||
[velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(shortdata, longdata)
|
||||
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)
|
||||
retstr = "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")";
|
||||
elif bdsreg == 0x09:
|
||||
subtype = data["bds09"].get_type()
|
||||
if subtype == 0:
|
||||
[velocity, heading, vert_spd, turnrate] = 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 subtype == 1:
|
||||
[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 + ")"
|
||||
else:
|
||||
retstr = None
|
||||
|
||||
return retstr
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user