From fc276d7571ad25bba406dfc8e83d6e81f10c482a Mon Sep 17 00:00:00 2001 From: Sebastien Chaumontet Date: Wed, 2 Apr 2025 00:01:34 +0200 Subject: [PATCH] Add Microtrak APRS parser. (https://microtrak.fr/) --- CHANGELOG.md | 2 ++ ogn/parser/aprs_comment/microtrak_parser.py | 23 ++++++++++++++++++ ogn/parser/parse.py | 2 ++ ogn/parser/pattern.py | 4 ++++ tests/parser/test_parse_microtrak.py | 25 ++++++++++++++++++++ tests/parser/valid_beacon_data/microtrak.txt | 6 +++++ 6 files changed, 62 insertions(+) create mode 100644 ogn/parser/aprs_comment/microtrak_parser.py create mode 100644 tests/parser/test_parse_microtrak.py create mode 100644 tests/parser/valid_beacon_data/microtrak.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index ed2e145..bf15be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## unreleased - client: If no reference_timestamp provided use timestamp from APRS server (fixes #85) +- parser: Handle dst_calls 'OGFLR6' (protocol 6) and 'OGFLR7' (protocol 7) like 'OGFLR' (fixes #123) +- parser: Added support for OGNMTK (Microtrak) beacons ## 1.2.1: - 2021-06-06 - client: Added peer IP to log messages diff --git a/ogn/parser/aprs_comment/microtrak_parser.py b/ogn/parser/aprs_comment/microtrak_parser.py new file mode 100644 index 0000000..702a218 --- /dev/null +++ b/ogn/parser/aprs_comment/microtrak_parser.py @@ -0,0 +1,23 @@ +from ogn.parser.pattern import PATTERN_MICROTRAK_POSITION_COMMENT + +from .base import BaseParser + + +class MicrotrakParser(BaseParser): + def __init__(self): + self.beacon_type = 'microtrak' + self.position_pattern = PATTERN_MICROTRAK_POSITION_COMMENT + + def parse_position(self, aprs_comment): + match = self.position_pattern.match(aprs_comment) + + result = {} + if match.group('details'): + result.update({ + 'address_type': int(match.group('details'), 16) & 0b00000011, + 'aircraft_type': (int(match.group('details'), 16) & 0b00111100) >> 2, + 'no-tracking': (int(match.group('details'), 16) & 0b01000000) >> 6 == 1, + 'stealth': (int(match.group('details'), 16) & 0b10000000) >> 7 == 1, + 'address': match.group('address'), + }) + return result diff --git a/ogn/parser/parse.py b/ogn/parser/parse.py index c4d6b7e..8c798c9 100644 --- a/ogn/parser/parse.py +++ b/ogn/parser/parse.py @@ -17,6 +17,7 @@ from ogn.parser.aprs_comment.spider_parser import SpiderParser from ogn.parser.aprs_comment.spot_parser import SpotParser from ogn.parser.aprs_comment.inreach_parser import InreachParser from ogn.parser.aprs_comment.safesky_parser import SafeskyParser +from ogn.parser.aprs_comment.microtrak_parser import MicrotrakParser from ogn.parser.aprs_comment.generic_parser import GenericParser positions = {} @@ -167,6 +168,7 @@ dstcall_parser_mapping = {'APRS': OgnParser(), 'OGSPID': SpiderParser(), 'OGSPOT': SpotParser(), 'OGNSKY': SafeskyParser(), + 'OGNMTK': MicrotrakParser(), 'GENERIC': GenericParser(beacon_type='unknown'), } diff --git a/ogn/parser/pattern.py b/ogn/parser/pattern.py index dba25b6..1702caf 100644 --- a/ogn/parser/pattern.py +++ b/ogn/parser/pattern.py @@ -88,6 +88,10 @@ PATTERN_SAFESKY_POSITION_COMMENT = re.compile(r""" (?:gps(?P(?P(\d+))x(?P(\d+)))?)? """, re.VERBOSE | re.MULTILINE) +PATTERN_MICROTRAK_POSITION_COMMENT = re.compile(r""" + id(?P
[\dA-F]{2})(?P
[\dA-F]{6}?)\s? +""", re.VERBOSE | re.MULTILINE) + PATTERN_TRACKER_STATUS_COMMENT = re.compile(r""" h(?P[\d]{2})\s v(?P[\d]{2})\s? diff --git a/tests/parser/test_parse_microtrak.py b/tests/parser/test_parse_microtrak.py new file mode 100644 index 0000000..4c43c36 --- /dev/null +++ b/tests/parser/test_parse_microtrak.py @@ -0,0 +1,25 @@ +import unittest + +from ogn.parser.aprs_comment.microtrak_parser import MicrotrakParser + + +class TestStringMethods(unittest.TestCase): + def test_position_comment(self): + message = MicrotrakParser().parse_position("id21A8CBA8") + + self.assertEqual(message['address_type'], 1) + self.assertEqual(message['aircraft_type'], 8) + self.assertFalse(message['stealth']) + self.assertFalse(message['no-tracking']) + self.assertEqual(message['address'], "A8CBA8") + + def test_position_comment_relevant_keys_only(self): + # return only keys where we got informations + message = MicrotrakParser().parse_position("id21A8CBA8") + + self.assertIsNotNone(message) + self.assertEqual(sorted(message.keys()), sorted(['address_type', 'aircraft_type', 'stealth', 'address', 'no-tracking'])) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/parser/valid_beacon_data/microtrak.txt b/tests/parser/valid_beacon_data/microtrak.txt new file mode 100644 index 0000000..5bad4b3 --- /dev/null +++ b/tests/parser/valid_beacon_data/microtrak.txt @@ -0,0 +1,6 @@ +# The following beacons are example for Microtrak's APRS format +# source: https://github.com/glidernet/ogn-aprs-protocol +# +MTK895B2D>OGNMTK,qAS,Microtrak:/195300h4849.11N/00216.49E'000/000/A=000472 !W72! id07895B2D +MTK895B2D>OGNMTK,qAS,Microtrak:/195300h4849.11N/00216.49E'000/000/A=000472 !W72! id07895B2D +MTK6FC895>OGNMTK,qAS,Microtrak:/195346h4849.09N/00216.52E'000/000/A=000295 !W18! id076FC895