From 33a7690f7deb12bbdf0896cc364e0bd501dd3a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Gru=CC=88ndger?= Date: Sat, 30 Sep 2017 11:42:16 +0200 Subject: [PATCH 1/2] Added Tracker (OGNTRK) and Receiver (OGNSDR) parser --- ogn/parser/parse.py | 26 +++++++++++++----- ogn/parser/pattern.py | 54 ++++++++++++++++++++++++++++++++++++++ tests/parser/test_parse.py | 2 +- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/ogn/parser/parse.py b/ogn/parser/parse.py index 6978668..73757cd 100644 --- a/ogn/parser/parse.py +++ b/ogn/parser/parse.py @@ -11,6 +11,10 @@ from ogn.parser.parse_lt24 import parse as parse_lt24_beacon from ogn.parser.parse_spider import parse as parse_spider_beacon from ogn.parser.parse_spot import parse as parse_spot_beacon from ogn.parser.parse_skylines import parse as parse_skylines_beacon +from ogn.parser.parse_tracker import parse_position as parse_tracker_position +from ogn.parser.parse_tracker import parse_status as parse_tracker_status +from ogn.parser.parse_receiver import parse_position as parse_receiver_position +from ogn.parser.parse_receiver import parse_status as parse_receiver_status def parse_aprs(message, reference_date=None, reference_time=None): @@ -50,7 +54,7 @@ def parse_aprs(message, reference_date=None, reference_time=None): raise AprsParseError(message) -def parse_ogn_beacon(aprs_comment, dstcall="APRS"): +def parse_ogn_beacon(aprs_comment, dstcall="APRS", aprs_type="position"): if dstcall == "APRS": # this can be a receiver or an aircraft if not aprs_comment: return {'beacon_type': 'receiver_beacon'} @@ -71,13 +75,21 @@ def parse_ogn_beacon(aprs_comment, dstcall="APRS"): ac_data.update({'beacon_type': 'aircraft_beacon'}) return ac_data elif dstcall == "OGNTRK": - ac_data = parse_aircraft_beacon(aprs_comment) - ac_data.update({'beacon_type': 'aircraft_beacon'}) - return ac_data + if aprs_type == "position": + data = parse_tracker_position(aprs_comment) + data.update({'beacon_type': 'aircraft_beacon'}) + elif aprs_type == "status": + data = parse_tracker_status(aprs_comment) + data.update({'beacon_type': 'aircraft_beacon'}) + return data elif dstcall == "OGNSDR": - ac_data = parse_receiver_beacon(aprs_comment) - ac_data.update({'beacon_type': 'receiver_beacon'}) - return ac_data + if aprs_type == "position": + data = parse_receiver_position(aprs_comment) + data.update({'beacon_type': 'receiver_beacon'}) + elif aprs_type == "status": + data = parse_receiver_status(aprs_comment) + data.update({'beacon_type': 'receiver_beacon'}) + return data elif dstcall == "OGLT24": ac_data = parse_lt24_beacon(aprs_comment) ac_data.update({'beacon_type': 'lt24_beacon'}) diff --git a/ogn/parser/pattern.py b/ogn/parser/pattern.py index 8a0dce2..dc33363 100644 --- a/ogn/parser/pattern.py +++ b/ogn/parser/pattern.py @@ -10,6 +10,60 @@ PATTERN_NAVITER_BEACON = re.compile(""" (?P[+-][\d.]+)rot """, re.VERBOSE | re.MULTILINE) +PATTERN_TRACKER_BEACON_POSITION = re.compile(""" + id(?P
\w{2})(?P\w{6}?)\s? + (?:(?P[+-]\d+?)fpm\s)? + (?:(?P[+-][\d.]+?)rot\s)? + (?:FL(?P[\d.]+)\s)? + (?:(?P[\d.]+?)dB\s)? + (?:(?P\d+)e\s)? + (?:(?P[+-][\d.]+?)kHz\s?)? + (?:gps(?P\d+x\d+)\s?)? + (?:s(?P[\d.]+)\s?)? + (?:h(?P[\dA-F]{2})\s?)? +""", re.VERBOSE | re.MULTILINE) + +PATTERN_TRACKER_BEACON_STATUS = re.compile(""" + h(?P[\d]{2})\s + v(?P[\d]{2})\s + (?P[\d]+)sat/(?P\d)\s + (?P\d+)m\s + (?P[\d.]+)hPa\s + (?P[+-][\d.]+)degC\s + (?P\d+)%\s + (?P[\d.]+)V\s + (?P\d+)/(?P[+-][\d.]+)dBm\s + (?P\d+)/min +""", re.VERBOSE | re.MULTILINE) + +PATTERN_RECEIVER_POSITION = re.compile(r""" + (?:(?P.+))? +""", re.VERBOSE | re.MULTILINE) + +PATTERN_RECEIVER_STATUS = re.compile(""" + (?: + v(?P\d+\.\d+\.\d+) + (?:\.(?P.+?))? + \s)? + CPU:(?P[\d.]+)\s + RAM:(?P[\d.]+)/(?P[\d.]+)MB\s + NTP:(?P[\d.]+)ms/(?P[+-][\d.]+)ppm\s + (?:(?P[\d.]+)V\s)? + (?:(?P[\d.]+)A\s)? + (?:(?P[+-][\d.]+)C\s*)? + (?:(?P\d+)/(?P\d+)Acfts\[1h\]\s*)? + (?:RF: + (?: + (?P[+-][\d]+) + (?P[+-][\d.]+)ppm/ + )? + (?P[+-][\d.]+)dB + (?:/(?P[+-][\d.]+)dB@10km\[(?P\d+)\])? + (?:/(?P[+-][\d.]+)dB@10km\[(?P\d+)/(?P\d+)\])? + )? +""", re.VERBOSE | re.MULTILINE) + + # The following regexp patterns are part of the ruby ogn-client. # source: https://github.com/svoop/ogn_client-ruby diff --git a/tests/parser/test_parse.py b/tests/parser/test_parse.py index b855baa..3417f73 100644 --- a/tests/parser/test_parse.py +++ b/tests/parser/test_parse.py @@ -17,7 +17,7 @@ class TestStringMethods(unittest.TestCase): aprs = parse_aprs(line, datetime(2015, 4, 10, 17, 0)) self.assertFalse(aprs is None) if aprs['comment']: - message = parse_ogn_beacon(aprs['comment'], dstcall=aprs['dstcall']) + message = parse_ogn_beacon(aprs['comment'], dstcall=aprs['dstcall'], aprs_type=aprs['aprs_type']) self.assertEqual(message['beacon_type'], beacon_type) def test_aprs_aircraft_beacons(self): From 054c9eeed0c3b8272efab6cb89299521bb469324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Gru=CC=88ndger?= Date: Sat, 30 Sep 2017 14:17:35 +0200 Subject: [PATCH 2/2] Fixed ddhhmm vs. hhmmss problem --- ogn/parser/parse.py | 9 ++++++++- ogn/parser/parse_receiver.py | 32 ++++++++++++++++++++++++++++++++ ogn/parser/parse_tracker.py | 27 +++++++++++++++++++++++++++ ogn/parser/pattern.py | 2 +- tests/parser/test_parse_aprs.py | 7 +++++++ 5 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 ogn/parser/parse_receiver.py create mode 100644 ogn/parser/parse_tracker.py diff --git a/ogn/parser/parse.py b/ogn/parser/parse.py index 73757cd..f0bf5e7 100644 --- a/ogn/parser/parse.py +++ b/ogn/parser/parse.py @@ -25,11 +25,18 @@ def parse_aprs(message, reference_date=None, reference_time=None): match_position = re.search(PATTERN_APRS_POSITION, message) if match_position: + if match_position.group('time_hhmmss'): + timestamp = createTimestamp(match_position.group('time_hhmmss'), reference_date, reference_time) + else: + timestamp_ddmmhh = match_position.group('time_ddmmhh') + reference_date = reference_date.replace(day=int(timestamp_ddmmhh[:2])) + timestamp = createTimestamp(timestamp_ddmmhh[2:] + '00', reference_date) + return {'name': match_position.group('callsign'), 'dstcall': match_position.group('dstcall'), 'relay': match_position.group('relay') if match_position.group('relay') else None, 'receiver_name': match_position.group('receiver'), - 'timestamp': createTimestamp(match_position.group('time'), reference_date, reference_time), + 'timestamp': timestamp, 'latitude': parseAngle('0' + match_position.group('latitude') + (match_position.group('latitude_enhancement') or '0')) * (-1 if match_position.group('latitude_sign') == 'S' else 1), 'symboltable': match_position.group('symbol_table'), diff --git a/ogn/parser/parse_receiver.py b/ogn/parser/parse_receiver.py new file mode 100644 index 0000000..ab919ee --- /dev/null +++ b/ogn/parser/parse_receiver.py @@ -0,0 +1,32 @@ +import re + +from ogn.parser.pattern import PATTERN_RECEIVER_POSITION, PATTERN_RECEIVER_STATUS + + +def parse_position(aprs_comment): + match = re.search(PATTERN_RECEIVER_POSITION, aprs_comment) + return {'user_comment': match.group('user_comment') if match.group('user_comment') else None} + + +def parse_status(aprs_comment): + match = re.search(PATTERN_RECEIVER_STATUS, aprs_comment) + return {'version': match.group('version'), + 'platform': match.group('platform'), + 'cpu_load': float(match.group('cpu_load')), + 'free_ram': float(match.group('ram_free')), + 'total_ram': float(match.group('ram_total')), + 'ntp_error': float(match.group('ntp_offset')), + 'rt_crystal_correction': float(match.group('ntp_correction')), + 'voltage': float(match.group('voltage')) if match.group('voltage') else None, + 'amperage': float(match.group('amperage')) if match.group('amperage') else None, + 'cpu_temp': float(match.group('cpu_temperature')) if match.group('cpu_temperature') else None, + 'senders_visible': int(match.group('visible_senders')) if match.group('visible_senders') else None, + 'senders_total': int(match.group('senders')) if match.group('senders') else None, + 'rec_crystal_correction': int(match.group('rf_correction_manual')) if match.group('rf_correction_manual') else None, + 'rec_crystal_correction_fine': float(match.group('rf_correction_automatic')) if match.group('rf_correction_automatic') else None, + 'rec_input_noise': float(match.group('signal_quality')) if match.group('signal_quality') else None, + 'senders_signal': float(match.group('senders_signal_quality')) if match.group('senders_signal_quality') else None, + 'senders_messages': float(match.group('senders_messages')) if match.group('senders_messages') else None, + 'good_senders_signal': float(match.group('good_senders_signal_quality')) if match.group('good_senders_signal_quality') else None, + 'good_senders': float(match.group('good_senders')) if match.group('good_senders') else None, + 'good_and_bad_senders': float(match.group('good_and_bad_senders')) if match.group('good_and_bad_senders') else None} diff --git a/ogn/parser/parse_tracker.py b/ogn/parser/parse_tracker.py new file mode 100644 index 0000000..3ddf26b --- /dev/null +++ b/ogn/parser/parse_tracker.py @@ -0,0 +1,27 @@ +import re + +from ogn.parser.utils import fpm2ms +from ogn.parser.pattern import PATTERN_TRACKER_BEACON_POSITION, PATTERN_TRACKER_BEACON_STATUS + + +def parse_position(aprs_comment): + match = re.search(PATTERN_TRACKER_BEACON_POSITION, aprs_comment) + return {'address_type': int(match.group('details'), 16) & 0b00000011, + 'aircraft_type': (int(match.group('details'), 16) & 0b01111100) >> 2, + 'stealth': (int(match.group('details'), 16) & 0b10000000) >> 7 == 1, + 'address': match.group('id'), + 'climb_rate': int(match.group('climb_rate')) * fpm2ms if match.group('climb_rate') else None, + 'turn_rate': float(match.group('turn_rate')) if match.group('turn_rate') else None, + 'flightlevel': float(match.group('flight_level')) if match.group('flight_level') else None, + 'signal_quality': float(match.group('signal_quality')) if match.group('signal_quality') else None, + 'error_count': int(match.group('errors')) if match.group('errors') else None, + 'frequency_offset': float(match.group('frequency_offset')) if match.group('frequency_offset') else None, + 'gps_status': match.group('gps_accuracy') if match.group('gps_accuracy') else None, + 'software_version': float(match.group('flarm_software_version')) if match.group('flarm_software_version') else None, + 'hardware_version': int(match.group('flarm_hardware_version'), 16) if match.group('flarm_hardware_version') else None} + + +def parse_status(aprs_comment): + match = re.search(PATTERN_TRACKER_BEACON_STATUS, aprs_comment) + return {'voltage': float(match.group('voltage')) if match.group('voltage') else None, + 'temperature': float(match.group('temperature')) if match.group('temperature') else None} diff --git a/ogn/parser/pattern.py b/ogn/parser/pattern.py index dc33363..aadde63 100644 --- a/ogn/parser/pattern.py +++ b/ogn/parser/pattern.py @@ -1,7 +1,7 @@ import re -PATTERN_APRS_POSITION = re.compile(r"^(?P.+?)>(?P[A-Z0-9]+),((?P[A-Za-z0-9]+)\*)?.*,(?P.+?):/(?P