wpietri / simpleais

A simple Python AIS parser intended for casual use.
GNU Lesser General Public License v3.0
15 stars 6 forks source link

Message type 24 (Class B static data) does not detect and correctly decode part B #7

Open astuder opened 7 months ago

astuder commented 7 months ago

Message type 24 comes in two variants, called "part A" and "part B". simpleais always returns both sets of fields.

Example message: !AIVDM,1,1,,B,H5NhCmTN7B=400500000001P0004,0*56

Decoded by simpleais:

type 24
repeat 0
mmsi 367793110
partno 1
shipname G!4#Q@@AP@@@@@@@X
ignored-160 4
shiptype Fishing
vendorid GRM
model 1
serial 5
callsign
to_bow 12
to_stern 0
to_port 0
to_starboard 0
mothership_mmsi 25165824
ignored-162 4
text None

Correct decoding (via Maritec):

type 24
repeat 0
mmsi 367793110
part_no 1
type fishing
vendorid GRMD@@E
callsign @@@@@@@
to_bow 12
to_stern 0
to_port 0
to_starboard 0
spare 4

The output for shipname may be uninitialized memory, at one occasion I got C: DRIVE - Edit: Never mind, there's a ship with that name :)

This type may require some custom handling as the documentation for AIVDM merged both definitions into the same table: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_24_static_data_report

astuder commented 7 months ago

Message types 22, 25 and 26 have a similar issue with the broadcast/addressed bit indicating different field decoding.

My first idea was to have an optional "type decoders" to post-process the message, that are invoked based on message type. The type decoders could be managed as a list in MessageDecoder similar to the field decoders.

But simpleais enumerating/decoding fields on access complicates things a bit..

How about a "subtype" structure in the MessageDecoder class with:

type
subtype_field_name
subtype_fields [subtype_values][fields]

For example for msg type 24:

type = 24
subtype_field_name = partno
subtype_fields = [
    [shipname, spare],
    [shiptype, vendorid, model, serial, callsign, to_bow, to_stern, to_port, to_starboard, mothership_mmsi, spare]
]

and then query that in the iterators and getters to only return applicable fields:

if requested_field_name in subtypes[field(subtype_field_name)].sub_type_fields:
    # field exists for this subtype
else:
    # field does not exist

caveat, mothership is even more complicated and overlaps with dimensions:

Interpretation of the 30 bits 132-162 in Part B is variable. If the MMSI at 8-37 is that of an auxiliary craft, the entry is taken to refer to a small attached auxiliary vessel and these 30 bits are read as the MMSI of the mother ship. Otherwise the 30 bits describe vessel dimensions as in Message Type 5.

According to [MMSI], an MMSI is associated with an auxiliary craft when it is of the form 98XXXYYYY, where (1) the '98' in positions 1 and 2 is required to designate an auxiliary craft, (2) the digits XXX in the 3, 4 and 5 positions are the MID (the three-digit country code as described in [ITU-MID]) and (3) YYYY is any decimal literal from 0000 to 9999.

wpietri commented 7 months ago

Thanks for mentioning this. Just to understand the priority, is this something you actively need? Or is it more a desire to have everything correct?

astuder commented 7 months ago

I can work around it with a few lines of Python code.

But for the common msg type 24 (sent by pleasure boats), it's a bit jarring to see the invalid names when using aist. Though that might be an oversight in aist as I now see that you already check for partno in there.

astuder commented 7 months ago

I implemented a fix in https://github.com/astuder/simpleais/commit/6105c45118753a36a4ed20d2b95bef23cbffc00e

It introduces the concept of subtypes, where the value of one field controls the decoding of other fields. Each unique subtype creates a trimmed instance of MessageDecoder, which is requested by the constructor of Sentence if applicable.

This fixes the issue with overlapping field definitions when decoding type 22 (channel management) and type 24 (static data report):

Sentence 1:
          text: !AIVDM,1,1,,B,H5Nv6n058TDj0tJ1HT=@u8T4000,2*3E
        length: 160
          type: 24
        repeat: 0
          mmsi: 368019160
        partno: 0
      shipname: ARIEL OF VICTORIA
   ignored-160: -

Sentence 2:
          text: !AIVDM,1,1,,B,H5Nv6n4T0000000G4:pojh1h5130,0*62
        length: 168
          type: 24
        repeat: 0
          mmsi: 368019160
        partno: 1
      shiptype: Sailing
      vendorid: 
         model: 0
        serial: 0
      callsign: WDJ8720
        to_bow: 14
      to_stern: 5
       to_port: 1
  to_starboard: 3
  mothership_mmsi: 29380675
   ignored-162: 0

mothership_mmsi still is a problem as its handling requires more complex rules depending on mmsi field. I haven't seen this in use in the wild yet.

This fix also improves support for dest_mmsi in type 25/26 (single/multiple slot binary message), but those messages probably still won't return the data field. Though, according to AIVDM documentation, these message types haven't been seen in the wild.

This branch also fixes warnings about escaping of backslashes in aivdm_translate.py