M0r13n / pyais

AIS message decoding and encoding in Python (AIVDM/AIVDO)
MIT License
176 stars 61 forks source link

Determination of the communication status field #136

Closed prefixFelix closed 4 months ago

prefixFelix commented 4 months ago

Problem

It seems that the current method of detecting and distinguishing communication status fields is not entirely reliable. For example, the following example incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA.

decoded = pyais.decode('!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51')
com = decoded.get_communication_state()
print(f"SOTDMA: {decoded.is_sotdma}, ITDMA: {decoded.is_itdma}")

According to the following code it is checked whether the 20th bit of the communications status is set or not. However, as the com state is only 19 bits long, a bit from the previous data fields is checked here and not one from the com state. In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state.

@property
def is_sotdma(self) -> bool:
    """The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
    return self.radio <= self.MAX_COMM_STATE_VALUE

@property
def is_itdma(self) -> bool:
    """The radio status field has it's 20th bit (MSB) set to 1"""
    return self.radio > self.MAX_COMM_STATE_VALUE

Solution

It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types. The existing implementation can be used here, as with these message types the bit before the actual com state determines its type.

M0r13n commented 4 months ago

@prefixFelix

I think your root cause analysis is not quite correct.

However, as the com state is only 19 bits long, a bit from the previous data fields is checked here and not one from the com state. In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state.

Pyais indeed checks the 20th bit, but there is no overflow because the radio field is an integer (the bit array is not accessed). Thus, is_sotdma is always true for messages of type 1, 2, and 3, as the radio field is always smaller than 0x7ffff (2^19 - 1) for these messages (the radio attribute is forced into 19 bits during decoding).

Your proposed solution is correct nonetheless. :+1:

M0r13n commented 4 months ago

Would you like to take a look at my proposed fix?

-> https://github.com/M0r13n/pyais/pull/137

prefixFelix commented 4 months ago

You're right, I misinterpreted the initial situation.

Your pull request looks logical and correct to me. Thanks for the quick fix.

M0r13n commented 4 months ago

Nice! I will merge tomorrow and create a new release

M0r13n commented 4 months ago

The fix is available in version v2.6.5.