diff --git a/tests/parser/parse/__init__.py b/tests/parser/parse/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/parser/parse/test_comments.py b/tests/parser/parse/test_comments.py new file mode 100644 index 0000000..2611e8c --- /dev/null +++ b/tests/parser/parse/test_comments.py @@ -0,0 +1,23 @@ +from datetime import datetime, timezone + +from ogn.parser import parse + + +def test_comment(): + raw_message = "# bad configured ogn receiver" + message = parse(raw_message) + + assert message['comment'] == raw_message + assert message['aprs_type'] == 'comment' + + +def test_server_comment(): + raw_message = "# aprsc 2.1.4-g408ed49 17 Mar 2018 09:30:36 GMT GLIDERN1 37.187.40.234:10152" + message = parse(raw_message) + + assert message['version'] == '2.1.4-g408ed49' + assert message['timestamp'] == datetime(2018, 3, 17, 9, 30, 36, tzinfo=timezone.utc) + assert message['server'] == 'GLIDERN1' + assert message['ip_address'] == '37.187.40.234' + assert message['port'] == 10152 + assert message['aprs_type'] == 'server' diff --git a/tests/parser/test_parse.py b/tests/parser/parse/test_parse.py similarity index 93% rename from tests/parser/test_parse.py rename to tests/parser/parse/test_parse.py index 203009a..4badfcd 100644 --- a/tests/parser/test_parse.py +++ b/tests/parser/parse/test_parse.py @@ -10,7 +10,7 @@ from ogn.parser.exceptions import AprsParseError def _parse_valid_beacon_data_file(filename, beacon_type): - with open(os.path.dirname(__file__) + '/../../ogn-aprs-protocol/valid_messages/' + filename) as f: + with open(os.path.dirname(__file__) + '/../../../ogn-aprs-protocol/valid_messages/' + filename) as f: for line in f: if line.strip() == '': continue @@ -143,7 +143,10 @@ def test_spot_beacons(): def test_generic_beacons(): message = parse("EPZR>WTFDSTCALL,TCPIP*,qAC,GLIDERN1:>093456h this is a comment") + + assert message['aprs_type'] == 'status' assert message['beacon_type'] == 'unknown' + assert message['user_comment'] == "this is a comment" @@ -157,6 +160,11 @@ def test_fail_empty(): parse("") +def test_fail_validationassert(): + with pytest.raises(AprsParseError): + parse("notAValidString") + + def test_fail_bad_string(): with pytest.raises(AprsParseError): parse("Lachens>APRS,TCPIwontbeavalidstring") @@ -186,19 +194,12 @@ def test_default_reference_date(createTimestamp_mock): assert call_args_before != call_args_seconds_later -def test_copy_constructor(): - valid_aprs_string = "FLRDDA5BA>APRS,qAS,LFMX:/160829h4415.41N/00600.03E'342/049/A=005524 id0ADDA5BA -454fpm -1.1rot 8.8dB 0e +51.2kHz gps4x5" - message = parse(valid_aprs_string) - - assert message['name'] == 'FLRDDA5BA' - assert message['address'] == 'DDA5BA' - - def test_no_receiver(): result = parse("EDFW>OGNSDR:/102713h4949.02NI00953.88E&/A=000984") assert result['aprs_type'] == 'position' assert result['beacon_type'] == 'receiver' + assert result['name'] == 'EDFW' assert result['dstcall'] == 'OGNSDR' assert result.get('receiver_name') is None diff --git a/tests/parser/parse/test_position.py b/tests/parser/parse/test_position.py new file mode 100644 index 0000000..e9a3769 --- /dev/null +++ b/tests/parser/parse/test_position.py @@ -0,0 +1,100 @@ +import pytest + +from ogn.parser import parse +from ogn.parser.exceptions import AprsParseError +from ogn.parser.utils import FPM_TO_MS, HPM_TO_DEGS, KNOTS_TO_MS, KPH_TO_MS, FEETS_TO_METER + + +def test_basic(): + raw_message = r"FLRDDA5BA>APRS,qAS,LFMX:/160829h4415.41N/00600.03E'342/049/A=005524 this is a comment" + message = parse(raw_message) + + assert message['aprs_type'] == 'position' + assert message['beacon_type'] == 'unknown' + + assert message['name'] == "FLRDDA5BA" + assert message['dstcall'] == "APRS" + assert message['receiver_name'] == "LFMX" + assert message['timestamp'].strftime('%H:%M:%S') == "16:08:29" + assert message['latitude'] == pytest.approx(44.25683, 5) + assert message['symboltable'] == '/' + assert message['longitude'] == pytest.approx(6.0005, 5) + assert message['symbolcode'] == '\'' + assert message['track'] == 342 + assert message['ground_speed'] == 49 * KNOTS_TO_MS / KPH_TO_MS + assert message['altitude'] == pytest.approx(5524 * FEETS_TO_METER, 5) + assert message['user_comment'] == "this is a comment" + + +def test_v026_relay(): + # beacons can be relayed + raw_message = "FLRFFFFFF>OGNAVI,NAV07220E*,qAS,NAVITER:/092002h1000.00S/01000.00W'000/000/A=003281 !W00! id2820FFFFFF +300fpm +1.7rot" + message = parse(raw_message) + + assert message['aprs_type'] == 'position' + assert message['beacon_type'] == 'naviter' + + assert message['relay'] == "NAV07220E" + + +def test_no_altitude(): + # altitude is not a 'must have' + raw_message = "FLRDDEEF1>OGCAPT,qAS,CAPTURS:/065511h4837.63N/00233.79E'000/000" + message = parse(raw_message) + + assert message['aprs_type'] == 'position' + assert message['beacon_type'] == 'capturs' + + assert message.get('altitude') is None + + +def test_invalid_coordinates(): + # sometimes the coordinates leave their valid range: -90<=latitude<=90 or -180<=longitude<=180 + with pytest.raises(AprsParseError): + parse("RND000000>APRS,qAS,TROCALAN1:/210042h6505.31S/18136.75W^054/325/A=002591 !W31! idA4000000 +099fpm +1.8rot FL029.04 12.0dB 5e -6.3kHz gps11x17") + + with pytest.raises(AprsParseError): + parse("RND000000>APRS,qAS,TROCALAN1:/210042h9505.31S/17136.75W^054/325/A=002591 !W31! idA4000000 +099fpm +1.8rot FL029.04 12.0dB 5e -6.3kHz gps11x17") + + +def test_invalid_timestamp(): + with pytest.raises(AprsParseError): + parse("OGND4362A>APRS,qAS,Eternoz:/194490h4700.25N/00601.47E'003/063/A=000000 !W22! id07D4362A 0fpm +0.0rot FL000.00 2.0dB 3e -2.8kHz gps3x4 +12.2dBm") + + with pytest.raises(AprsParseError): + parse("Ulrichamn>APRS,TCPIP*,qAC,GLIDERN1:/194490h5747.30NI01324.77E&/A=001322") + + +def test(): + raw_message = r"FLRDDEEF1>OGCAPT,qAS,CAPTURS:/065511h4837.63N/00233.79E'255/045/A=003399 !W03! id06DDFAA3 -613fpm -3.9rot 22.5dB 7e -7.0kHz gps3x7 s7.07 h41 rD002F8" + message = parse(raw_message) + + assert message['aprs_type'] == 'position' + assert message['beacon_type'] == 'capturs' + + assert message['name'] == "FLRDDEEF1" + assert message['dstcall'] == "OGCAPT" + assert message['receiver_name'] == "CAPTURS" + assert message['timestamp'].strftime('%H:%M:%S') == "06:55:11" + assert message['latitude'] == pytest.approx(48.62605, 5) + assert message['longitude'] == pytest.approx(2.56298, 5) + assert message['symboltable'] == '/' + assert message['symbolcode'] == '\'' + assert message['track'] == 255 + assert message['ground_speed'] == 45 * KNOTS_TO_MS / KPH_TO_MS + assert message['altitude'] == pytest.approx(3399 * FEETS_TO_METER, 5) + + assert message['address_type'] == 2 + assert message['aircraft_type'] == 1 + assert message['stealth'] is False + assert message['no-tracking'] is False + assert message['address'] == 'DDFAA3' + assert message['climb_rate'] == pytest.approx(-613 * FPM_TO_MS, 0.1) + assert message['turn_rate'] == pytest.approx(-3.9 * HPM_TO_DEGS, 0.1) + assert message['signal_quality'] == 22.5 + assert message['error_count'] == 7 + assert message['frequency_offset'] == -7.0 + assert message['gps_quality'] == '3x7' + assert message['software_version'] == 7.07 + assert message['hardware_version'] == 65 + assert message['real_address'] == 'D002F8' diff --git a/tests/parser/parse/test_position_weather.py b/tests/parser/parse/test_position_weather.py new file mode 100644 index 0000000..e335628 --- /dev/null +++ b/tests/parser/parse/test_position_weather.py @@ -0,0 +1,32 @@ +from ogn.parser import parse +from ogn.parser.utils import KNOTS_TO_MS, KPH_TO_MS, INCH_TO_MM, fahrenheit_to_celsius + + +def test_v028_fanet_position_weather(): + # with v0.2.8 fanet devices can report weather data + raw_message = 'FNTFC9002>OGNFNT,qAS,LSXI2:/163051h4640.33N/00752.21E_187/004g007t075h78b63620 29.0dB -8.0kHz' + message = parse(raw_message) + + assert message['aprs_type'] == 'position_weather' + assert message['beacon_type'] == 'fanet' + + assert message['wind_direction'] == 187 + assert message['wind_speed'] == 4 * KNOTS_TO_MS / KPH_TO_MS + assert message['wind_speed_peak'] == 7 * KNOTS_TO_MS / KPH_TO_MS + assert message['temperature'] == fahrenheit_to_celsius(75) + assert message['humidity'] == 78 * 0.01 + assert message['barometric_pressure'] == 63620 + + assert message['signal_quality'] == 29.0 + assert message['frequency_offset'] == -8.0 + + +def test_GXAirCom_fanet_position_weather_rainfall(): + raw_message = 'FNT08F298>OGNFNT,qAS,DREIFBERG:/082654h4804.90N/00845.74E_273/005g008t057r123p234h90b10264 0.0dB' + message = parse(raw_message) + + assert message['aprs_type'] == 'position_weather' + assert message['beacon_type'] == 'fanet' + + assert message['rainfall_1h'] == 123 / 100 * INCH_TO_MM + assert message['rainfall_24h'] == 234 / 100 * INCH_TO_MM diff --git a/tests/parser/parse/test_status.py b/tests/parser/parse/test_status.py new file mode 100644 index 0000000..36b0400 --- /dev/null +++ b/tests/parser/parse/test_status.py @@ -0,0 +1,45 @@ +from ogn.parser import parse + + +def test_v025(): + # introduced the "aprs status" format where many informations (lat, lon, alt, speed, ...) are just optional + raw_message = "EPZR>APRS,TCPIP*,qAC,GLIDERN1:>093456h this is a comment" + message = parse(raw_message) + + assert message['aprs_type'] == 'status' + assert message['beacon_type'] == 'unknown' + + assert message['name'] == "EPZR" + assert message['receiver_name'] == "GLIDERN1" + assert message['timestamp'].strftime('%H:%M:%S') == "09:34:56" + assert message['user_comment'] == "this is a comment" + + +def test(): + raw_message = "EPZR>APRS,TCPIP*,qAC,GLIDERN1:>093456h v0.2.7.RPI-GPU CPU:0.7 RAM:770.2/968.2MB NTP:1.8ms/-3.3ppm +55.7C 7/8Acfts[1h] RF:+54-1.1ppm/-0.16dB/+7.1dB@10km[19481]/+16.8dB@10km[7/13]" + message = parse(raw_message) + + assert message['aprs_type'] == 'status' + assert message['beacon_type'] == 'unknown' + + assert message['name'] == "EPZR" + assert message['receiver_name'] == "GLIDERN1" + assert message['timestamp'].strftime('%H:%M:%S') == "09:34:56" + assert message['version'] == "0.2.7" + assert message['platform'] == "RPI-GPU" + assert message['cpu_load'] == 0.7 + assert message['free_ram'] == 770.2 + assert message['total_ram'] == 968.2 + assert message['ntp_offset'] == 1.8 + assert message['rt_crystal_correction'] == -3.3 + assert message['cpu_temp'] == 55.7 + assert message['senders_visible'] == 7 + assert message['senders_total'] == 8 + assert message['rec_crystal_correction'] == 54 + assert message['rec_crystal_correction_fine'] == -1.1 + assert message['rec_input_noise'] == -0.16 + assert message['senders_signal'] == 7.1 + assert message['senders_messages'] == 19481 + assert message['good_senders_signal'] == 16.8 + assert message['good_senders'] == 7 + assert message['good_and_bad_senders'] == 13 diff --git a/tests/parser/test_parse_aprs.py b/tests/parser/test_parse_aprs.py deleted file mode 100644 index c9384b1..0000000 --- a/tests/parser/test_parse_aprs.py +++ /dev/null @@ -1,150 +0,0 @@ -import pytest - -from datetime import datetime, timezone - -from ogn.parser import parse -from ogn.parser.utils import KNOTS_TO_MS, KPH_TO_MS, FEETS_TO_METER, INCH_TO_MM, fahrenheit_to_celsius -from ogn.parser.exceptions import AprsParseError - - -def test_fail_validationassert(): - with pytest.raises(AprsParseError): - parse("notAValidString") - - -def test_basic(): - message = parse("FLRDDA5BA>APRS,qAS,LFMX:/160829h4415.41N/00600.03E'342/049/A=005524 this is a comment") - - assert message['aprs_type'] == 'position' - assert message['name'] == "FLRDDA5BA" - assert message['dstcall'] == "APRS" - assert message['receiver_name'] == "LFMX" - assert message['timestamp'].strftime('%H:%M:%S') == "16:08:29" - assert message['latitude'] == pytest.approx(44.25683, 5) - assert message['symboltable'] == '/' - assert message['longitude'] == pytest.approx(6.0005, 5) - assert message['symbolcode'] == '\'' - assert message['track'] == 342 - assert message['ground_speed'] == 49 * KNOTS_TO_MS / KPH_TO_MS - assert message['altitude'] == pytest.approx(5524 * FEETS_TO_METER, 5) - assert message['user_comment'] == "this is a comment" - - -def test_v024(): - # higher precision datum format introduced - raw_message = "FLRDDA5BA>APRS,qAS,LFMX:/160829h4415.41N/00600.03E'342/049/A=005524 !W26! id21400EA9 -2454fpm +0.9rot 19.5dB 0e -6.6kHz gps1x1 s6.02 h44 rDF0C56" - message = parse(raw_message) - - assert message['aprs_type'] == 'position' - assert message['latitude'] - 44.2568 - 1 / 30000 == pytest.approx(2 / 1000 / 60, 10) - assert message['longitude'] - 6.0005 == pytest.approx(6 / 1000 / 60, 10) - - -def test_v025(): - # introduced the "aprs status" format where many informations (lat, lon, alt, speed, ...) are just optional - raw_message = "EPZR>APRS,TCPIP*,qAC,GLIDERN1:>093456h this is a comment" - message = parse(raw_message) - - assert message['aprs_type'] == 'status' - assert message['name'] == "EPZR" - assert message['receiver_name'] == "GLIDERN1" - assert message['timestamp'].strftime('%H:%M:%S') == "09:34:56" - assert message['user_comment'] == "this is a comment" - - -def test_v026(): - # 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(raw_message) - - assert message['aprs_type'] == 'position' - assert message.get('comment') is None - - -def test_v026_relay(): - # beacons can be relayed - raw_message = "FLRFFFFFF>OGNAVI,NAV07220E*,qAS,NAVITER:/092002h1000.00S/01000.00W'000/000/A=003281 !W00! id2820FFFFFF +300fpm +1.7rot" - message = parse(raw_message) - - assert message['aprs_type'] == 'position' - assert message['relay'] == "NAV07220E" - - -def test_v027_ddhhmm(): - # beacons can have hhmmss or ddhhmm timestamp - raw_message = "ICA4B0678>APRS,qAS,LSZF:/301046z4729.50N/00812.89E'227/091/A=002854 !W01! id054B0678 +040fpm +0.0rot 19.0dB 0e +1.5kHz gps1x1" - message = parse(raw_message) - - assert message['aprs_type'] == 'position' - assert message['timestamp'].strftime('%d %H:%M') == "30 10:46" - - -def test_v028_fanet_position_weather(): - # with v0.2.8 fanet devices can report weather data - raw_message = 'FNTFC9002>OGNFNT,qAS,LSXI2:/163051h4640.33N/00752.21E_187/004g007t075h78b63620 29.0dB -8.0kHz' - message = parse(raw_message) - - assert message['aprs_type'] == 'position_weather' - assert message['wind_direction'] == 187 - assert message['wind_speed'] == 4 * KNOTS_TO_MS / KPH_TO_MS - assert message['wind_speed_peak'] == 7 * KNOTS_TO_MS / KPH_TO_MS - assert message['temperature'] == fahrenheit_to_celsius(75) - assert message['humidity'] == 78 * 0.01 - assert message['barometric_pressure'] == 63620 - - assert message['signal_quality'] == 29.0 - assert message['frequency_offset'] == -8.0 - - -def test_GXAirCom_fanet_position_weather_rainfall(): - raw_message = 'FNT08F298>OGNFNT,qAS,DREIFBERG:/082654h4804.90N/00845.74E_273/005g008t057r123p234h90b10264 0.0dB' - message = parse(raw_message) - - assert message['aprs_type'] == 'position_weather' - assert message['rainfall_1h'] == 123 / 100 * INCH_TO_MM - assert message['rainfall_24h'] == 234 / 100 * INCH_TO_MM - - -def test_no_altitude(): - # altitude is not a 'must have' - raw_message = "FLRDDEEF1>OGCAPT,qAS,CAPTURS:/065511h4837.63N/00233.79E'000/000" - message = parse(raw_message) - - assert message.get('altitude') is None - - -def test_invalid_coordinates(): - # sometimes the coordinates leave their valid range: -90<=latitude<=90 or -180<=longitude<=180 - with pytest.raises(AprsParseError): - parse("RND000000>APRS,qAS,TROCALAN1:/210042h6505.31S/18136.75W^054/325/A=002591 !W31! idA4000000 +099fpm +1.8rot FL029.04 12.0dB 5e -6.3kHz gps11x17") - - with pytest.raises(AprsParseError): - parse("RND000000>APRS,qAS,TROCALAN1:/210042h9505.31S/17136.75W^054/325/A=002591 !W31! idA4000000 +099fpm +1.8rot FL029.04 12.0dB 5e -6.3kHz gps11x17") - - -def test_invalid_timestamp(): - with pytest.raises(AprsParseError): - parse("OGND4362A>APRS,qAS,Eternoz:/194490h4700.25N/00601.47E'003/063/A=000000 !W22! id07D4362A 0fpm +0.0rot FL000.00 2.0dB 3e -2.8kHz gps3x4 +12.2dBm") - - with pytest.raises(AprsParseError): - parse("Ulrichamn>APRS,TCPIP*,qAC,GLIDERN1:/194490h5747.30NI01324.77E&/A=001322") - - -def test_bad_comment(): - raw_message = "# bad configured ogn receiver" - message = parse(raw_message) - - assert message['comment'] == raw_message - assert message['aprs_type'] == 'comment' - - -def test_server_comment(): - raw_message = "# aprsc 2.1.4-g408ed49 17 Mar 2018 09:30:36 GMT GLIDERN1 37.187.40.234:10152" - message = parse(raw_message) - - assert message['version'] == '2.1.4-g408ed49' - assert message['timestamp'] == datetime(2018, 3, 17, 9, 30, 36, tzinfo=timezone.utc) - assert message['server'] == 'GLIDERN1' - assert message['ip_address'] == '37.187.40.234' - assert message['port'] == 10152 - assert message['aprs_type'] == 'server'