I found a file that contains about 85,000 messages (3 minute live AISHub feed), but I can't make heads or tails of it.



Is there a schema somewhere?


There is a good github repository of Kurt Schwehr who works at the Center for Coastal and Ocean Mapping (to track whale activities for example). There you will find a decoder and documents to understand the nmea messages (mostly links menioned by the posts of @ianmayo and @GID Dev). Here is a small howto running under LINUX and python 2.7.

To get some code running, you need git a C++ compiler, the python setup environment, cmake. Download the data from

$ git clone https://github.com/schwehr/libais.git

and follow the installation instruction on/at the github page or run

$ cd YOUR_BUILD_PATH/libais
$ cmake .  # to bulid the Makefile 
$ make     # to build the libais C++
$ python setup.py build # to build the python stuff
$ sudo python setup.py install # to deploy it

After all you should have the libraries in your python environment.

 $ ls /usr/local/lib/python2.7/dist-packages/
 easy-install.pth  libais-0.16-py2.7-linux-x86_64.egg

 $ ls /usr/local/lib/python2.7/dist-packages/libais-0.16-py2.7-linux-x86_64.egg
 ais  _ais.py  _ais.pyc  _ais.so  EGG-INFO  test

Here is some quick and dirty code in a script called test-ais.py to get the unix like head & tail behavoir. I use json as a "clear text pretty printer".


# To supress the warning ...could be done better    
# FutureWarning: The stream module is deprecated and will be removed in 1.0
# https://github.com/schwehr/libais/blob/master/ais/stream/__init__.py
# coded in in __init__.py line 10-14
import warnings

# import json module for pretty print
import json

# import ais.stream module to decode
# a ais binary nmea message to json 
import ais.stream

# import sys module to read stuff from
# standard input STDIN
import sys

# decode a file or somthing form the STDIN
f = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin

# Iterate over the messages 
for msg in ais.stream.decode(f):
    # make a json pretty print for each message
    print json.dumps(msg, indent=4, sort_keys=True)


Assuming that the nmea-samples file is in a data directory, you can filter out the line you want to show cat, head and tail...

$ tail -1 data/nmea-sample | ./test-ais.py
    "day": 14, 
    "fix_type": 1, 
    "hour": 11, 
    "id": 4, 
    "minute": 33, 
    "mmsi": 2320717, 
    "month": 3, 
    "position_accuracy": 0, 
    "raim": false, 
    "repeat_indicator": 3, 
    "second": 30, 
    "slot_offset": 2250, 
    "slot_timeout": 0, 
    "spare": 0, 
    "sync_state": 0, 
    "transmission_ctl": 0, 
    "x": -5.782454967498779, 
    "y": 57.842193603515625, 
    "year": 2012

Starting from the json code, it should be easy to go on with further formatting and storing stuff.


The AIVDM/AIVDO protocol decoding site contains the answer but there is a lot to sift through there. To answer the question posed, this is from the aforementioned site in that format:

Here is a typical AIVDM data packet:


And here is what the fields mean:

Field 1, !AIVDM, identifies this as an AIVDM packet.

Field 2 (1 in this example) is the count of fragments in the currently accumulating message. The payload size of each sentence is limited by NMEA 0183’s 82-character maximum, so it is sometimes required to split a payload over several fragment sentences.

Field 3 (1 in this example) is the fragment number of this sentence. It will be one-based. A sentence with a fragment count of 1 and a fragment number of 1 is complete in itself.

Field 4 (empty in this example) is a sequential message ID for multi-sentence messages.

Field 5 (B in this example) is a radio channel code. AIS uses the high side of the duplex from two VHF radio channels: AIS Channel A is 161.975Mhz (87B); AIS Channel B is 162.025Mhz (88B). In the wild, channel codes 1 and 2 may also be encountered; the standards do not prescribe an interpretation of these but it’s obvious enough..

Field 6 (177KQJ5000G?tO`K>RA1wUbN0TKH in this example) is the data payload. We’ll describe how to decode this in later sections.

Field 7 (0) is the number of fill bits requires to pad the data payload to a 6 bit boundary, ranging from 0 to 5. Equivalently, subtracting 5 from this tells how many least significant bits of the last 6-bit nibble in the data payload should be ignored. Note that this pad byte has a tricky interaction with the <[ITU-1371]> requirement for byte alignment in over-the-air AIS messages; see the detailed discussion of message lengths and alignment in a later section.

The *-separated suffix (5C) is the NMEA 0183 data-integrity checksum for the sentence, preceded by "". It is computed on the entire sentence including the AIVDM tag but excluding the leading "!".

Furthermore, the important part here is actually field 6, so if you sift through the site some more, you'll get the answer: that field 6 data payload contains a ton (no really, a ton!) of various fields within it. So you can write your own code to parse it out, or alternatively use the github repo posted in the other answer which contains various SDKs/APIs that should probably have what you need (depending on where you got your AIS data): https://github.com/bcl/aisparser


Here's a schema for the format, it seems quite thorough:

AIVDM/AIVDO protocol decoding

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.