Snippet - Decoder for AIVDM/AIVDO protocols (NMEA/AIS)

I just posted Decoder for AIVDM/AIVDO protocols (NMEA/AIS) on Codeshare. Feel free to discuss and make suggestions here.

6 Likes

Cool code!!! I did not even think to pack data of three byte!!! this is smart and results in smaller code than mine

Do you have already coded a method that return the string from 6bit ASCII? Such that in message type 14 from bits 40 to …

I spend a hard time on it without success for the moment.
Here’s my understanding:
Let’s consider that the payload is:

O5`4tlt:1@ E=@
in byte representation:
062 062 079 053 096 052 116 108 116 058 049 064 069 061 064
Converted in six bit gives (substract 48 and if result is >40 substract again 8:
014 014 031 005 040 004 060 052 060 010 001 016 021 013 016
which in binary is:
00001110 00001110 00011111 00000101 00101000 00000100 00111100 00110100 00111100 00001010 00000001 00010000 00010101 00001101 00010000
On which we should remove the two MSB of each byte
001110 001110 011111 000101 101000 000100 111100 110100 111100 001010 000001 010000 010101 001101 010000
Now we can process the sentence
001110 Type de message 14
00 Repeat indicator 0
111001111100010110100000010011 MMSI: 972122131
11 Not used
001101 ->13 M
001111 ->15 O
000010 ->2 B
100000 ->32 SPACE
010100 ->20 T
000101 ->5 E
010011 ->19 S
010100 ->20 T
00

@ leforban - I updated the snippet to include string decoding. Note that the detection of trailing pad bits is a bit tricky. It is hard to tell whether the last character is an @ or padding bits. Generally, a perfectly right-aligned trailing sextet of zeros will be padding, so in my code, that character is dropped. If you know the length of your output string, you can remove the if at line 74. But in Type14 messages for example, you never know the string length and this kind of heuristic is needed because AIS messages must be padded to 8 bits, which can result in an ambiguous final character.

1 Like

This works like a charm! I reread the code and you perform a comparison with 39 in the six bit convert method whereas I found 40 in the following doc GPSD redirection page

That’s a great document, but there are some errors and ambiguity in it - that “40” is one of them. I think they meant >= 40. Anyway, a little squinting at the ascii chart and the table at the head of that link (which is also my go-to reference) sorted that out.

1 Like

…and if this has helped you out, sharing a little like-button action is appreciated, too.

1 Like

like job done :wink:

You are right if the table is good, then the value is 39! :clap:

Now I am facing an other problem to extract latitude and longitude. When values are positive, results are good but for negative number I do not succeed to compute it properly. And more than that I do not succeed to check the good bit to see if the value is negative or positive…

I tought to use the following code to detect the value of the MSB but it does not seem to work as I have expected.

var iByte2 = (start + len-1 ) / 8;
var iBit2 =  ((start + len-1 ) % 8);
if ((source[iByte2] & (1 << iBit2)) != 0) //if MSB is 1 then it is a negative number
 {
}


Any clue?

Your snippet is cut off.

Anyways to check MSB you need to do :

1 Like

Main challenge for me was to extract the byte value…

Anyway here is my code to extract signed Int (used in Lat Long value):

        
public static float GetBitsAsFloatInt(byte[] source, int start, int len)
        {
            int result = 0;
            string sb = "";
            bool is_neg = false;
            for (int i = start; i < (start + len); ++i)
            {
                var iByte = i/8;
                var iBit = 7 - (i%8);
                if (i == start)
                {
                    if ((source[iByte] & (1 < < iBit)) != 0)
                    {
                        is_neg = true;
                    }
                }
                else
                {
                    if(!is_neg)
                        result = result < < 1 | (((source[iByte] & (1 < < iBit)) != 0) ? 1 : 0);
                    else result = result < < 1 | (((source[iByte] & (1 < < iBit)) != 0) ? 0 : 1);
                }
             
            }
            if (is_neg)
                result = -result - 1;
           return result;
        }

@ mcalsyn: if you find the code efficient, feel free to add it in the codeshare!

I used something like this. It’s not actually all that much more efficient time-wise but it results in smaller code footprint :

 source, int start, int len)
{
    var value = GetBitsAsUnsignedInt(source, start, len);
    if ((value & (1 < < (len-1)))!=0)
    {
        // perform 32 bit sign extension
        for (var i = len; i < 32; ++i )
        {
            value |= (1 < < i);
        }
    }
    return value;
}

I’ve updated the snippet.

1 Like

Absolutely correct for any sane system, but not in AIS. In AIS, you can have odd-length (e.g., 27 bit) integers, so you can’t rely on the sign bit being in the MSb of a given byte.

1 Like