diff --git a/aprslib/parsing/common.py b/aprslib/parsing/common.py index 2cce3e0..fa5a465 100644 --- a/aprslib/parsing/common.py +++ b/aprslib/parsing/common.py @@ -136,17 +136,24 @@ def parse_data_extentions(body): parsed = {} # course speed bearing nrq + # Page 27 of the spec # format: 111/222/333/444text - match = re.findall(r"^([0-9 .]{3})/([0-9 .]{3})", body) + match = re.findall(r"^([0-9 \.]{3})/([0-9 \.]{3})", body) if match: cse, spd = match[0] body = body[7:] - parsed.update({'course': int(cse) if cse.isdigit() and 1 <= int(cse) <= 360 else 0}) - if spd.isdigit(): + if cse.isdigit() and cse != "000": + parsed.update({'course': int(cse) if 1 <= int(cse) <= 360 else 0}) + if spd.isdigit() and spd != "000": parsed.update({'speed': int(spd)*1.852}) - match = re.findall(r"^/([0-9 .]{3})/([0-9 .]{3})", body) + # DF Report format + # Page 29 of teh spec + match = re.findall(r"^/([0-9 \.]{3})/([0-9 \.]{3})", body) if match: + # cse=000 means stations is fixed, Page 29 of the spec + if cse == '000': + parsed.update({'course': 0}) brg, nrq = match[0] body = body[8:] if brg.isdigit(): diff --git a/aprslib/parsing/position.py b/aprslib/parsing/position.py index 24cdebd..e66c502 100644 --- a/aprslib/parsing/position.py +++ b/aprslib/parsing/position.py @@ -3,7 +3,7 @@ import re from aprslib import base91 from aprslib.exceptions import ParseError from aprslib.parsing import logger -from aprslib.parsing.common import parse_timestamp, parse_comment +from aprslib.parsing.common import parse_timestamp, parse_comment, parse_data_extentions from aprslib.parsing.weather import parse_weather_data __all__ = [ @@ -60,6 +60,11 @@ def parse_position(packet_type, body): # check comment for weather information # Page 62 of the spec if parsed['symbol'] == '_': + # attempt to parse winddir/speed + # Page 92 of the spec + body, result = parse_data_extentions(body) + parsed.update(result) + logger.debug("Attempting to parse weather report from comment") body, result = parse_weather_data(body) parsed.update({ diff --git a/tests/test_parse_common.py b/tests/test_parse_common.py index d64f07f..69c8d73 100644 --- a/tests/test_parse_common.py +++ b/tests/test_parse_common.py @@ -256,30 +256,47 @@ class DataExtentionsTC(unittest.TestCase): 'speed': 100*1.852, }) - def test_empty_course_speed(self): + def test_course_speed_spaces(self): body = " / /text" remaining, parsed = parse_data_extentions(body) self.assertEqual(remaining, '/text') - self.assertEqual(parsed, { - 'course': 0, - }) + self.assertEqual(parsed, {}) + def test_course_speed_dots(self): body = ".../.../text" remaining, parsed = parse_data_extentions(body) self.assertEqual(remaining, '/text') - self.assertEqual(parsed, { - 'course': 0, - }) + self.assertEqual(parsed, {}) + def test_course_speed_zeros(self): + body = "000/000/text" + remaining, parsed = parse_data_extentions(body) + + self.assertEqual(remaining, '/text') + self.assertEqual(parsed, {}) + + def test_course_speed_valid_chars_but_invalid_values(self): body = "22./33 /text" remaining, parsed = parse_data_extentions(body) self.assertEqual(remaining, '/text') - self.assertEqual(parsed, { - 'course': 0, - }) + self.assertEqual(parsed, {}) + + def test_course_speed_invalid_chars_spd(self): + body = "222/33a/text" + remaining, parsed = parse_data_extentions(body) + + self.assertEqual(remaining, '222/33a/text') + self.assertEqual(parsed, {}) + + def test_course_speed_invalid_chars_cse(self): + body = "22a/333/text" + remaining, parsed = parse_data_extentions(body) + + self.assertEqual(remaining, '22a/333/text') + self.assertEqual(parsed, {}) def test_empty_bearing_nrq(self): body = "111/100/ /...text" @@ -300,6 +317,17 @@ class DataExtentionsTC(unittest.TestCase): 'speed': 100*1.852, }) + def test_course_speed_bearing_nrq_empty_cse_speed(self): + body = "000/000/234/345text" + remaining, parsed = parse_data_extentions(body) + + self.assertEqual(remaining, 'text') + self.assertEqual(parsed, { + 'course': 0, + 'bearing': 234, + 'nrq': 345, + }) + def test_course_speed_bearing_nrq(self): body = "123/100/234/345text" remaining, parsed = parse_data_extentions(body) diff --git a/tests/test_parse_position.py b/tests/test_parse_position.py new file mode 100644 index 0000000..d051954 --- /dev/null +++ b/tests/test_parse_position.py @@ -0,0 +1,167 @@ +import unittest + +from aprslib.parsing import parse_position + +wind_multiplier = 0.44704 +mm_multiplier = 0.254 + +class ParsePositionDataExtAndWeather(unittest.TestCase): + def setUp(self): + self.maxDiff = None + + def test_position_packet_only_weather_valid(self): + packet_type = '@' + packet = "092345z4903.50N/07201.75W_g000t066r000p000...dUII" + expected = { + 'messagecapable': True, + 'raw_timestamp': '092345z', + 'timestamp': 1657410300, + 'format': 'uncompressed', + 'posambiguity': 0, + 'symbol': '_', + 'symbol_table': '/', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'comment': '...dUII', + 'weather': { + 'wind_gust': 0.0, + 'temperature': 18.88888888888889, + 'rain_1h': 0.0, + 'rain_24h': 0.0 + } + } + + _, result = parse_position(packet_type, packet) + self.assertEqual(expected, result) + + def test_position_packet_data_ext_and_weather_valid(self): + packet_type = '@' + packet = "092345z4903.50N/07201.75W_090/001g000t066r000p000...dUII" + expected = { + 'messagecapable': True, + 'raw_timestamp': '092345z', + 'timestamp': 1657410300, + 'format': 'uncompressed', + 'posambiguity': 0, + 'symbol': '_', + 'symbol_table': '/', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'course': 90, + 'speed': 1*1.852, + 'comment': '...dUII', + 'weather': { + 'wind_gust': 0.0, + 'temperature': 18.88888888888889, + 'rain_1h': 0.0, + 'rain_24h': 0.0 + } + } + + _, result = parse_position(packet_type, packet) + self.assertEqual(expected, result) + + def test_position_packet_optional_speed(self): + packet_type = '@' + packet = "092345z4903.50N/07201.75W_090/...g000t066r000p000...dUII" + expected = { + 'messagecapable': True, + 'raw_timestamp': '092345z', + 'timestamp': 1657410300, + 'format': 'uncompressed', + 'posambiguity': 0, + 'symbol': '_', + 'symbol_table': '/', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'course': 90, + 'comment': '...dUII', + 'weather': { + 'wind_gust': 0.0, + 'temperature': 18.88888888888889, + 'rain_1h': 0.0, + 'rain_24h': 0.0 + } + } + + _, result = parse_position(packet_type, packet) + self.assertEqual(expected, result) + + def test_position_packet_optional_course(self): + packet_type = '@' + packet = "092345z4903.50N/07201.75W_ /001g000t066r000p000...dUII" + expected = { + 'messagecapable': True, + 'raw_timestamp': '092345z', + 'timestamp': 1657410300, + 'format': 'uncompressed', + 'posambiguity': 0, + 'symbol': '_', + 'symbol_table': '/', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'speed': 1*1.852, + 'comment': '...dUII', + 'weather': { + 'wind_gust': 0.0, + 'temperature': 18.88888888888889, + 'rain_1h': 0.0, + 'rain_24h': 0.0 + } + } + + _, result = parse_position(packet_type, packet) + self.assertEqual(expected, result) + + def test_position_packet_optional_speed_and_course(self): + packet_type = '@' + packet = "092345z4903.50N/07201.75W_.../...g000t066r000p000...dUII" + expected = { + 'messagecapable': True, + 'raw_timestamp': '092345z', + 'timestamp': 1657410300, + 'format': 'uncompressed', + 'posambiguity': 0, + 'symbol': '_', + 'symbol_table': '/', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'comment': '...dUII', + 'weather': { + 'wind_gust': 0.0, + 'temperature': 18.88888888888889, + 'rain_1h': 0.0, + 'rain_24h': 0.0 + } + } + + _, result = parse_position(packet_type, packet) + self.assertEqual(expected, result) + def test_position_packet_optional_course(self): + packet_type = '@' + packet = "092345z4903.50N/07201.75W_ /001g000t066r000p000...dUII" + expected = { + 'messagecapable': True, + 'raw_timestamp': '092345z', + 'timestamp': 1657410300, + 'format': 'uncompressed', + 'posambiguity': 0, + 'symbol': '_', + 'symbol_table': '/', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'speed': 1*1.852, + 'comment': '...dUII', + 'weather': { + 'wind_gust': 0.0, + 'temperature': 18.88888888888889, + 'rain_1h': 0.0, + 'rain_24h': 0.0 + } + } + + _, result = parse_position(packet_type, packet) + self.assertEqual(expected, result) + +if __name__ == '__main__': + unittest.main()