From e49c312efc25e34f772fc827f589e95b5b88c468 Mon Sep 17 00:00:00 2001 From: Hemna Date: Thu, 13 Jul 2023 12:30:35 -0400 Subject: [PATCH] Fix issue 80: Weather packet decoding. Weather packets were being decoded incorrectly. Over the air packets for weather are in wind miles per hour. Rain is in hundreths of an inch according to the official spec. This patch changes the weather packet output to include the wind_speed and wind_direction as part of the weather dict in the packet instead of course and speed. According to the APRS Spect CSE/SPD for weather packets, which are denoted with the _ character are supposed to be used for wind speed and wind direction. "The on-air APRS WX protocols, however, still will be in MPH and F." "RAIN VALUES: Rain is counted in increments of 0.1 or 0.01 inch or 1mm. but reports all values in 0.01 inches over the air." References: http://www.aprs.org/APRS-docs/WX.TXT --- aprslib/parsing/common.py | 3 ++- aprslib/parsing/position.py | 11 ++++++++++- aprslib/parsing/weather.py | 6 ++++-- tests/test_parse_common.py | 8 ++++---- tests/test_parse_position.py | 20 ++++++++++---------- tests/test_parse_weather_data.py | 4 ++-- 6 files changed, 32 insertions(+), 20 deletions(-) diff --git a/aprslib/parsing/common.py b/aprslib/parsing/common.py index fa5a465..246e8d7 100644 --- a/aprslib/parsing/common.py +++ b/aprslib/parsing/common.py @@ -138,6 +138,7 @@ def parse_data_extentions(body): # course speed bearing nrq # Page 27 of the spec # format: 111/222/333/444text + # Speed is in mph match = re.findall(r"^([0-9 \.]{3})/([0-9 \.]{3})", body) if match: cse, spd = match[0] @@ -145,7 +146,7 @@ def parse_data_extentions(body): 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}) + parsed.update({'speed': int(spd)}) # DF Report format # Page 29 of teh spec diff --git a/aprslib/parsing/position.py b/aprslib/parsing/position.py index e66c502..64752d6 100644 --- a/aprslib/parsing/position.py +++ b/aprslib/parsing/position.py @@ -63,10 +63,19 @@ def parse_position(packet_type, body): # attempt to parse winddir/speed # Page 92 of the spec body, result = parse_data_extentions(body) - parsed.update(result) + wind_speed = result.get("speed") + wind_direction = result.get("course") logger.debug("Attempting to parse weather report from comment") body, result = parse_weather_data(body) + if wind_speed: + result.update({ + 'wind_speed': wind_speed, + }) + if wind_direction: + result.update({ + 'wind_direction': wind_direction, + }) parsed.update({ 'comment': body.strip(' '), 'weather': result, diff --git a/aprslib/parsing/weather.py b/aprslib/parsing/weather.py index add8db2..1c8cb8c 100644 --- a/aprslib/parsing/weather.py +++ b/aprslib/parsing/weather.py @@ -7,8 +7,10 @@ ] # constants -wind_multiplier = 0.44704 -rain_multiplier = 0.254 +# wind is in miles per hour +wind_multiplier = 1 +# Spec 1.1 Rain is in hundredths of an inch. +rain_multiplier = 0.01 key_map = { 'g': 'wind_gust', diff --git a/tests/test_parse_common.py b/tests/test_parse_common.py index 69c8d73..9f4ac08 100644 --- a/tests/test_parse_common.py +++ b/tests/test_parse_common.py @@ -253,7 +253,7 @@ def test_course_speed(self): self.assertEqual(remaining, '/text') self.assertEqual(parsed, { 'course': 123, - 'speed': 100*1.852, + 'speed': 100, }) def test_course_speed_spaces(self): @@ -305,7 +305,7 @@ def test_empty_bearing_nrq(self): self.assertEqual(remaining, 'text') self.assertEqual(parsed, { 'course': 111, - 'speed': 100*1.852, + 'speed': 100, }) body = "111/100/2 /33.text" @@ -314,7 +314,7 @@ def test_empty_bearing_nrq(self): self.assertEqual(remaining, 'text') self.assertEqual(parsed, { 'course': 111, - 'speed': 100*1.852, + 'speed': 100, }) def test_course_speed_bearing_nrq_empty_cse_speed(self): @@ -335,7 +335,7 @@ def test_course_speed_bearing_nrq(self): self.assertEqual(remaining, 'text') self.assertEqual(parsed, { 'course': 123, - 'speed': 100*1.852, + 'speed': 100, 'bearing': 234, 'nrq': 345, }) diff --git a/tests/test_parse_position.py b/tests/test_parse_position.py index 4a2287b..d8d2de9 100644 --- a/tests/test_parse_position.py +++ b/tests/test_parse_position.py @@ -30,7 +30,7 @@ def test_position_packet_only_weather_valid(self): 'wind_gust': 0.0, 'temperature': 18.88888888888889, 'rain_1h': 0.0, - 'rain_24h': 0.0 + 'rain_24h': 0.0, } } @@ -50,14 +50,14 @@ def test_position_packet_data_ext_and_weather_valid(self): '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 + 'rain_24h': 0.0, + 'wind_direction': 90, + 'wind_speed': 1, } } @@ -77,13 +77,13 @@ def test_position_packet_optional_speed(self): '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 + 'rain_24h': 0.0, + 'wind_direction': 90 } } @@ -103,13 +103,13 @@ def test_position_packet_optional_course(self): '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 + 'rain_24h': 0.0, + 'wind_speed': 1, } } @@ -153,13 +153,13 @@ def test_position_packet_optional_course(self): '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 + 'rain_24h': 0.0, + 'wind_speed': 1, } } diff --git a/tests/test_parse_weather_data.py b/tests/test_parse_weather_data.py index 7793525..18f9aa3 100644 --- a/tests/test_parse_weather_data.py +++ b/tests/test_parse_weather_data.py @@ -3,8 +3,8 @@ from aprslib.parsing import parse_weather_data from aprslib.parsing import parse -wind_multiplier = 0.44704 -mm_multiplier = 0.254 +wind_multiplier = 1 +mm_multiplier = 0.010 class ParseCommentWeather(unittest.TestCase): def setUp(self):