diff --git a/README.md b/README.md index 4c7e96e..e5a570a 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,10 @@ The task server must be running for `db.updateddb`. - [x] Document/Improve cli commands - [ ] Separate settings from module (currently at ogn/command/dbutils.py) - [ ] Enable granular data acquisition (eg. store receiver beacons only) -- [ ] Database: Rename 'Flarm' to 'Device'? - [ ] Future Database-Migrations: Use Alembric? + - [ ] Rename 'Flarm' to 'Device'? + - [ ] Rename self.heared\_aircraft\_IDs (lowercase) in aircraft\_beacon + - [ ] Rename self.heared\_aircraft\_IDs - [ ] Fix command/logbook.py (@Meisterschueler?) - [ ] Introduce scheduled tasks with 'celery beat' (eg. updateddb) diff --git a/ogn/aprs_parser.py b/ogn/aprs_parser.py index 3cdea88..7db6d47 100644 --- a/ogn/aprs_parser.py +++ b/ogn/aprs_parser.py @@ -1,16 +1,16 @@ from .model import Beacon, AircraftBeacon, ReceiverBeacon +from ogn.exceptions import AprsParseError - -def parse_aprs(text): - if not isinstance(text, str): - raise Exception("Unknown type: %s" % type(text)) - elif text == "": - raise Exception("String is empty") - elif text[0] == "#": +def parse_aprs(packet): + if not isinstance(packet, str): + raise TypeError("Expected packet to be str, got %s" % type(packet)) + elif packet == "": + raise AprsParseError(substring=packet, expected_type="non-empty aprs packet") + elif packet[0] == "#": return None beacon = Beacon() - beacon.parse(text) + beacon.parse(packet) # symboltable / symbolcodes used by OGN: # I&: used as receiver diff --git a/ogn/aprs_utils.py b/ogn/aprs_utils.py index 6444043..b026aae 100644 --- a/ogn/aprs_utils.py +++ b/ogn/aprs_utils.py @@ -6,16 +6,16 @@ kmh2kts = 0.539957 feet2m = 0.3048 ms2fpm = 196.85 -kts2kmh = 1/kmh2kts -m2feet = 1/feet2m -fpm2ms = 1/ms2fpm +kts2kmh = 1 / kmh2kts +m2feet = 1 / feet2m +fpm2ms = 1 / ms2fpm def dmsToDeg(dms): absDms = abs(dms) d = math.floor(absDms) m = (absDms - d) * 100 / 60 - return (d + m) + return d + m def createTimestamp(hhmmss, reference): @@ -31,6 +31,6 @@ def createTimestamp(hhmmss, reference): reference = reference + timedelta(days=1) elif (reference.hour == 0) & (hh == 23): reference = reference - timedelta(days=1) - elif (abs(reference.hour - hh) > 1): + elif abs(reference.hour - hh) > 1: raise Exception("Time difference is too big. Reference time:%s - timestamp:%s" % (reference, hhmmss)) return datetime(reference.year, reference.month, reference.day, hh, mm, ss) diff --git a/ogn/collect/celery.py b/ogn/collect/celery.py index f6892dd..bc89364 100644 --- a/ogn/collect/celery.py +++ b/ogn/collect/celery.py @@ -3,7 +3,7 @@ from __future__ import absolute_import from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from celery import Celery, Task +from celery import Celery from celery.signals import worker_init, worker_shutdown app = Celery('ogn.collect', @@ -13,6 +13,7 @@ app = Celery('ogn.collect', DB_URI = 'sqlite:///beacons.db' + @worker_init.connect def connect_db(signal, sender): # Load settings like DB_URI... @@ -21,6 +22,7 @@ def connect_db(signal, sender): Session = sessionmaker(bind=engine) sender.app.session = Session() + @worker_shutdown.connect def close_db(signal, sender): sender.app.session.close() diff --git a/ogn/collect/fetchddb.py b/ogn/collect/fetchddb.py index 65d6728..1e3e354 100644 --- a/ogn/collect/fetchddb.py +++ b/ogn/collect/fetchddb.py @@ -16,14 +16,21 @@ def update_ddb_data(): app.session.query(Flarm).delete() devices = get_ddb() - logger.info("Devices: %s"%str(devices)) - app.session.bulk_save_objects(devices) + logger.debug("New Devices: %s" % str(devices)) + app.session.bulk_save_objects(devices) app.session.commit() + return len(devices) -# TODO: Reimplement. + +@app.task def import_ddb_data(filename='custom.txt'): - flarms = get_ddb(filename) - db.session.bulk_save_objects(flarms) - session.commit() + logger.info("Import ddb data from file.") + + devices = get_ddb(filename) + + app.session.bulk_save_objects(devices) + app.session.commit() + + return len(devices) diff --git a/ogn/collect/logbook.py b/ogn/collect/logbook.py index 20c6a3f..9d8626f 100644 --- a/ogn/collect/logbook.py +++ b/ogn/collect/logbook.py @@ -1,6 +1,6 @@ from __future__ import absolute_import -from datetime import datetime, timedelta +from datetime import datetime from celery.utils.log import get_task_logger from ogn.collect.celery import app @@ -9,7 +9,7 @@ from sqlalchemy.sql import func, null from sqlalchemy import and_, or_, insert, between from sqlalchemy.sql.expression import case, true, false, label -from ogn.model import Flarm, AircraftBeacon, TakeoffLanding +from ogn.model import AircraftBeacon, TakeoffLanding logger = get_task_logger(__name__) @@ -26,34 +26,44 @@ def compute_takeoff_and_landing(): last_takeoff_landing = datetime(2015, 1, 1, 0, 0, 0) # make a query with current, previous and next position, so we can detect takeoffs and landings - sq = app.session.query(AircraftBeacon.address, - func.lag(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_prev'), - func.lead(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_next'), - AircraftBeacon.timestamp, - func.lag(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_prev'), - func.lead(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_next'), - AircraftBeacon.latitude, - func.lag(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_prev'), - func.lead(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_next'), - AircraftBeacon.longitude, - func.lag(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_prev'), - func.lead(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_next'), - AircraftBeacon.ground_speed, - AircraftBeacon.track, - func.lag(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_prev'), - func.lead(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_next'), - AircraftBeacon.ground_speed, - func.lag(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_prev'), - func.lead(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_next'), - AircraftBeacon.altitude, - func.lag(AircraftBeacon.altitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('altitude_prev'), - func.lead(AircraftBeacon.altitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('altitude_next')) \ + sq = app.session.query( + AircraftBeacon.address, + func.lag(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_prev'), + func.lead(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_next'), + AircraftBeacon.timestamp, + func.lag(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_prev'), + func.lead(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_next'), + AircraftBeacon.latitude, + func.lag(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_prev'), + func.lead(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_next'), + AircraftBeacon.longitude, + func.lag(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_prev'), + func.lead(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_next'), + AircraftBeacon.ground_speed, + AircraftBeacon.track, + func.lag(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_prev'), + func.lead(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_next'), + AircraftBeacon.ground_speed, + func.lag(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_prev'), + func.lead(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_next'), + AircraftBeacon.altitude, + func.lag(AircraftBeacon.altitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('altitude_prev'), + func.lead(AircraftBeacon.altitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('altitude_next')) \ .filter(AircraftBeacon.timestamp > last_takeoff_landing) \ .order_by(func.date(AircraftBeacon.timestamp), AircraftBeacon.address, AircraftBeacon.timestamp) \ .subquery() # find takeoffs and landings (look at the trigger_speed) - takeoff_landing_query = app.session.query(sq.c.address, sq.c.timestamp, sq.c.latitude, sq.c.longitude, sq.c.track, sq.c.ground_speed, sq.c.altitude, case([(sq.c.ground_speed>takeoff_speed, True), (sq.c.ground_speed takeoff_speed, True), + (sq.c.ground_speed < landing_speed, False)]).label('is_takeoff')) \ .filter(sq.c.address_prev == sq.c.address == sq.c.address_next) \ .filter(or_(and_(sq.c.ground_speed_prev < takeoff_speed, # takeoff sq.c.ground_speed > takeoff_speed, @@ -67,6 +77,3 @@ def compute_takeoff_and_landing(): ins = insert(TakeoffLanding).from_select((TakeoffLanding.address, TakeoffLanding.timestamp, TakeoffLanding.latitude, TakeoffLanding.longitude, TakeoffLanding.track, TakeoffLanding.ground_speed, TakeoffLanding.altitude, TakeoffLanding.is_takeoff), takeoff_landing_query) app.session.execute(ins) app.session.commit() - - - diff --git a/ogn/commands/database.py b/ogn/commands/database.py old mode 100755 new mode 100644 index f18b9f4..f6c3ed6 --- a/ogn/commands/database.py +++ b/ogn/commands/database.py @@ -5,6 +5,7 @@ manager = Manager() from ogn.collect.fetchddb import update_ddb_data + @manager.command def init(): """Initialize the database.""" diff --git a/ogn/commands/logbook.py b/ogn/commands/logbook.py old mode 100755 new mode 100644 index 8d84341..cc23558 --- a/ogn/commands/logbook.py +++ b/ogn/commands/logbook.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -from datetime import datetime, timedelta +from datetime import timedelta from sqlalchemy.sql import func, null from sqlalchemy import and_, or_, insert, between from sqlalchemy.sql.expression import case, true, false, label -from ogn.model import Flarm, AircraftBeacon, TakeoffLanding +from ogn.model import Flarm, TakeoffLanding from ogn.commands.dbutils import session from manager import Manager manager = Manager() + @manager.command def show(airport_name, latitude, longitude, altitude): """Show a logbook for located at given position.""" @@ -27,19 +28,62 @@ def show(airport_name, latitude, longitude, altitude): max_altitude = altitude + 200 # make a query with current, previous and next "takeoff_landing" event, so we can find complete flights - sq = session.query(TakeoffLanding.address, - func.lag(TakeoffLanding.address).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('address_prev'), - func.lead(TakeoffLanding.address).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('address_next'), - TakeoffLanding.timestamp, - func.lag(TakeoffLanding.timestamp).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('timestamp_prev'), - func.lead(TakeoffLanding.timestamp).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('timestamp_next'), - TakeoffLanding.track, - func.lag(TakeoffLanding.track).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('track_prev'), - func.lead(TakeoffLanding.track).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('track_next'), - TakeoffLanding.is_takeoff, - func.lag(TakeoffLanding.is_takeoff).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('is_takeoff_prev'), - func.lead(TakeoffLanding.is_takeoff).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('is_takeoff_next')) \ - .filter(and_(between(TakeoffLanding.latitude, latmin, latmax), between(TakeoffLanding.longitude, lonmin, lonmax))) \ + sq = session.query( + TakeoffLanding.address, + func.lag(TakeoffLanding.address) \ + .over( + order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('address_prev'), + func.lead(TakeoffLanding.address) \ + .over( + order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('address_next'), + TakeoffLanding.timestamp, + func.lag( + TakeoffLanding.timestamp) \ + .over( + order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('timestamp_prev'), + func.lead( + TakeoffLanding.timestamp) \ + .over(order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('timestamp_next'), + TakeoffLanding.track, + func.lag( + TakeoffLanding.track) \ + .over(order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('track_prev'), + func.lead( + TakeoffLanding.track) \ + .over(order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('track_next'), + TakeoffLanding.is_takeoff, + func.lag( + TakeoffLanding.is_takeoff) \ + .over(order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('is_takeoff_prev'), + func.lead( + TakeoffLanding.is_takeoff) \ + .over(order_by=and_(func.date(TakeoffLanding.timestamp), + TakeoffLanding.address, + TakeoffLanding.timestamp)) \ + .label('is_takeoff_next')) \ + .filter(and_(between(TakeoffLanding.latitude, latmin, latmax), + between(TakeoffLanding.longitude, lonmin, lonmax))) \ .filter(TakeoffLanding.altitude < max_altitude) \ .subquery() @@ -50,7 +94,7 @@ def show(airport_name, latitude, longitude, altitude): .filter(sq.c.timestamp_next - sq.c.timestamp < timedelta(days=1)) # split complete flights (with takeoff and landing) with duration > 1 day into one takeoff and one landing - split_start_query = session.query(sq.c.timestamp.label('reftime'), sq.c.address.label('address'), sq.c.timestamp.label('takeoff'), sq.c.track.label('takeoff_track'), null().label('landing'), null().label('landing_track'), null().label('duration')) \ + split_start_query = session.query(sq.c.timestamp.label('reftime'), sq.c.address.label('address'), sq.c.timestamp.label('takeoff'), sq.c.track.label('takeoff_track'), null().label('landing'), null().label('landing_track'), null().label('duration')) \ .filter(and_(sq.c.is_takeoff == true(), sq.c.is_takeoff_next == false())) \ .filter(sq.c.address == sq.c.address_next) \ .filter(sq.c.timestamp_next - sq.c.timestamp >= timedelta(days=1)) @@ -73,19 +117,42 @@ def show(airport_name, latitude, longitude, altitude): sq.c.is_takeoff_next == true())) # unite all - union_query = complete_flight_query.union(split_start_query, split_landing_query, only_landings_query, only_starts_query) \ + union_query = complete_flight_query.union( + split_start_query, + split_landing_query, + only_landings_query, + only_starts_query) \ .subquery() # get aircraft informations and sort all entries by the reference time - logbook_query = session.query(union_query.c.reftime, union_query.c.address, union_query.c.takeoff, union_query.c.takeoff_track, union_query.c.landing, union_query.c.landing_track, union_query.c.duration, Flarm.registration, Flarm.aircraft) \ + logbook_query = session.query( + union_query.c.reftime, + union_query.c.address, + union_query.c.takeoff, + union_query.c.takeoff_track, + union_query.c.landing, + union_query.c.landing_track, + union_query.c.duration, + Flarm.registration, + Flarm.aircraft) \ .outerjoin(Flarm, union_query.c.address == Flarm.address) \ .order_by(union_query.c.reftime) - print('--- Logbook (' + airport_name + ') ---') + print('--- Logbook (%s) ---' % airport_name) + none_datetime_replacer = lambda datetime_object: '--:--:--' if datetime_object is None else datetime_object.time() - none_track_replacer = lambda track_object: '--' if track_object is None else round(track_object/10.0) + none_track_replacer = lambda track_object: '--' if track_object is None else round(track_object / 10.0) none_timedelta_replacer = lambda timedelta_object: '--:--:--' if timedelta_object is None else timedelta_object none_registration_replacer = lambda registration_object, address: '[' + address + ']' if registration_object is None else registration_object none_aircraft_replacer = lambda aircraft_object: '(unknown)' if aircraft_object is None else aircraft_object + for [reftime, address, takeoff, takeoff_track, landing, landing_track, duration, registration, aircraft] in logbook_query.all(): - print('%10s %8s (%2s) %8s (%2s) %8s %8s %s' % (reftime.date(), none_datetime_replacer(takeoff), none_track_replacer(takeoff_track), none_datetime_replacer(landing), none_track_replacer(landing_track), none_timedelta_replacer(duration), none_registration_replacer(registration, address), none_aircraft_replacer(aircraft))) + print('%10s %8s (%2s) %8s (%2s) %8s %8s %s' % ( + reftime.date(), + none_datetime_replacer(takeoff), + none_track_replacer(takeoff_track), + none_datetime_replacer(landing), + none_track_replacer(landing_track), + none_timedelta_replacer(duration), + none_registration_replacer(registration, address), + none_aircraft_replacer(aircraft))) diff --git a/ogn/commands/showreceiver.py b/ogn/commands/showreceiver.py old mode 100755 new mode 100644 index 8940e6b..c484884 --- a/ogn/commands/showreceiver.py +++ b/ogn/commands/showreceiver.py @@ -9,7 +9,7 @@ from ogn.model.receiver_device import ReceiverDevice from ogn.commands.dbutils import session back_24h = datetime.utcnow() - timedelta(days=1) -receiver_messages_per_24h = 24*60 / 5 +receiver_messages_per_24h = 24 * 60 / 5 from manager import Manager manager = Manager() diff --git a/ogn/exceptions.py b/ogn/exceptions.py new file mode 100644 index 0000000..d4d8566 --- /dev/null +++ b/ogn/exceptions.py @@ -0,0 +1,12 @@ +""" +exception definitions +""" + + +class AprsParseError(Exception): + """Parse error while parsing an aprs packet substring.""" + def __init__(self, substring, expected_type): + self.message = "Aprs Substring can't be parsed as %s." % expected_type + super(AprsParseError, self).__init__(self.message) + self.substring = substring + self.expected_type = expected_type diff --git a/ogn/gateway/__init__.py b/ogn/gateway/__init__.py index 31a5eff..99a6e3b 100644 --- a/ogn/gateway/__init__.py +++ b/ogn/gateway/__init__.py @@ -4,6 +4,7 @@ from time import time from ogn.gateway import settings from ogn.commands.dbutils import session from ogn.aprs_parser import parse_aprs +from ogn.exceptions import AprsParseError from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -27,14 +28,14 @@ class ognGateway: self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.sock.connect((settings.APRS_SERVER_HOST, settings.APRS_SERVER_PORT)) - login = 'user %s pass %s vers ogn-gateway-python %s %s\n' % (aprs_user, settings.APRS_PASSCODE, MODULE_VERSION, settings.APRS_FILTER) + login = 'user %s pass %s vers ogn-gateway-python %s %s\n' % (aprs_user, settings.APRS_PASSCODE, MODULE_VERSION, settings.APRS_FILTER) self.sock.send(login.encode()) self.sock_file = self.sock.makefile('rw') def run(self): keepalive_time = time() while True: - if time()-keepalive_time > 60: + if time() - keepalive_time > 60: self.sock.send("#keepalive".encode()) keepalive_time = time() @@ -60,9 +61,9 @@ class ognGateway: def proceed_line(self, line): try: beacon = parse_aprs(line) - except Exception as e: - print('Failed to parse line: %s' % line) - print('Reason: %s' % e) + except AprsParseError as e: + print(e.message) + print("Substring: " % e.substring) return if beacon is not None: diff --git a/ogn/model/aircraft_beacon.py b/ogn/model/aircraft_beacon.py index ac67d1a..79d4477 100644 --- a/ogn/model/aircraft_beacon.py +++ b/ogn/model/aircraft_beacon.py @@ -97,7 +97,7 @@ class AircraftBeacon(Beacon): self.stealth = ((int(address_match.group(1), 16) & 0b10000000) >> 7 == 1) self.address = address_match.group(2) elif climb_rate_match is not None: - self.climb_rate = int(climb_rate_match.group(1))*fpm2ms + self.climb_rate = int(climb_rate_match.group(1)) * fpm2ms elif turn_rate_match is not None: self.turn_rate = float(turn_rate_match.group(1)) elif signal_strength_match is not None: @@ -130,4 +130,18 @@ class AircraftBeacon(Beacon): raise Exception("No valid position description: %s" % part) def __repr__(self): - return("" % (self.name, self.address_type, self.aircraft_type, self.timestamp, self.address_type, self.aircraft_type, self.stealth, self.address, self.climb_rate, self.turn_rate, self.signal_strength, self.error_count, self.frequency_offset, self.gps_status)) + return "" % ( + self.name, + self.address_type, + self.aircraft_type, + self.timestamp, + self.address_type, + self.aircraft_type, + self.stealth, + self.address, + self.climb_rate, + self.turn_rate, + self.signal_strength, + self.error_count, + self.frequency_offset, + self.gps_status) diff --git a/ogn/model/beacon.py b/ogn/model/beacon.py index 4d14955..511a383 100644 --- a/ogn/model/beacon.py +++ b/ogn/model/beacon.py @@ -4,12 +4,13 @@ from sqlalchemy import Column, String, Integer, Float, DateTime from sqlalchemy.ext.declarative import AbstractConcreteBase from ogn.aprs_utils import createTimestamp, dmsToDeg, kts2kmh, feet2m +from ogn.exceptions import AprsParseError from .base import Base # "original" pattern from OGN: "(.+?)>APRS,.+,(.+?):/(\\d{6})+h(\\d{4}\\.\\d{2})(N|S).(\\d{5}\\.\\d{2})(E|W).((\\d{3})/(\\d{3}))?/A=(\\d{6}).*?" PATTERN_APRS = r"^(.+?)>APRS,.+,(.+?):/(\d{6})+h(\d{4}\.\d{2})(N|S)(.)(\d{5}\.\d{2})(E|W)(.)((\d{3})/(\d{3}))?/A=(\d{6})\s(.*)$" -prog = re.compile(PATTERN_APRS) +re_pattern_aprs = re.compile(PATTERN_APRS) class Beacon(AbstractConcreteBase, Base): @@ -29,9 +30,9 @@ class Beacon(AbstractConcreteBase, Base): comment = None def parse(self, text, reference_time=None): - result = prog.match(text) + result = re_pattern_aprs.match(text) if result is None: - raise Exception("String is not valid" % text) + raise AprsParseError(substring=text, expected_type="Beacon") self.name = result.group(1) self.receiver_name = result.group(2) @@ -52,11 +53,11 @@ class Beacon(AbstractConcreteBase, Base): if result.group(10) is not None: self.track = int(result.group(11)) - self.ground_speed = int(result.group(12))*kts2kmh + self.ground_speed = int(result.group(12)) * kts2kmh else: self.track = 0 self.ground_speed = 0 - self.altitude = int(result.group(13))*feet2m + self.altitude = int(result.group(13)) * feet2m self.comment = result.group(14) diff --git a/ogn/model/flarm.py b/ogn/model/flarm.py index 90d46d3..2229977 100644 --- a/ogn/model/flarm.py +++ b/ogn/model/flarm.py @@ -21,4 +21,14 @@ class Flarm(Base): address_origin = Column(SmallInteger) def __repr__(self): - return("" % (self.address_type, self.address, self.name, self.airport, self.aircraft, self.registration, self.competition, self.frequency, self.tracked, self.identified)) + return "" % ( + self.address_type, + self.address, + self.name, + self.airport, + self.aircraft, + self.registration, + self.competition, + self.frequency, + self.tracked, + self.identified) diff --git a/ogn/model/receiver_beacon.py b/ogn/model/receiver_beacon.py index 4d3e892..acb9fd4 100644 --- a/ogn/model/receiver_beacon.py +++ b/ogn/model/receiver_beacon.py @@ -85,4 +85,4 @@ class ReceiverBeacon(Beacon): raise Exception("No valid receiver description: %s" % part) def __repr__(self): - return("" % (self.name, self.version)) + return "" % (self.name, self.version)