kopia lustrzana https://github.com/glidernet/python-ogn-client
commit
5b131855a3
|
@ -1,6 +1,7 @@
|
|||
# CHANGELOG
|
||||
|
||||
## Unreleased
|
||||
- parser: Added support for OGN v0.2.6 aircraft and receiver beacons
|
||||
|
||||
## 0.6.0 - 2016-10-21
|
||||
- parser: Added support for OGN v0.2.5 receiver beacons
|
||||
|
|
|
@ -24,8 +24,8 @@ def parse_aprs(message, reference_date=None, reference_time=None):
|
|||
'longitude': parseAngle(match_position.group('longitude') + (match_position.group('longitude_enhancement') or '0')) *
|
||||
(-1 if match_position.group('longitude_sign') == 'W' else 1),
|
||||
'symbolcode': match_position.group('symbol'),
|
||||
'track': int(match_position.group('course')) if match_position.group('course_extension') else 0,
|
||||
'ground_speed': int(match_position.group('ground_speed')) * kts2kmh if match_position.group('ground_speed') else 0,
|
||||
'track': int(match_position.group('course')) if match_position.group('course_extension') else None,
|
||||
'ground_speed': int(match_position.group('ground_speed')) * kts2kmh if match_position.group('ground_speed') else None,
|
||||
'altitude': int(match_position.group('altitude')) * feet2m,
|
||||
'comment': match_position.group('comment')}
|
||||
|
||||
|
@ -47,16 +47,16 @@ def parse_ogn_aircraft_beacon(aprs_comment):
|
|||
'aircraft_type': (int(ac_match.group('details'), 16) & 0b01111100) >> 2,
|
||||
'stealth': (int(ac_match.group('details'), 16) & 0b10000000) >> 7 == 1,
|
||||
'address': ac_match.group('id'),
|
||||
'climb_rate': int(ac_match.group('climb_rate')) * fpm2ms,
|
||||
'turn_rate': float(ac_match.group('turn_rate')),
|
||||
'climb_rate': int(ac_match.group('climb_rate')) * fpm2ms if ac_match.group('climb_rate') else None,
|
||||
'turn_rate': float(ac_match.group('turn_rate')) if ac_match.group('turn_rate') else None,
|
||||
'flightlevel': float(ac_match.group('flight_level')) if ac_match.group('flight_level') else None,
|
||||
'signal_quality': float(ac_match.group('signal_quality')),
|
||||
'error_count': float(ac_match.group('errors')),
|
||||
'frequency_offset': float(ac_match.group('frequency_offset')),
|
||||
'gps_status': ac_match.group('gps_accuracy'),
|
||||
'signal_quality': float(ac_match.group('signal_quality')) if ac_match.group('signal_quality') else None,
|
||||
'error_count': float(ac_match.group('errors')) if ac_match.group('signal_quality') else None,
|
||||
'frequency_offset': float(ac_match.group('frequency_offset')) if ac_match.group('frequency_offset') else None,
|
||||
'gps_status': ac_match.group('gps_accuracy') if ac_match.group('gps_accuracy') else None,
|
||||
'software_version': float(ac_match.group('flarm_software_version')) if ac_match.group('flarm_software_version') else None,
|
||||
'hardware_version': int(ac_match.group('flarm_hardware_version'), 16) if ac_match.group('flarm_hardware_version') else None,
|
||||
'real_address': ac_match.group('flarm_id'),
|
||||
'real_address': ac_match.group('flarm_id') if ac_match.group('flarm_id') else None,
|
||||
'signal_power': float(ac_match.group('signal_power')) if ac_match.group('signal_power') else None}
|
||||
else:
|
||||
return None
|
||||
|
@ -77,12 +77,12 @@ def parse_ogn_receiver_beacon(aprs_comment):
|
|||
'cpu_temp': float(rec_match.group('cpu_temperature')) if rec_match.group('cpu_temperature') else None,
|
||||
'senders_visible': int(rec_match.group('visible_senders')) if rec_match.group('visible_senders') else None,
|
||||
'senders_total': int(rec_match.group('senders')) if rec_match.group('senders') else None,
|
||||
'rec_crystal_correction': int(rec_match.group('rf_correction_manual')) if rec_match.group('rf_correction_manual') else 0,
|
||||
'rec_crystal_correction_fine': float(rec_match.group('rf_correction_automatic')) if rec_match.group('rf_correction_automatic') else 0.0,
|
||||
'rec_input_noise': float(rec_match.group('signal')) if rec_match.group('signal') else None,
|
||||
'senders_signal': float(rec_match.group('senders_signal')) if rec_match.group('senders_signal') else None,
|
||||
'rec_crystal_correction': int(rec_match.group('rf_correction_manual')) if rec_match.group('rf_correction_manual') else None,
|
||||
'rec_crystal_correction_fine': float(rec_match.group('rf_correction_automatic')) if rec_match.group('rf_correction_automatic') else None,
|
||||
'rec_input_noise': float(rec_match.group('signal_quality')) if rec_match.group('signal_quality') else None,
|
||||
'senders_signal': float(rec_match.group('senders_signal_quality')) if rec_match.group('senders_signal_quality') else None,
|
||||
'senders_messages': float(rec_match.group('senders_messages')) if rec_match.group('senders_messages') else None,
|
||||
'good_senders_signal': float(rec_match.group('good_senders_signal')) if rec_match.group('good_senders_signal') else None,
|
||||
'good_senders_signal': float(rec_match.group('good_senders_signal_quality')) if rec_match.group('good_senders_signal_quality') else None,
|
||||
'good_senders': float(rec_match.group('good_senders')) if rec_match.group('good_senders') else None,
|
||||
'good_and_bad_senders': float(rec_match.group('good_and_bad_senders')) if rec_match.group('good_and_bad_senders') else None}
|
||||
else:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import re
|
||||
|
||||
|
||||
PATTERN_APRS_POSITION = re.compile(r"^(?P<callsign>.+?)>(?P<dstcall>[A-Z0-9]+),.+,(?P<receiver>.+?):/(?P<time>\d{6})+h(?P<latitude>\d{4}\.\d{2})(?P<latitude_sign>N|S)(?P<symbol_table>.)(?P<longitude>\d{5}\.\d{2})(?P<longitude_sign>E|W)(?P<symbol>.)(?P<course_extension>(?P<course>\d{3})/(?P<ground_speed>\d{3}))?/A=(?P<altitude>\d{6})(?P<pos_extension>\s!W((?P<latitude_enhancement>\d)(?P<longitude_enhancement>\d))!)?\s(?P<comment>.*)$")
|
||||
PATTERN_APRS_POSITION = re.compile(r"^(?P<callsign>.+?)>(?P<dstcall>[A-Z0-9]+),.+,(?P<receiver>.+?):/(?P<time>\d{6})+h(?P<latitude>\d{4}\.\d{2})(?P<latitude_sign>N|S)(?P<symbol_table>.)(?P<longitude>\d{5}\.\d{2})(?P<longitude_sign>E|W)(?P<symbol>.)(?P<course_extension>(?P<course>\d{3})/(?P<ground_speed>\d{3}))?/A=(?P<altitude>\d{6})(?P<pos_extension>\s!W((?P<latitude_enhancement>\d)(?P<longitude_enhancement>\d))!)?(?:\s(?P<comment>.*))?$")
|
||||
PATTERN_APRS_STATUS = re.compile(r"(?P<callsign>.+?)>(?P<dstcall>[A-Z0-9]+),.+,(?P<receiver>.+?):>(?P<time>\d{6})+h\s(?P<comment>.*)$")
|
||||
|
||||
# The following regexp patterns are part of the ruby ogn-client.
|
||||
|
@ -9,7 +9,7 @@ PATTERN_APRS_STATUS = re.compile(r"(?P<callsign>.+?)>(?P<dstcall>[A-Z0-9]+),.+,(
|
|||
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2015 Sven Schwyn
|
||||
# Copyright (c) 2015-2017 Sven Schwyn
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -32,35 +32,35 @@ PATTERN_APRS_STATUS = re.compile(r"(?P<callsign>.+?)>(?P<dstcall>[A-Z0-9]+),.+,(
|
|||
PATTERN_RECEIVER_BEACON = re.compile(r"""
|
||||
(?:
|
||||
v(?P<version>\d+\.\d+\.\d+)
|
||||
\.?(?P<platform>.+?)?
|
||||
(?:\.(?P<platform>.+?))?
|
||||
\s)?
|
||||
CPU:(?P<cpu_load>[\d.]+)\s
|
||||
RAM:(?P<ram_free>[\d.]+)\/(?P<ram_total>[\d.]+)MB\s
|
||||
NTP:(?P<ntp_offset>[\d.]+)ms\/(?P<ntp_correction>[+-][\d.]+)ppm\s
|
||||
RAM:(?P<ram_free>[\d.]+)/(?P<ram_total>[\d.]+)MB\s
|
||||
NTP:(?P<ntp_offset>[\d.]+)ms/(?P<ntp_correction>[+-][\d.]+)ppm\s
|
||||
(?:(?P<voltage>[\d.]+)V\s)?
|
||||
(?:(?P<amperage>[\d.]+)A\s)?
|
||||
(?:(?P<cpu_temperature>[+-][\d.]+)C\s*)?
|
||||
(?:(?P<visible_senders>\d+)\/(?P<senders>\d+)Acfts\[1h\]\s*)?
|
||||
(?:(?P<visible_senders>\d+)/(?P<senders>\d+)Acfts\[1h\]\s*)?
|
||||
(?:RF:
|
||||
(?:
|
||||
(?P<rf_correction_manual>[+-][\d]+)
|
||||
(?P<rf_correction_automatic>[+-][\d.]+)ppm\/
|
||||
(?P<rf_correction_automatic>[+-][\d.]+)ppm/
|
||||
)?
|
||||
(?P<signal>[+-][\d.]+)dB
|
||||
(?:\/(?P<senders_signal>[+-][\d.]+)dB@10km\[(?P<senders_messages>\d+)\])?
|
||||
(?:\/(?P<good_senders_signal>[+-][\d.]+)dB@10km\[(?P<good_senders>\d+)\/(?P<good_and_bad_senders>\d+)\])?
|
||||
(?P<signal_quality>[+-][\d.]+)dB
|
||||
(?:/(?P<senders_signal_quality>[+-][\d.]+)dB@10km\[(?P<senders_messages>\d+)\])?
|
||||
(?:/(?P<good_senders_signal_quality>[+-][\d.]+)dB@10km\[(?P<good_senders>\d+)/(?P<good_and_bad_senders>\d+)\])?
|
||||
)?
|
||||
""", re.VERBOSE | re.MULTILINE)
|
||||
|
||||
|
||||
PATTERN_AIRCRAFT_BEACON = re.compile(r"""
|
||||
id(?P<details>\w{2})(?P<id>\w+?)\s
|
||||
(?P<climb_rate>[+-]\d+?)fpm\s
|
||||
(?P<turn_rate>[+-][\d.]+?)rot\s
|
||||
id(?P<details>\w{2})(?P<id>\w{6}?)\s?
|
||||
(?:(?P<climb_rate>[+-]\d+?)fpm\s)?
|
||||
(?:(?P<turn_rate>[+-][\d.]+?)rot\s)?
|
||||
(?:FL(?P<flight_level>[\d.]+)\s)?
|
||||
(?P<signal_quality>[\d.]+?)dB\s
|
||||
(?P<errors>\d+)e\s
|
||||
(?P<frequency_offset>[+-][\d.]+?)kHz\s?
|
||||
(?:(?P<signal_quality>[\d.]+?)dB\s)?
|
||||
(?:(?P<errors>\d+)e\s)?
|
||||
(?:(?P<frequency_offset>[+-][\d.]+?)kHz\s?)?
|
||||
(?:gps(?P<gps_accuracy>\d+x\d+)\s?)?
|
||||
(?:s(?P<flarm_software_version>[\d.]+)\s?)?
|
||||
(?:h(?P<flarm_hardware_version>[\dA-F]{2})\s?)?
|
||||
|
|
|
@ -20,10 +20,13 @@ class TestStringMethods(unittest.TestCase):
|
|||
self.assertEqual(aircraft_beacon['frequency_offset'], 51.2)
|
||||
self.assertEqual(aircraft_beacon['gps_status'], '4x5')
|
||||
|
||||
# self.assertEqual(len(aircraft_beacon['heared_aircraft_addresses']), 3)
|
||||
# self.assertEqual(aircraft_beacon['heared_aircraft_addresses'][0], '1084')
|
||||
# self.assertEqual(aircraft_beacon['heared_aircraft_addresses'][1], 'B597')
|
||||
# self.assertEqual(aircraft_beacon['heared_aircraft_addresses'][2], 'B598')
|
||||
@unittest.skip("heared aircrafts not supported yet")
|
||||
def test_hear(self):
|
||||
aircraft_beacon = parse_ogn_aircraft_beacon("id0ADDA5BA -454fpm -1.1rot 8.8dB 0e +51.2kHz gps4x5 hear1084 hearB597 hearB598")
|
||||
self.assertEqual(len(aircraft_beacon['heared_aircraft_addresses']), 3)
|
||||
self.assertEqual(aircraft_beacon['heared_aircraft_addresses'][0], '1084')
|
||||
self.assertEqual(aircraft_beacon['heared_aircraft_addresses'][1], 'B597')
|
||||
self.assertEqual(aircraft_beacon['heared_aircraft_addresses'][2], 'B598')
|
||||
|
||||
def test_stealth(self):
|
||||
aircraft_beacon = parse_ogn_aircraft_beacon("id0ADD1234 -454fpm -1.1rot 8.8dB 0e +51.2kHz gps4x5 hear1084 hearB597 hearB598")
|
||||
|
@ -49,6 +52,13 @@ class TestStringMethods(unittest.TestCase):
|
|||
|
||||
self.assertEqual(aircraft_beacon['signal_power'], 7.4)
|
||||
|
||||
def test_v026(self):
|
||||
# from 0.2.6 it is sufficent we have only the ID, climb and turn rate or just the ID
|
||||
aircraft_beacon_triple = parse_ogn_aircraft_beacon("id093D0930 +000fpm +0.0rot")
|
||||
aircraft_beacon_single = parse_ogn_aircraft_beacon("id093D0930")
|
||||
|
||||
self.assertIsNotNone(aircraft_beacon_triple)
|
||||
self.assertIsNotNone(aircraft_beacon_single)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -45,5 +45,13 @@ class TestStringMethods(unittest.TestCase):
|
|||
self.assertEqual(message['timestamp'].strftime('%H:%M:%S'), "09:34:56")
|
||||
self.assertEqual(message['comment'], "this is a comment")
|
||||
|
||||
def test_v026(self):
|
||||
# from 0.2.6 the ogn comment of a receiver beacon is just optional
|
||||
raw_message = "Ulrichamn>APRS,TCPIP*,qAC,GLIDERN1:/085616h5747.30NI01324.77E&/A=001322"
|
||||
message = parse_aprs(raw_message, reference_date=datetime(2015, 1, 1, 8, 56, 0))
|
||||
|
||||
self.assertEqual(message['comment'], None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -5,7 +5,7 @@ from datetime import datetime
|
|||
from time import sleep
|
||||
|
||||
from ogn.parser.parse import parse_aprs, parse_ogn_beacon
|
||||
from ogn.parser.exceptions import AprsParseError, OgnParseError
|
||||
from ogn.parser.exceptions import AprsParseError
|
||||
|
||||
|
||||
class TestStringMethods(unittest.TestCase):
|
||||
|
@ -14,7 +14,8 @@ class TestStringMethods(unittest.TestCase):
|
|||
for line in f:
|
||||
if not line[0] == '#':
|
||||
aprs = parse_aprs(line, datetime(2015, 4, 10, 17, 0))
|
||||
parse_ogn_beacon(aprs['comment'])
|
||||
if aprs['comment']:
|
||||
parse_ogn_beacon(aprs['comment'])
|
||||
|
||||
def test_fail_none(self):
|
||||
with self.assertRaises(TypeError):
|
||||
|
@ -28,18 +29,6 @@ class TestStringMethods(unittest.TestCase):
|
|||
with self.assertRaises(AprsParseError):
|
||||
parse_aprs("Lachens>APRS,TCPIwontbeavalidstring")
|
||||
|
||||
def test_incomplete_device_string(self):
|
||||
with self.assertRaises(OgnParseError):
|
||||
aprs = parse_aprs("ICA4B0E3A>APRS,qAS,Letzi:/072319h4711.75N\\00802.59E^327/149/A=006498 id154B0E3A -395",
|
||||
datetime(2015, 4, 10, 7, 24))
|
||||
parse_ogn_beacon(aprs['comment'])
|
||||
|
||||
def test_incomplete_receiver_string(self):
|
||||
with self.assertRaises(OgnParseError):
|
||||
aprs = parse_aprs("Lachens>APRS,TCPIP*,qAC,GLIDERN2:/165334h4344.70NI00639.19E&/A=005435 v0.2.1 CPU:0.3 RAM:1764.4/21",
|
||||
datetime(2015, 4, 10, 16, 54))
|
||||
parse_ogn_beacon(aprs['comment'])
|
||||
|
||||
@mock.patch('ogn.parser.parse.createTimestamp')
|
||||
def test_default_reference_date(self, createTimestamp_mock):
|
||||
valid_aprs_string = "Lachens>APRS,TCPIP*,qAC,GLIDERN2:/165334h4344.70NI00639.19E&/A=005435 v0.2.1 CPU:0.3 RAM:1764.4/21"
|
||||
|
|
|
@ -22,10 +22,12 @@ class TestStringMethods(unittest.TestCase):
|
|||
self.assertEqual(receiver_beacon['free_ram'], 669.9)
|
||||
self.assertEqual(receiver_beacon['total_ram'], 887.7)
|
||||
self.assertEqual(receiver_beacon['ntp_error'], 1.0)
|
||||
self.assertEqual(receiver_beacon['rec_crystal_correction'], 0.0)
|
||||
self.assertEqual(receiver_beacon['rec_crystal_correction_fine'], 0.0)
|
||||
self.assertEqual(receiver_beacon['rec_input_noise'], 0.06)
|
||||
|
||||
# parts not available set to None
|
||||
self.assertEqual(receiver_beacon['rec_crystal_correction'], None)
|
||||
self.assertEqual(receiver_beacon['rec_crystal_correction_fine'], None)
|
||||
|
||||
def test_v025(self):
|
||||
receiver_beacon = parse_ogn_receiver_beacon("v0.2.5.RPI-GPU CPU:0.8 RAM:287.3/458.7MB NTP:1.0ms/-6.4ppm 5.016V 0.534A +51.9C RF:+55+0.4ppm/-0.67dB/+10.8dB@10km[57282]")
|
||||
self.assertEqual(receiver_beacon['voltage'], 5.016)
|
||||
|
|
|
@ -25,4 +25,10 @@ Arnsberg>APRS,TCPIP*,qAC,GLIDERN1:>042146h v0.2.5.ARM CPU:0.4 RAM:764.9/970.8MB
|
|||
CNF3a>APRS,TCPIP*,qAC,GLIDERN3:/042143h4529.25NI07505.65W&/A=000259 v0.2.5.ARM CPU:0.6 RAM:514.6/970.8MB NTP:4.5ms/-1.5ppm +27.2C RF:+0-0.4ppm/+18.69dB
|
||||
CNF3a>APRS,TCPIP*,qAC,GLIDERN3:>042143h v0.2.5.ARM CPU:0.6 RAM:514.6/970.8MB NTP:4.5ms/-1.5ppm +27.2C 0/0Acfts[1h] RF:+0-0.4ppm/+18.69dB/+13.0dB@10km[104282]/+9.7dB@10km[2/3]
|
||||
VITACURA2>APRS,TCPIP*,qAC,GLIDERN3:/042136h3322.81SI07034.95W&/A=002345 v0.2.5.ARM CPU:0.3 RAM:695.0/970.5MB NTP:0.6ms/-5.7ppm +51.5C RF:+0-0.0ppm/+1.32dB
|
||||
VITACURA2>APRS,TCPIP*,qAC,GLIDERN3:>042136h v0.2.5.ARM CPU:0.3 RAM:695.0/970.5MB NTP:0.6ms/-5.7ppm +52.1C 0/0Acfts[1h] RF:+0-0.0ppm/+1.32dB/+2.1dB@10km[193897]/+9.0dB@10km[10/20]
|
||||
VITACURA2>APRS,TCPIP*,qAC,GLIDERN3:>042136h v0.2.5.ARM CPU:0.3 RAM:695.0/970.5MB NTP:0.6ms/-5.7ppm +52.1C 0/0Acfts[1h] RF:+0-0.0ppm/+1.32dB/+2.1dB@10km[193897]/+9.0dB@10km[10/20]
|
||||
### since 0.2.6
|
||||
### ... the ogn comment of a receiver beacon is just optional
|
||||
Ulrichamn>APRS,TCPIP*,qAC,GLIDERN1:/085616h5747.30NI01324.77E&/A=001322
|
||||
### ... a aircraft beacon needs just an ID, climb rate and turn rate or just the ID
|
||||
ICA3ECE59>APRS,qAS,GLDRTR:/171254h5144.78N/00616.67E'263/000/A=000075 id093D0930 +000fpm +0.0rot
|
||||
ICA3ECE59>APRS,qAS,GLDRTR:/171254h5144.78N/00616.67E'263/000/A=000075 id053ECE59
|
Ładowanie…
Reference in New Issue