def test_invalid_addressee_field_size(): raw = r'XX1XX-1>APRS,TCPIP*,qAC,TEST::YY9YY-9 :This is a test message{001' # This message does not have the correct size for the addressee field, so the 2nd ':' is in the # wrong position with pytest.raises(ParseError): APRS.parse(raw)
def test_invalid_message_id(): # Message IDs have a maximum length of 5 try: APRS.parse( 'XX1XX-1>APRS,TCPIP*,qAC,TEST::YY9YY-9 :This is a test message{000001' ) assert False except ParseError: assert True except Exception: assert False
def test_parse_uncompressed_positions(input_raw, latitude, longitude, data_type_id, timestamp): packet = APRS.parse(input_raw) assert type(packet) == PositionPacket assert repr(packet) == f"<PositionPacket: {packet.source}>" assert packet.data_type_id == data_type_id assert packet.source == "XX1XX" assert packet.destination == "APRS" assert str(packet.path) == "TCPIP*,qAC,FOURTH" assert type(packet.point) == Point assert packet.latitude == latitude assert packet.longitude == longitude assert packet.ambiguity == 0 assert packet.altitude == 5000 assert packet.course == 221 assert packet.speed == 0 assert packet.symbol_table == "/" assert packet.symbol_id == "$" assert packet.compressed is False if timestamp: assert packet.timestamp.day == 9 assert packet.timestamp.hour == 23 assert packet.timestamp.minute == 45 assert packet.comment == "Test packet"
def test_group_bulletin_packet(): raw = r'XX1XX-1>APRS,TCPIP*,qAC,TEST::BLN4WX :Stand by your snowplows' packet = APRS.parse(raw) assert type(packet) == MessagePacket assert repr(packet) == \ f"<MessagePacket: {packet.source} -> Group Bulletin " \ f"{packet.group_bulletin_name} #{packet.bulletin_id}>" assert packet.data_type_id == ":" assert packet.source == "XX1XX-1" assert packet.destination == "APRS" assert str(packet.path) == "TCPIP*,qAC,TEST" assert packet.addressee == "BLN4WX" assert packet.bulletin_id == 4 assert packet.group_bulletin_name == "WX" assert packet.message == "Stand by your snowplows" # Test resetting attributes to None packet.bulletin_id = None assert packet.bulletin_id is None packet.group_bulletin_name = None assert packet.group_bulletin_name is None
def test_position_with_invalid_data_type_id(): with pytest.raises(ParseError): # This is a contrived example packet = APRS.parse( 'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$221/000/A=005000Test packet' ) packet.data_type_id = "X" packet._parse()
def test_position_with_phg(): packet = APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$PHG5132') assert packet.power == 25 assert packet.height == 20 assert packet.gain == 3 assert packet.directivity == 90
def test_position_with_dfs(): packet = APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$DFS2360') assert packet.strength == 2 assert packet.height == 80 assert packet.gain == 6 assert packet.directivity is None
def test_position_with_df(): packet = APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W\088/036/270/729') assert packet.course == 88 assert packet.speed == 36 assert packet.bearing == 270 assert packet.number == 87.5 assert packet.df_range == 4 assert packet.quality == 1
def test_packets_klz_destination_bit(): # KLZ can be used to denote a space raw = [ r'XX1XX-1>U1PRKK-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet', r'XX1XX-1>U1PRLL-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet', r'XX1XX-1>U1PRZZ-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' ] for r in raw: p = APRS.parse(r) assert p.latitude == 51.033333
def test_packets_first_destination_bit(): # All these are the same latitude, but with different first bits raw = [ r'XX1XX-1>U1PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet', r'XX1XX-1>F1PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet', r'XX1XX-1>51PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' ] for r in raw: p = APRS.parse(r) assert p.latitude == 51.038833
def test_parse_compressed_positions(input_raw, latitude, longitude): packet = APRS.parse(input_raw) assert type(packet) == PositionPacket assert repr(packet) == f"<PositionPacket: {packet.source}>" assert packet.data_type_id == "=" assert packet.source == "XX1XX" assert packet.destination == "APRS" assert str(packet.path) == "TCPIP*,qAC,FOURTH" assert packet.latitude == latitude assert packet.longitude == longitude assert packet.ambiguity == 0 assert packet.course == 88 assert packet.speed == 36.2 assert packet.symbol_table == "/" assert packet.symbol_id == ">" assert packet.comment == "Test packet"
def test_bulletin_packet(): raw = r'XX1XX-1>APRS,TCPIP*,qAC,TEST::BLN3 :Snow expected in Tampa RSN' packet = APRS.parse(raw) assert type(packet) == MessagePacket assert repr( packet ) == f"<MessagePacket: {packet.source} -> Bulletin #{packet.bulletin_id}>" assert packet.data_type_id == ":" assert packet.source == "XX1XX-1" assert packet.destination == "APRS" assert str(packet.path) == "TCPIP*,qAC,TEST" assert packet.addressee == "BLN3" assert packet.bulletin_id == 3 assert packet.message == "Snow expected in Tampa RSN" # Test resetting attributes to None packet.bulletin_id = None assert packet.bulletin_id is None
def test_announcement_packet(): raw = r'XX1XX-1>APRS,TCPIP*,qAC,TEST::BLNQ :Mt St Helen digi will be QRT this weekend' packet = APRS.parse(raw) assert type(packet) == MessagePacket assert repr(packet) == \ f"<MessagePacket: {packet.source} -> Announcement {packet.announcement_id}>" assert packet.data_type_id == ":" assert packet.source == "XX1XX-1" assert packet.destination == "APRS" assert str(packet.path) == "TCPIP*,qAC,TEST" assert packet.addressee == "BLNQ" assert packet.bulletin_id is None assert packet.announcement_id == "Q" assert packet.message == "Mt St Helen digi will be QRT this weekend" # Test resetting attributes to None packet.announcement_id = None assert packet.announcement_id is None
def test_missing_symbol_id(): # Missing symbol ID with pytest.raises(ParseError): APRS.parse(r'XX1XX-1>U1PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"B')
def test_packets_with_190_subtracted_from_longitude(): p = APRS.parse( r'XX1XX-1>U1PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`x\Fl"Bk/]"?l}Test Mic-E packet' ) assert p.longitude == -2.073667
def test_packet_with_missing_speed_and_course(): with pytest.raises(ParseError): APRS.parse( r'XX1XX-1>M1PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' )
def test_packet_with_invalid_destination_bit(): with pytest.raises(ParseError): APRS.parse( r'XX1XX-1>M1PRSS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' )
def _decode(self): raw, typelist, callsign, tslist, latlist, lonlist, altlist, commentlist, messagelist = ([] for i in range(9)) sourcelist, addresseelist, statuslist, courselist, bearinglist, speedlist = ([] for i in range(6)) errortimes, errorpackets = ([] for i in range(2)) for row in self.packets.itertuples(): print(row) try: decoded = APRS.parse(row.packet, row.datetime) except: errortimes.append(row.datetime) errorpackets.append(row.packet) continue if type(decoded).__name__ == 'PositionPacket': typelist.append('Position') tslist.append(decoded._ts) latlist.append(decoded.latitude) lonlist.append(decoded.longitude) altlist.append(decoded.altitude) commentlist.append(decoded.comment) messagelist.append('None') sourcelist.append(decoded.source) addresseelist.append('None') statuslist.append('None') courselist.append(decoded.course) bearinglist.append(decoded.bearing) speedlist.append(decoded.speed) elif type(decoded).__name__ == 'MessagePacket': typelist.append('Message') tslist.append(decoded._ts) latlist.append('None') lonlist.append('None') altlist.append('None') commentlist.append('None') messagelist.append(decoded.message) sourcelist.append(decoded.source) addresseelist.append(decoded.addressee) statuslist.append('None') courselist.append('None') bearinglist.append('None') speedlist.append('None') elif type(decoded).__name__ == 'StatusPacket': typelist.append('Status') tslist.append(decoded._ts) latlist.append('None') lonlist.append('None') altlist.append('None') commentlist.append('None') messagelist.append('None') sourcelist.append(decoded.source) addresseelist.append('None') statuslist.append(decoded.status_message) courselist.append('None') bearinglist.append('None') speedlist.append('None') elif type(decoded).__name__ == 'ObjectPacket': typelist.append('Object') tslist.append(decoded._ts) latlist.append('None') lonlist.append('None') altlist.append('None') commentlist.append('None') messagelist.append('None') sourcelist.append(decoded.source) addresseelist.append('None') statuslist.append('None') courselist.append('None') bearinglist.append('None') speedlist.append('None') else: print(type(decoded).__name__) print('Not able to decode this packet type yet...') # errortimes.append(row.datetime) # errorpackets.append(row.packet) decoded_df = pd.DataFrame( {'type': typelist, 'timestamp': tslist, 'source': sourcelist, 'addressee': addresseelist, 'latitude': latlist, 'longitude': lonlist, 'altitude': altlist, 'comment': commentlist, 'message': messagelist, 'status': statuslist, 'course': courselist, 'bearing': bearinglist, 'speed': speedlist} ) decoded_df = decoded_df.set_index(['timestamp']) decoded_df.to_csv(self.outfile, na_rep="None")
def test_packets_sixth_destination_bit(): # The sixth bit can be used to flip the longitude raw = r'XX1XX-1>U1PRS3-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' p = APRS.parse(raw) assert p.longitude == 114.073667
def test_position_with_df_missing_df_values(): with pytest.raises(ParseError): # Missing DF values APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W\\')
def test_packets_fourth_destination_bit(): # The fourth bit can be used to flip the latitude raw = r'XX1XX-1>U1P2SS-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' p = APRS.parse(raw) assert p.latitude == -51.038833
def packet(): packet = APRS.parse(raw) return packet
def test_position_with_missing_timestamp(): with pytest.raises(ParseError): # This packet should have a timestamp APRS.parse('XX1XX>APRS,TCPIP*,qAC,FOURTH:@5030.50S/10020.30E$221/000/A=005000Test packet')
def _decode(self): raw, typelist, callsign, tslist, latlist, lonlist, altlist, commentlist, messagelist = ( [] for i in range(9)) sourcelist, addresseelist, statuslist, courselist, bearinglist, speedlist = ( [] for i in range(6)) for row in self.packets.itertuples(): print(row) if row.packet[0] == "#": # Ignore server messages continue decoded = APRS.parse(row.packet, row.datetime) raw.append(decoded.raw) tslist.append(decoded._ts) callsign.append(decoded.source) if type(decoded).__name__ == 'PositionPacket': typelist.append('Position') latlist.append(decoded.latitude) lonlist.append(decoded.longitude) altlist.append(decoded.altitude) commentlist.append(decoded.comment) messagelist.append('None') sourcelist.append(decoded.source) addresseelist.append('None') statuslist.append('None') courselist.append(decoded.course) bearinglist.append(decoded.bearing) speedlist.append(decoded.speed) elif type(decoded).__name__ == 'MessagePacket': typelist.append('Message') latlist.append('None') lonlist.append('None') altlist.append('None') commentlist.append('None') messagelist.append(decoded.message) sourcelist.append(decoded.source) addresseelist.append(decoded.addressee) statuslist.append('None') courselist.append('None') bearinglist.append('None') speedlist.append('None') elif type(decoded).__name__ == 'StatusPacket': typelist.append('Status') latlist.append('None') lonlist.append('None') altlist.append('None') commentlist.append('None') messagelist.append('None') sourcelist.append(decoded.source) addresseelist.append('None') statuslist.append(decoded.status_message) courselist.append('None') bearinglist.append('None') speedlist.append('None') elif type(decoded).__name__ == 'ObjectPacket': typelist.append('Object') latlist.append('None') lonlist.append('None') altlist.append('None') commentlist.append('None') messagelist.append('None') sourcelist.append(decoded.source) addresseelist.append('None') statuslist.append('None') courselist.append('None') bearinglist.append('None') speedlist.append('None') else: print(type(decoded).__name__) print('Not able to decode this packet type yet...') decoded_df = pd.DataFrame({ 'type': typelist, 'timestamp': tslist, 'source': sourcelist, 'addressee': addresseelist, 'latitude': latlist, 'longitude': lonlist, 'altitude': altlist, 'comment': commentlist, 'message': messagelist, 'status': statuslist, 'course': courselist, 'bearing': bearinglist, 'speed': speedlist }) print(decoded_df) decoded_df = decoded_df.set_index(['timestamp']) # decoded_df.to_csv("/Users/ptduran/Desktop/APRS/decoded/KJ4OVR_2012-2015.LOG", na_rep = "None") decoded_df.to_csv( "/Users/ptduran/Desktop/APRS/decoded/KJ4ERJ_2019-2020.LOG", na_rep="None")
def test_position_with_weather(): # TODO - Weather is not yet implemented packet = APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W_TEST')
def test_position_with_rng(): packet = APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$RNG0050') assert packet.radio_range == 50
def packet_with_hdg_and_pwr(): packet = APRS.parse(raw_with_hdg_and_pwr) return packet
def test_packets_fifth_destination_bit(): # The fifth bit can be used to add an offset to the longitude raw = r'XX1XX-1>U1PR3S-1,WIDE1-1,WIDE2-2,qAR,CALGRY:`*\Fl"Bk/]"?l}Test Mic-E packet' p = APRS.parse(raw) assert p.longitude == -14.073667
def test_invalid_packet(): # Missing > after source try: APRS.parse( 'XX1XXAPRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$221/000/A=005000Test packet' ) assert False except ParseError: assert True except Exception: assert False # Source is too long try: APRS.parse( 'XXX1XXX-11>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$221/000/A=005000Test packet' ) assert False except ParseError: assert True except Exception: assert False # Destination is too long try: APRS.parse( 'XX1XX>APRSAPRSAPRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$221/000/A=005000Test packet' ) assert False except ParseError: assert True except Exception: assert False # Destination is invalid try: APRS.parse( 'XX1XX>aprs,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$221/000/A=005000Test packet' ) assert False except ParseError: assert True except Exception: assert False # Destination is invalid try: APRS.parse( 'XX1XX>APRS-XX,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W$221/000/A=005000Test packet' ) assert False except ParseError: assert True except Exception: assert False # Packet is too short try: APRS.parse('XX1XX>APRS,TCPIP*,qAC,FOURTH:=') assert False except ParseError: assert True except Exception: assert False
def test_position_with_df_invalid_df_format(): with pytest.raises(ParseError): # Invalid DF format APRS.parse(r'XX1XX>APRS,TCPIP*,qAC,FOURTH:=5030.50N/10020.30W\088036270729')