diff --git a/apps/mlat_server b/apps/mlat_server index 980c36a..b233e39 100755 --- a/apps/mlat_server +++ b/apps/mlat_server @@ -43,29 +43,6 @@ import bisect pickle_prot = 0 #pickle_prot = pickle.HIGHEST_PROTOCOL -class stamp: - def __init__(self, clientinfo, secs, frac_secs): - self.clientinfo = clientinfo - self.secs = secs - self.frac_secs = frac_secs - def __lt__(self, other): - if self.secs == other.secs: - return self.frac_secs < other.frac_secs - else: - return self.secs < other.secs - def __gt__(self, other): - if self.secs == other.secs: - return self.frac_secs > other.frac_secs - else: - return self.secs > other.secs - def __eq__(self, other): - return self.secs == other.secs and self.frac_secs == other.frac_secs - def __ne__(self, other): - return self.secs != other.secs or self.frac_secs != other.frac_secs - #good to within ms for comparison - def tofloat(self): - return self.secs + self.frac_secs - def ordered_insert(a, item): a.insert(bisect.bisect_right(a, item), item) diff --git a/apps/modes_rx b/apps/modes_rx index 9653c6f..8488db8 100755 --- a/apps/modes_rx +++ b/apps/modes_rx @@ -27,6 +27,8 @@ from optparse import OptionParser import time, os, sys, threading from string import split, join import air_modes +from air_modes.types import modes_report, stamp +from air_modes.parse import modes_reply import gnuradio.gr.gr_threading as _threading import csv from air_modes.exceptions import * @@ -240,9 +242,12 @@ if __name__ == '__main__': if not queue.empty_p() : while not queue.empty_p() : msg = queue.delete_head() #blocking read + [data, ecc, reference, timestamp_int, timestamp_frac] = msg.to_string().split() + #error handling? creating a modes_reply can throw NoHandlerError, but you really want that to be handled by the output plugin in question. + msg_tuple = modes_report(modes_reply(long(data, 16)), long(ecc, 16), float(reference), stamp(int(timestamp_int), float(timestamp_frac))) for out in outputs: try: - out(msg.to_string()) + out(msg_tuple) except air_modes.ADSBError: pass diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 3ab4578..976b551 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -46,6 +46,7 @@ GR_PYTHON_INSTALL( rx_path.py sbs1.py sql.py + types.py Quaternion.py DESTINATION ${GR_PYTHON_DIR}/air_modes ) diff --git a/python/__init__.py b/python/__init__.py index 5bc1f9d..75ed159 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -61,6 +61,7 @@ from raw_server import raw_server from mlat_client import mlat_client from exceptions import * from az_map import * +from types import * #this is try/excepted in case the user doesn't have numpy installed try: from flightgear import output_flightgear diff --git a/python/flightgear.py b/python/flightgear.py index 548e76e..8c122fd 100755 --- a/python/flightgear.py +++ b/python/flightgear.py @@ -27,37 +27,34 @@ class output_flightgear(air_modes.parse): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.connect((self.hostname, self.port)) - def output(self, message): - [data, ecc, reference, timestamp_int, timestamp_frac] = message.split() - timestamp = int(timestamp_int) + float(timestamp_frac) - data = air_modes.modes_reply(long(data, 16)) + def output(self, msg): try: - msgtype = data["df"] + msgtype = msg.data["df"] if msgtype == 17: #ADS-B report - icao24 = data["aa"] - bdsreg = data["me"].get_type() + icao24 = msg.data["aa"] + bdsreg = msg.data["me"].get_type() if bdsreg == 0x08: #ident packet - (ident, actype) = self.parseBDS08(data) + (ident, actype) = self.parseBDS08(msg.data) #select model based on actype self.callsigns[icao24] = [ident, actype] elif bdsreg == 0x06: #BDS0,6 pos - [ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data) + [ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(msg.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) + [altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(msg.data) self.positions[icao24] = [decoded_lat, decoded_lon, altitude] self.update(icao24) elif bdsreg == 0x09: #velocity subtype = data["bds09"].get_type() if subtype == 0: - [velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(data) + [velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(msg.data) elif subtype == 1: - [velocity, heading, vert_spd] = self.parseBDS09_1(data) + [velocity, heading, vert_spd] = self.parseBDS09_1(msg.data) turnrate = 0 else: return diff --git a/python/msprint.py b/python/msprint.py index d43f8f2..29d5aaf 100644 --- a/python/msprint.py +++ b/python/msprint.py @@ -29,39 +29,33 @@ class output_print(air_modes.parse): def __init__(self, mypos): air_modes.parse.__init__(self, mypos) - def parse(self, message): - [data, ecc, reference, timestamp_int, timestamp_frac] = message.split() - timestamp = int(timestamp_int) + float(timestamp_frac) + def parse(self, msg): - ecc = long(ecc, 16) - reference = float(reference) - - if reference == 0.0: + if msg.reference == 0.0: refdb = -150.0 else: - refdb = 20.0*math.log10(reference) - output = "(%.0f %.10f) " % (refdb, timestamp); + refdb = 20.0*math.log10(msg.reference) + output = "(%.0f %.10f) " % (refdb, float(msg.timestamp)); try: - data = air_modes.modes_reply(long(data, 16)) - msgtype = data["df"] + msgtype = msg.data["df"] if msgtype == 0: - output += self.print0(data, ecc) + output += self.print0(msg.data, msg.ecc) elif msgtype == 4: - output += self.print4(data, ecc) + output += self.print4(msg.data, msg.ecc) elif msgtype == 5: - output += self.print5(data, ecc) + output += self.print5(msg.data, msg.ecc) elif msgtype == 11: - output += self.print11(data, ecc) + output += self.print11(msg.data, msg.ecc) elif msgtype == 17: - output += self.print17(data) + output += self.print17(msg.data) elif msgtype == 20 or msgtype == 21 or msgtype == 16: - output += self.printTCAS(data, ecc) + output += self.printTCAS(msg.data, msg.ecc) else: - output += "No handler for message type %i from %x (but it's in modes_parse)" % (msgtype, ecc) + output += "No handler for message type %i from %x (but it's in modes_parse)" % (msgtype, msg.ecc) return output except NoHandlerError as e: - output += "No handler for message type %s from %x" % (e.msgtype, ecc) + output += "No handler for message type %s from %x" % (e.msgtype, msg.ecc) return output except MetricAltError: pass diff --git a/python/sbs1.py b/python/sbs1.py index 0015970..0be4571 100644 --- a/python/sbs1.py +++ b/python/sbs1.py @@ -100,27 +100,21 @@ class output_sbs1(air_modes.parse): else: return ",,," - def parse(self, message): + def parse(self, msg): #assembles a SBS-1-style output string from the received message - - [data, ecc, reference, timestamp_int, timestamp_frac] = message.split() - timestamp = int(timestamp_int) + float(timestamp_frac) - - data = air_modes.modes_reply(long(data, 16)) - ecc = long(ecc, 16) - msgtype = data["df"] + msgtype = msg.data["df"] outmsg = None if msgtype == 0: - outmsg = self.pp0(data, ecc) + outmsg = self.pp0(msg.data, msg.ecc) elif msgtype == 4: - outmsg = self.pp4(data, ecc) + outmsg = self.pp4(msg.data, msg.ecc) elif msgtype == 5: - outmsg = self.pp5(data, ecc) + outmsg = self.pp5(msg.data, msg.ecc) elif msgtype == 11: - outmsg = self.pp11(data, ecc) + outmsg = self.pp11(msg.data, msg.ecc) elif msgtype == 17: - outmsg = self.pp17(data) + outmsg = self.pp17(msg.data) else: raise NoHandlerError(msgtype) return outmsg diff --git a/python/sql.py b/python/sql.py index 6b0aeb2..d377bd9 100644 --- a/python/sql.py +++ b/python/sql.py @@ -86,21 +86,13 @@ class output_sql(air_modes.parse): except ADSBError: pass - def make_insert_query(self, message): + def make_insert_query(self, msg): #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 - [data, ecc, reference, timestamp_int, timestamp_frac] = message.split() - timestamp = int(timestamp_int) + float(timestamp_frac) - - data = air_modes.modes_reply(long(data, 16)) - ecc = long(ecc, 16) -# reference = float(reference) - - query = None - msgtype = data["df"] + msgtype = msg.data["df"] if msgtype == 17: - query = self.sql17(data) + query = self.sql17(msg.data) return query diff --git a/python/types.py b/python/types.py new file mode 100644 index 0000000..810ec0c --- /dev/null +++ b/python/types.py @@ -0,0 +1,62 @@ +# +# Copyright 2013 Nick Foster +# +# This file is part of gr-air-modes +# +# gr-air-modes is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# gr-air-modes is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with gr-air-modes; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from collections import namedtuple + +#this is a timestamp that preserves precision when used with UTC timestamps +#ordinary double-precision timestamp would lose significant fractional precision +#when the mantissa is as large as necessary for UTC timestamps. +class stamp: + def __init__(self, secs, frac_secs): + self.secs = secs + self.frac_secs = frac_secs + def __lt__(self, other): + if self.secs == other.secs: + return self.frac_secs < other.frac_secs + else: + return self.secs < other.secs + def __gt__(self, other): + if self.secs == other.secs: + return self.frac_secs > other.frac_secs + else: + return self.secs > other.secs + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.secs == other.secs and self.frac_secs == other.frac_secs + elif isinstance(other, float): + return float(self) == other + else: + raise TypeError + def __ne__(self, other): + return self.secs != other.secs or self.frac_secs != other.frac_secs + def __le__(self, other): + return (self == other) or (self < other) + def __ge__(self, other): + return (self == other) or (self > other) + + #to ensure we don't hash by stamp + __hash__ = None + + #good to within ms for comparison + def __float__(self): + return self.secs + self.frac_secs + +modes_report = namedtuple('modes_report', ['data', 'ecc', 'reference', 'timestamp'])