def test_nmea_multipart_sentence_reassembly(self): """ test the ability to recieve multiple NMEA 0183 sentences and join them together into one AIS message """ expected = ('000101000011000111100001100010000000000' '10000001001001111000100100011001' '1010100001001100000111011010010000010000' '000011000100100010000010100110' '0001001001111100000100000100000100000100000' '10000010000010000010000010000010000010000010' '00000101100100101000000001100000111100001000' '010011100010011100000001001111000010000001010' '010010010000101001001010010001111100000100000' '100000100000100000100000100000100000100000100' '0001000001000000000') testsentences = [('!AIVDM,2,1,2,B,537QR042Ci8kD9PsB20HT' '@DhTv2222222222221I:0H?24pW0ChPDTQB,0*49'), '!AIVDM,2,2,2,B,DSp888888888880,2*7A'] testtracker = nmea.NMEAtracker() for sentence in testsentences: processed = testtracker.process_sentence(sentence) if processed: binarypayload = binary.ais_sentence_payload_binary(processed) self.assertEqual(expected, binarypayload)
def process_message(self, data): """ determine what type of AIS message it is Args: data(str): full message payload from 1 or more NMEA sentences Raises: InvalidMMSI: if the mmsi = 000000000 Returns: msgobj(messages.aismessage.AISMessage): the ais message type object """ msgbinary = binary.ais_sentence_payload_binary(data) msgtype = binary.decode_sixbit_integer(msgbinary[0:6]) if msgtype in (4, 11): msgobj = allmessages.MSGTYPES[msgtype](msgbinary) if msgobj.mmsi == '000000000': raise InvalidMMSI('Invalid MMSI - 000000000') if msgobj.mmsi not in self.stations: self.stations[msgobj.mmsi] = AISStation(msgobj.mmsi) if self.stations[msgobj.mmsi].stnclass == 'Unknown': self.stations[msgobj.mmsi].determine_station_class(msgobj) msgobj.rxtime = msgobj.timestamp self.stations[msgobj.mmsi].find_position_information(msgobj) self.messagesprocessed += 1 self.messages[allmessages.MSGDESCRIPTIONS[msgtype]] += 1
def process_message(self, data, timestamp=None): """ determine what type of AIS message it is Note: the timestamp can be given as an argument for each message to be processed or timings can be approximated from the last type 4/11 base station report timestamp received. for the latter option it is preferred that you have a nearby base station transmitting the correct time on a regular interval Args: data(str): full message payload from 1 or more NMEA sentences timestamp(str): time this message was recieved, if provided this will take precedence over timings received from AIS base stations Raises: UnknownMessageType: if the message type is not in the allmessages.MSGTYPES dict InvalidMMSI: if the mmsi = 000000000 Returns: msgobj(messages.aismessage.AISMessage): the ais message type object """ msgbinary = binary.ais_sentence_payload_binary(data) msgtype = binary.decode_sixbit_integer(msgbinary[0:6]) if msgtype in allmessages.MSGTYPES.keys(): msgobj = allmessages.MSGTYPES[msgtype](msgbinary) else: raise UnknownMessageType( 'Unknown message type {} - {}'.format(msgtype, data)) if msgobj.mmsi == '000000000': raise InvalidMMSI('Invalid MMSI - 000000000') if msgobj.mmsi not in self.stations: self.stations[msgobj.mmsi] = AISStation(msgobj.mmsi) if self.stations[msgobj.mmsi].stnclass == 'Unknown': self.stations[msgobj.mmsi].determine_station_class(msgobj) if (self.stations[msgobj.mmsi].stntype == 'Unknown' or self.stations[msgobj.mmsi].name == ''): self.stations[msgobj.mmsi].find_station_name_and_type(msgobj) if timestamp: if timestamp not in self.timings: self.timings.append(timestamp) else: if msgtype in (4, 11) and msgobj.mmsi in self.timingsource: if (msgobj.timestamp != TIMEUNAVAILABLE and msgobj.timestamp not in self.timings and kml.DATETIMEREGEX.match(msgobj.timestamp)): self.timings.append(msgobj.timestamp + ' (estimated)') try: timestamp = self.timings[len(self.timings) - 1] except IndexError: timestamp = 'N/A' msgobj.rxtime = timestamp self.stations[msgobj.mmsi].find_position_information(msgobj) self.messagesprocessed += 1 self.messages[allmessages.MSGDESCRIPTIONS[msgtype]] += 1 return msgobj
def test_binary_string_to_NMEA_payload(self): """ tests converting a binary string back into its NMEA 0183 payload """ expectedpayload = '402=acAv=6;4cwi3FHNPPTW005H@' binresult = binary.ais_sentence_payload_binary(expectedpayload) testpayload = binary.ais_sentence_binary_payload(binresult) self.assertEqual(expectedpayload, testpayload)
def test_navigation_aid_monitoring_ROI(self): """ Test to see if a particular type 6 message is recognised """ payload = '6>jHC7D0V:C0?``00000P00i' msgbinary = binary.ais_sentence_payload_binary(payload) msg = t6.Type6BinaryMessage(msgbinary) self.assertEqual(msg.msgsubtype, 'Aid to Navigation monitoring ROI')
def test_marine_traffic_signals(self): """ Test to see if a particular type 8 message is recognised """ payload = ('8>jHDF00Dh0B8EP3<?CEB5P<978D0006J?wjapFA<N00') msgbinary = binary.ais_sentence_payload_binary(payload) msg = t8.Type8BinaryBroadcastMessage(msgbinary) self.assertEqual(msg.msgsubtype, 'Marine Traffic Signals')
def test_inland_static_and_voyage_data(self): """ Test to see if a particular type 8 message is recognised """ payload = '83P=pSPj2`8800400PPPM00M5fp0' msgbinary = binary.ais_sentence_payload_binary(payload) msg = t8.Type8BinaryBroadcastMessage(msgbinary) self.assertEqual(msg.msgsubtype, 'Inland Static & Voyage Data')
def test_meteorological_and_hydrological_data(self): """ Test to see if a particular type 8 message is recognised """ payload = ('8>jHC700Gwn;21S`2j2ePPFQDB06EuOwgwl' '?wnSwe7wvlO1PsAwwnSAEwvh0') msgbinary = binary.ais_sentence_payload_binary(payload) msg = t8.Type8BinaryBroadcastMessage(msgbinary) self.assertEqual(msg.msgsubtype, 'Meteorological and Hydrological Data')
def test_empty_value_for_cog(self): """ this sentence doesn't appear to have a value for course over ground the binary module should raise a NoBinaryData exception """ testsentence = '!AIVDM,1,1,,A,3O>soN5MUNBoMdUdlh,0*64' nmeatracker = nmea.NMEAtracker() testdata = nmeatracker.process_sentence(testsentence) testbinarystr = binary.ais_sentence_payload_binary(testdata) with self.assertRaises(binary.NoBinaryData): binary.decode_sixbit_integer(testbinarystr[116:128]) / 10
def test_position_update_from_messages(self): """ use actual recieved messages to update the position """ posreps = [ '13P;Ruhvj1wj=0bNTU;up;=T80Rd', '13P;RuhvjIwj7blNUOPtIr1n8000', '13P;RuhvjUwivdfNV=7dL:2t80Rd', '13P;RuhsCfwihvnNWpTtLb0<8@3p' ] for pos in posreps: binarystr = binary.ais_sentence_payload_binary(pos) posmsg = t123.Type123PositionReportClassA(binarystr) self.aisteststn.find_position_information(posmsg) self.assertEqual(len(posreps), len(self.aisteststn.posrep))
def test_NMEA_to_binary_string(self): """ Tests converting a sixbit ascii string into a binary string. """ testpayload = 'E>jHC=c6:W2h22R`@1:WdP00000Opa@H?KTcP10888e?B0' expectedbinary = ('01010100111011001001100001001100' '11011010110001100010101001110000' '10110000000010000010100010101000' '01000000000100101010011110110010' '00000000000000000000000000000000' '00011111111000101001010000011000' '00111101101110010010101110000000' '00010000000010000010000010001011' '01001111010010000000') binresult = binary.ais_sentence_payload_binary(testpayload) self.assertEqual(binresult, expectedbinary)
def test_find_station_name_and_type(self): """ get the name and ship type from a Type 5 Static Data Report """ t5payload = ('53P;Rul2<10S89PgN20l4p4pp4r222222222220' '`8@N==57nN9A3mAk0Dp8888888888880') expect = { 'name': 'MANANNAN', 'stntype': 'High speed craft (HSC), all ships of this type' } t5binary = binary.ais_sentence_payload_binary(t5payload) t5obj = t5.Type5StaticAndVoyageData(t5binary) self.aisteststn.find_station_name_and_type(t5obj) found = { 'name': self.aisteststn.name, 'stntype': self.aisteststn.stntype } self.assertDictEqual(expect, found)
def test_empty_string_ais_sentence_payload_binary(self): """ an empty string should raise a NoBinaryData exception """ with self.assertRaises(binary.NoBinaryData): binary.ais_sentence_payload_binary('')