From fc835ea851d8f655a082e718741f415d39f2513a Mon Sep 17 00:00:00 2001 From: John Ronan Date: Thu, 17 Dec 2015 18:29:22 +0000 Subject: [PATCH 1/4] Initial attempt at parsing weather data --- aprslib/parsing.py | 145 +++++++++++++++- tests/test_parse_comment_weather.py | 254 ++++++++++++++++++++++++++++ 2 files changed, 393 insertions(+), 6 deletions(-) create mode 100644 tests/test_parse_comment_weather.py diff --git a/aprslib/parsing.py b/aprslib/parsing.py index b977129..c402c16 100644 --- a/aprslib/parsing.py +++ b/aprslib/parsing.py @@ -243,10 +243,16 @@ def parse(packet): logger.debug("Parsed as normal position report") else: raise ParseError("invalid format") - - # decode comment - body, result = _parse_comment(body) - parsed.update(result) + # check comment for weather information + # Page 62 of the spec + if parsed['symbol'] == '_': + logger.debug("Attempting to parse weather report from comment") + body, result = _parse_comment_weather(body) + parsed.update(result) + else: + # decode comment + body, result = _parse_comment(body) + parsed.update(result) if packet_type == ';': parsed.update({ @@ -396,9 +402,8 @@ def _parse_timestamp(body, packet_type=''): def _parse_comment(body): parsed = {} - # attempt to parse remaining part of the packet (comment field) - # try CRS/SPD/ + # try CRS/SPD match = re.findall(r"^([0-9]{3})/([0-9]{3})", body) if match: cse, spd = match[0] @@ -926,3 +931,131 @@ def _parse_normal(body): }) return (body, parsed) + + +def _parse_comment_weather(body): + wind_multiplier = 0.44704 + rain_multiplier = 0.254 + + parsed = {} + match = re.findall(r"^([0-9]{3})/([0-9]{3})", body) + if match: + wind_direction, wind_speed = match[0] + parsed.update({ + 'wind_direction': int(wind_direction), + # mph to meters per second + 'wind_speed': float(int(wind_speed) * wind_multiplier) + }) + + match = re.findall(r"g([0-9]{3})", body) + if match: + wind_gust = match[0] + parsed.update({ + # mph to meters per second + 'wind_gust': float(int(wind_gust) * wind_multiplier) + }) + + # Positionless Weather report + match = re.findall(r"c([0-9]{3})s([0-9]{3})", body) + if match: + wind_direction, wind_speed = match[0] + parsed.update({ + 'wind_direction': int(wind_direction), + # mph to meters per second + 'wind_speed': float(int(wind_speed) * wind_multiplier) + }) + + match = re.findall(r"(? Date: Thu, 7 Jan 2016 15:23:49 +0000 Subject: [PATCH 2/4] refactored wx parsing --- aprslib/parsing.py | 156 +++++++--------------------- tests/test_parse_comment_weather.py | 110 +++++++++----------- 2 files changed, 89 insertions(+), 177 deletions(-) diff --git a/aprslib/parsing.py b/aprslib/parsing.py index c402c16..b533077 100644 --- a/aprslib/parsing.py +++ b/aprslib/parsing.py @@ -248,7 +248,7 @@ def parse(packet): if parsed['symbol'] == '_': logger.debug("Attempting to parse weather report from comment") body, result = _parse_comment_weather(body) - parsed.update(result) + parsed.update({'weather': result}) else: # decode comment body, result = _parse_comment(body) @@ -937,125 +937,49 @@ def _parse_comment_weather(body): wind_multiplier = 0.44704 rain_multiplier = 0.254 + key_map = { + 'g': 'wind_gust', + 'c': 'wind_direction', + 't': 'temperature', + 'S': 'wind_speed', + 'r': 'rain_1h', + 'p': 'rain_24h', + 'P': 'rain_since_midnight', + 'h': 'humidity', + 'b': 'pressure', + 'l': 'luminosity', + 'L': 'luminosity', + 's': 'snow', + '#': 'rain_raw', + } + val_map = { + 'g': lambda x: int(x) * wind_multiplier, + 'c': lambda x: int(x), + 'S': lambda x: int(x) * wind_multiplier, + 't': lambda x: (float(x) - 32) / 1.8, + 'r': lambda x: int(x) * rain_multiplier, + 'p': lambda x: int(x) * rain_multiplier, + 'P': lambda x: int(x) * rain_multiplier, + 'h': lambda x: int(x), + 'b': lambda x: float(x) / 10, + 'l': lambda x: int(x) + 1000, + 'L': lambda x: int(x), + 's': lambda x: float(x) * 25.4, + '#': lambda x: int(x), + } + parsed = {} - match = re.findall(r"^([0-9]{3})/([0-9]{3})", body) - if match: - wind_direction, wind_speed = match[0] - parsed.update({ - 'wind_direction': int(wind_direction), - # mph to meters per second - 'wind_speed': float(int(wind_speed) * wind_multiplier) - }) - match = re.findall(r"g([0-9]{3})", body) - if match: - wind_gust = match[0] - parsed.update({ - # mph to meters per second - 'wind_gust': float(int(wind_gust) * wind_multiplier) - }) + # parse weather data + body = re.sub(r"^([0-9]{3})/([0-9]{3})", "c\\1s\\2", body) + body = body.replace('s', 'S', 1) - # Positionless Weather report - match = re.findall(r"c([0-9]{3})s([0-9]{3})", body) - if match: - wind_direction, wind_speed = match[0] - parsed.update({ - 'wind_direction': int(wind_direction), - # mph to meters per second - 'wind_speed': float(int(wind_speed) * wind_multiplier) - }) + data = re.findall(r"([cSgtrpPlLs#]\d{3}|t-\d{2}|h\d{2}|b\d{5}|s\.\d{2}|s\d\.\d)", body) + data = map(lambda x: (key_map[x[0]] , val_map[x[0]](x[1:])), data) - match = re.findall(r"(? Date: Thu, 7 Jan 2016 15:37:36 +0000 Subject: [PATCH 3/4] put remaining text in 'comment' for wx prase --- aprslib/parsing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aprslib/parsing.py b/aprslib/parsing.py index b533077..5171f2d 100644 --- a/aprslib/parsing.py +++ b/aprslib/parsing.py @@ -248,7 +248,10 @@ def parse(packet): if parsed['symbol'] == '_': logger.debug("Attempting to parse weather report from comment") body, result = _parse_comment_weather(body) - parsed.update({'weather': result}) + parsed.update({ + 'comment': body.strip(' '), + 'weather': result, + }) else: # decode comment body, result = _parse_comment(body) From dc66940fb81719ad8a82a250e28b94a38df88848 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Sat, 9 Jan 2016 14:53:31 +0000 Subject: [PATCH 4/4] added parsing of positionless wx reports + tests --- aprslib/parsing.py | 24 +++- ..._weather.py => test_parse_weather_data.py} | 114 ++++++++++-------- 2 files changed, 86 insertions(+), 52 deletions(-) rename tests/{test_parse_comment_weather.py => test_parse_weather_data.py} (62%) diff --git a/aprslib/parsing.py b/aprslib/parsing.py index 5171f2d..d63d93a 100644 --- a/aprslib/parsing.py +++ b/aprslib/parsing.py @@ -162,10 +162,9 @@ def parse(packet): # \ - unused # ] - unused # ^ - unused - # _ - positionless weather report # { - user defined # } - 3rd party traffic - if packet_type in '#$%)*,B:_10090556c220s004g005t077r010p020P030h50b09900s5.5wRSW', + 'to': 'B', + 'via': '', + 'wx_raw_timestamp': '10090556', + "weather": { + "pressure": 990.0, + "humidity": 50, + "rain_1h": float(010.0 * mm_multiplier), + "rain_24h": float(020.0 * mm_multiplier), + "rain_since_midnight": float(030.0 * mm_multiplier), + "snow": float(5.5 * 25.4), + "temperature": float((77.0 - 32) / 1.8), + "wind_direction": 220, + "wind_gust": 5.0 * wind_multiplier, + "wind_speed": 4.0 * wind_multiplier + } } - result = _parse_comment_weather("10090556c220s004g005t077r010p020P030h50b09900s5.5wRSW") - self.assertEqual(expected, result) + packet = "A>B:_10090556c220s004g005t077r010p020P030h50b09900s5.5wRSW" - expected = "10090556wRSW", { + self.assertEqual(expected, parse(packet)) + + packet2 = "A>B:_10090556c220s112g t r h b wRSW" + expected['raw'] = packet2 + expected['weather'] = { "wind_direction": 220, "wind_speed": 112 * wind_multiplier } - result = _parse_comment_weather("10090556c220s112g t r p P h b wRSW") - self.assertEqual(expected, result) + self.assertEqual(expected, parse(packet2)) - expected = "10090556wRSW", { + packet3 = "A>B:_10090556c220s112g...t...r...p...P...b.....wRSW" + expected['raw'] = packet3 + expected['weather'] = { "wind_direction": 220, "wind_speed": 112 * wind_multiplier } - result = _parse_comment_weather("10090556c220s112g...t...r...p...P...h..b.....wRSW") - self.assertEqual(expected, result) + + self.assertEqual(expected, parse(packet3)) def test_position_packet(self): expected = "eCumulusWMR100", { @@ -235,7 +253,7 @@ class ParseCommentWeather(unittest.TestCase): "wind_speed": 1 * wind_multiplier } - result = _parse_comment_weather("319/001g004t048r...p P000h19b10294eCumulusWMR100") + result = _parse_weather_data("319/001g004t048r...p P000h19b10294eCumulusWMR100") self.assertEqual(expected, result) if __name__ == '__main__':