Merge pull request #7 from kerel-fs/feature/+parsererror

Add AprsParseError, Reimplement ddb import from file, Cleanup
pull/1/head
Meisterschueler 2015-11-15 19:42:28 +01:00
commit 4752b3fdbb
15 zmienionych plików z 209 dodań i 85 usunięć

Wyświetl plik

@ -78,8 +78,10 @@ The task server must be running for `db.updateddb`.
- [x] Document/Improve cli commands - [x] Document/Improve cli commands
- [ ] Separate settings from module (currently at ogn/command/dbutils.py) - [ ] Separate settings from module (currently at ogn/command/dbutils.py)
- [ ] Enable granular data acquisition (eg. store receiver beacons only) - [ ] Enable granular data acquisition (eg. store receiver beacons only)
- [ ] Database: Rename 'Flarm' to 'Device'?
- [ ] Future Database-Migrations: Use Alembric? - [ ] 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?) - [ ] Fix command/logbook.py (@Meisterschueler?)
- [ ] Introduce scheduled tasks with 'celery beat' (eg. updateddb) - [ ] Introduce scheduled tasks with 'celery beat' (eg. updateddb)

Wyświetl plik

@ -1,16 +1,16 @@
from .model import Beacon, AircraftBeacon, ReceiverBeacon from .model import Beacon, AircraftBeacon, ReceiverBeacon
from ogn.exceptions import AprsParseError
def parse_aprs(packet):
def parse_aprs(text): if not isinstance(packet, str):
if not isinstance(text, str): raise TypeError("Expected packet to be str, got %s" % type(packet))
raise Exception("Unknown type: %s" % type(text)) elif packet == "":
elif text == "": raise AprsParseError(substring=packet, expected_type="non-empty aprs packet")
raise Exception("String is empty") elif packet[0] == "#":
elif text[0] == "#":
return None return None
beacon = Beacon() beacon = Beacon()
beacon.parse(text) beacon.parse(packet)
# symboltable / symbolcodes used by OGN: # symboltable / symbolcodes used by OGN:
# I&: used as receiver # I&: used as receiver

Wyświetl plik

@ -6,16 +6,16 @@ kmh2kts = 0.539957
feet2m = 0.3048 feet2m = 0.3048
ms2fpm = 196.85 ms2fpm = 196.85
kts2kmh = 1/kmh2kts kts2kmh = 1 / kmh2kts
m2feet = 1/feet2m m2feet = 1 / feet2m
fpm2ms = 1/ms2fpm fpm2ms = 1 / ms2fpm
def dmsToDeg(dms): def dmsToDeg(dms):
absDms = abs(dms) absDms = abs(dms)
d = math.floor(absDms) d = math.floor(absDms)
m = (absDms - d) * 100 / 60 m = (absDms - d) * 100 / 60
return (d + m) return d + m
def createTimestamp(hhmmss, reference): def createTimestamp(hhmmss, reference):
@ -31,6 +31,6 @@ def createTimestamp(hhmmss, reference):
reference = reference + timedelta(days=1) reference = reference + timedelta(days=1)
elif (reference.hour == 0) & (hh == 23): elif (reference.hour == 0) & (hh == 23):
reference = reference - timedelta(days=1) 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)) 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) return datetime(reference.year, reference.month, reference.day, hh, mm, ss)

Wyświetl plik

@ -3,7 +3,7 @@ from __future__ import absolute_import
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from celery import Celery, Task from celery import Celery
from celery.signals import worker_init, worker_shutdown from celery.signals import worker_init, worker_shutdown
app = Celery('ogn.collect', app = Celery('ogn.collect',
@ -13,6 +13,7 @@ app = Celery('ogn.collect',
DB_URI = 'sqlite:///beacons.db' DB_URI = 'sqlite:///beacons.db'
@worker_init.connect @worker_init.connect
def connect_db(signal, sender): def connect_db(signal, sender):
# Load settings like DB_URI... # Load settings like DB_URI...
@ -21,6 +22,7 @@ def connect_db(signal, sender):
Session = sessionmaker(bind=engine) Session = sessionmaker(bind=engine)
sender.app.session = Session() sender.app.session = Session()
@worker_shutdown.connect @worker_shutdown.connect
def close_db(signal, sender): def close_db(signal, sender):
sender.app.session.close() sender.app.session.close()

Wyświetl plik

@ -16,14 +16,21 @@ def update_ddb_data():
app.session.query(Flarm).delete() app.session.query(Flarm).delete()
devices = get_ddb() devices = get_ddb()
logger.info("Devices: %s"%str(devices)) logger.debug("New Devices: %s" % str(devices))
app.session.bulk_save_objects(devices)
app.session.bulk_save_objects(devices)
app.session.commit() app.session.commit()
return len(devices) return len(devices)
# TODO: Reimplement.
@app.task
def import_ddb_data(filename='custom.txt'): def import_ddb_data(filename='custom.txt'):
flarms = get_ddb(filename) logger.info("Import ddb data from file.")
db.session.bulk_save_objects(flarms)
session.commit() devices = get_ddb(filename)
app.session.bulk_save_objects(devices)
app.session.commit()
return len(devices)

Wyświetl plik

@ -1,6 +1,6 @@
from __future__ import absolute_import from __future__ import absolute_import
from datetime import datetime, timedelta from datetime import datetime
from celery.utils.log import get_task_logger from celery.utils.log import get_task_logger
from ogn.collect.celery import app 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 import and_, or_, insert, between
from sqlalchemy.sql.expression import case, true, false, label 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__) logger = get_task_logger(__name__)
@ -26,34 +26,44 @@ def compute_takeoff_and_landing():
last_takeoff_landing = datetime(2015, 1, 1, 0, 0, 0) 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 # make a query with current, previous and next position, so we can detect takeoffs and landings
sq = app.session.query(AircraftBeacon.address, sq = app.session.query(
func.lag(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_prev'), AircraftBeacon.address,
func.lead(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_next'), func.lag(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_prev'),
AircraftBeacon.timestamp, func.lead(AircraftBeacon.address).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('address_next'),
func.lag(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_prev'), AircraftBeacon.timestamp,
func.lead(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_next'), func.lag(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_prev'),
AircraftBeacon.latitude, func.lead(AircraftBeacon.timestamp).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('timestamp_next'),
func.lag(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_prev'), AircraftBeacon.latitude,
func.lead(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_next'), func.lag(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_prev'),
AircraftBeacon.longitude, func.lead(AircraftBeacon.latitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('latitude_next'),
func.lag(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_prev'), AircraftBeacon.longitude,
func.lead(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_next'), func.lag(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_prev'),
AircraftBeacon.ground_speed, func.lead(AircraftBeacon.longitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('longitude_next'),
AircraftBeacon.track, AircraftBeacon.ground_speed,
func.lag(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_prev'), AircraftBeacon.track,
func.lead(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_next'), func.lag(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_prev'),
AircraftBeacon.ground_speed, func.lead(AircraftBeacon.track).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('track_next'),
func.lag(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_prev'), AircraftBeacon.ground_speed,
func.lead(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_next'), func.lag(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_prev'),
AircraftBeacon.altitude, func.lead(AircraftBeacon.ground_speed).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('ground_speed_next'),
func.lag(AircraftBeacon.altitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('altitude_prev'), AircraftBeacon.altitude,
func.lead(AircraftBeacon.altitude).over(order_by=and_(AircraftBeacon.address, AircraftBeacon.timestamp)).label('altitude_next')) \ 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) \ .filter(AircraftBeacon.timestamp > last_takeoff_landing) \
.order_by(func.date(AircraftBeacon.timestamp), AircraftBeacon.address, AircraftBeacon.timestamp) \ .order_by(func.date(AircraftBeacon.timestamp), AircraftBeacon.address, AircraftBeacon.timestamp) \
.subquery() .subquery()
# find takeoffs and landings (look at the trigger_speed) # 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<landing_speed, False)]).label('is_takeoff')) \ 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 < landing_speed, False)]).label('is_takeoff')) \
.filter(sq.c.address_prev == sq.c.address == sq.c.address_next) \ .filter(sq.c.address_prev == sq.c.address == sq.c.address_next) \
.filter(or_(and_(sq.c.ground_speed_prev < takeoff_speed, # takeoff .filter(or_(and_(sq.c.ground_speed_prev < takeoff_speed, # takeoff
sq.c.ground_speed > takeoff_speed, 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) 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.execute(ins)
app.session.commit() app.session.commit()

1
ogn/commands/database.py 100755 → 100644
Wyświetl plik

@ -5,6 +5,7 @@ manager = Manager()
from ogn.collect.fetchddb import update_ddb_data from ogn.collect.fetchddb import update_ddb_data
@manager.command @manager.command
def init(): def init():
"""Initialize the database.""" """Initialize the database."""

109
ogn/commands/logbook.py 100755 → 100644
Wyświetl plik

@ -1,18 +1,19 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import datetime, timedelta from datetime import timedelta
from sqlalchemy.sql import func, null from sqlalchemy.sql import func, null
from sqlalchemy import and_, or_, insert, between from sqlalchemy import and_, or_, insert, between
from sqlalchemy.sql.expression import case, true, false, label 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 ogn.commands.dbutils import session
from manager import Manager from manager import Manager
manager = Manager() manager = Manager()
@manager.command @manager.command
def show(airport_name, latitude, longitude, altitude): def show(airport_name, latitude, longitude, altitude):
"""Show a logbook for <airport_name> located at given position.""" """Show a logbook for <airport_name> located at given position."""
@ -27,19 +28,62 @@ def show(airport_name, latitude, longitude, altitude):
max_altitude = altitude + 200 max_altitude = altitude + 200
# make a query with current, previous and next "takeoff_landing" event, so we can find complete flights # make a query with current, previous and next "takeoff_landing" event, so we can find complete flights
sq = session.query(TakeoffLanding.address, sq = session.query(
func.lag(TakeoffLanding.address).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('address_prev'), TakeoffLanding.address,
func.lead(TakeoffLanding.address).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('address_next'), func.lag(TakeoffLanding.address) \
TakeoffLanding.timestamp, .over(
func.lag(TakeoffLanding.timestamp).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('timestamp_prev'), order_by=and_(func.date(TakeoffLanding.timestamp),
func.lead(TakeoffLanding.timestamp).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('timestamp_next'), TakeoffLanding.address,
TakeoffLanding.track, TakeoffLanding.timestamp)) \
func.lag(TakeoffLanding.track).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('track_prev'), .label('address_prev'),
func.lead(TakeoffLanding.track).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('track_next'), func.lead(TakeoffLanding.address) \
TakeoffLanding.is_takeoff, .over(
func.lag(TakeoffLanding.is_takeoff).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('is_takeoff_prev'), order_by=and_(func.date(TakeoffLanding.timestamp),
func.lead(TakeoffLanding.is_takeoff).over(order_by=and_(func.date(TakeoffLanding.timestamp), TakeoffLanding.address, TakeoffLanding.timestamp)).label('is_takeoff_next')) \ TakeoffLanding.address,
.filter(and_(between(TakeoffLanding.latitude, latmin, latmax), between(TakeoffLanding.longitude, lonmin, lonmax))) \ 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) \ .filter(TakeoffLanding.altitude < max_altitude) \
.subquery() .subquery()
@ -50,7 +94,7 @@ def show(airport_name, latitude, longitude, altitude):
.filter(sq.c.timestamp_next - sq.c.timestamp < timedelta(days=1)) .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 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(and_(sq.c.is_takeoff == true(), sq.c.is_takeoff_next == false())) \
.filter(sq.c.address == sq.c.address_next) \ .filter(sq.c.address == sq.c.address_next) \
.filter(sq.c.timestamp_next - sq.c.timestamp >= timedelta(days=1)) .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())) sq.c.is_takeoff_next == true()))
# unite all # 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() .subquery()
# get aircraft informations and sort all entries by the reference time # 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) \ .outerjoin(Flarm, union_query.c.address == Flarm.address) \
.order_by(union_query.c.reftime) .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_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_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_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 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(): 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)))

2
ogn/commands/showreceiver.py 100755 → 100644
Wyświetl plik

@ -9,7 +9,7 @@ from ogn.model.receiver_device import ReceiverDevice
from ogn.commands.dbutils import session from ogn.commands.dbutils import session
back_24h = datetime.utcnow() - timedelta(days=1) 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 from manager import Manager
manager = Manager() manager = Manager()

12
ogn/exceptions.py 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -4,6 +4,7 @@ from time import time
from ogn.gateway import settings from ogn.gateway import settings
from ogn.commands.dbutils import session from ogn.commands.dbutils import session
from ogn.aprs_parser import parse_aprs from ogn.aprs_parser import parse_aprs
from ogn.exceptions import AprsParseError
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
@ -27,14 +28,14 @@ class ognGateway:
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.sock.connect((settings.APRS_SERVER_HOST, settings.APRS_SERVER_PORT)) 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.send(login.encode())
self.sock_file = self.sock.makefile('rw') self.sock_file = self.sock.makefile('rw')
def run(self): def run(self):
keepalive_time = time() keepalive_time = time()
while True: while True:
if time()-keepalive_time > 60: if time() - keepalive_time > 60:
self.sock.send("#keepalive".encode()) self.sock.send("#keepalive".encode())
keepalive_time = time() keepalive_time = time()
@ -60,9 +61,9 @@ class ognGateway:
def proceed_line(self, line): def proceed_line(self, line):
try: try:
beacon = parse_aprs(line) beacon = parse_aprs(line)
except Exception as e: except AprsParseError as e:
print('Failed to parse line: %s' % line) print(e.message)
print('Reason: %s' % e) print("Substring: " % e.substring)
return return
if beacon is not None: if beacon is not None:

Wyświetl plik

@ -97,7 +97,7 @@ class AircraftBeacon(Beacon):
self.stealth = ((int(address_match.group(1), 16) & 0b10000000) >> 7 == 1) self.stealth = ((int(address_match.group(1), 16) & 0b10000000) >> 7 == 1)
self.address = address_match.group(2) self.address = address_match.group(2)
elif climb_rate_match is not None: 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: elif turn_rate_match is not None:
self.turn_rate = float(turn_rate_match.group(1)) self.turn_rate = float(turn_rate_match.group(1))
elif signal_strength_match is not None: elif signal_strength_match is not None:
@ -130,4 +130,18 @@ class AircraftBeacon(Beacon):
raise Exception("No valid position description: %s" % part) raise Exception("No valid position description: %s" % part)
def __repr__(self): def __repr__(self):
return("<AircraftBeacon %s: %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s>" % (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 "<AircraftBeacon %s: %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s>" % (
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)

Wyświetl plik

@ -4,12 +4,13 @@ from sqlalchemy import Column, String, Integer, Float, DateTime
from sqlalchemy.ext.declarative import AbstractConcreteBase from sqlalchemy.ext.declarative import AbstractConcreteBase
from ogn.aprs_utils import createTimestamp, dmsToDeg, kts2kmh, feet2m from ogn.aprs_utils import createTimestamp, dmsToDeg, kts2kmh, feet2m
from ogn.exceptions import AprsParseError
from .base import Base 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}).*?" # "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(.*)$" 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): class Beacon(AbstractConcreteBase, Base):
@ -29,9 +30,9 @@ class Beacon(AbstractConcreteBase, Base):
comment = None comment = None
def parse(self, text, reference_time=None): def parse(self, text, reference_time=None):
result = prog.match(text) result = re_pattern_aprs.match(text)
if result is None: if result is None:
raise Exception("String is not valid" % text) raise AprsParseError(substring=text, expected_type="Beacon")
self.name = result.group(1) self.name = result.group(1)
self.receiver_name = result.group(2) self.receiver_name = result.group(2)
@ -52,11 +53,11 @@ class Beacon(AbstractConcreteBase, Base):
if result.group(10) is not None: if result.group(10) is not None:
self.track = int(result.group(11)) self.track = int(result.group(11))
self.ground_speed = int(result.group(12))*kts2kmh self.ground_speed = int(result.group(12)) * kts2kmh
else: else:
self.track = 0 self.track = 0
self.ground_speed = 0 self.ground_speed = 0
self.altitude = int(result.group(13))*feet2m self.altitude = int(result.group(13)) * feet2m
self.comment = result.group(14) self.comment = result.group(14)

Wyświetl plik

@ -21,4 +21,14 @@ class Flarm(Base):
address_origin = Column(SmallInteger) address_origin = Column(SmallInteger)
def __repr__(self): def __repr__(self):
return("<Flarm: %s,%s,%s,%s,%s,%s,%s,%s,%s,%s>" % (self.address_type, self.address, self.name, self.airport, self.aircraft, self.registration, self.competition, self.frequency, self.tracked, self.identified)) return "<Flarm: %s,%s,%s,%s,%s,%s,%s,%s,%s,%s>" % (
self.address_type,
self.address,
self.name,
self.airport,
self.aircraft,
self.registration,
self.competition,
self.frequency,
self.tracked,
self.identified)

Wyświetl plik

@ -85,4 +85,4 @@ class ReceiverBeacon(Beacon):
raise Exception("No valid receiver description: %s" % part) raise Exception("No valid receiver description: %s" % part)
def __repr__(self): def __repr__(self):
return("<ReceiverBeacon %s: %s>" % (self.name, self.version)) return "<ReceiverBeacon %s: %s>" % (self.name, self.version)