Package ais :: Module nmea
[hide private]
[frames] | no frames]

Module nmea

source code

Handle creation and extraction of NMEA strings. Maybe need a separate VDM class like ais-py?


Requires:

Author: Kurt Schwehr

Version: 6288

Copyright: 2006

Since: 2006-Sep-26 FIX: replace with the file creation date

Status: under development

Organization: CCOM - FIX: if not CCOM change the name and link

License: GPL v2

Note: This package does not respence the maximum number of characters per line that is required in the NMEA specification.

Date: 2007-06-04

Functions [hide private]
str
checksumStr(data, verbose=False)
Take a NMEA 0183 string and compute the checksum.
source code
bool
isChecksumValid(nmeaStr, allowTailData=True, verbose=False)
Return True if the string checks out with the checksum
source code
 
buildNmea(aisBits, prefix='!', serviceType='AI', msgType='VDM', channelSeq=None, channel='A')
Create one long oversized nmea string for the bits
source code
str
cabEncode(TransA=False, TransB=False, Restart=False, Reset=False, prefix='AI')
CAB - Control AIS Base Station.
source code
dict
cabDecode(msg, validate=True)
Note that ZZ is probably not valid in this next example
source code
 
verQuery(prefix='xx', appendEOL=True)
Ask for the version string from a base station
source code
 
encodeQuery(query, prefix='xx', appendEOL=True)
Ask for the version string from a base station
source code
 
acaEncode(seqnum='', north='', east='', south='', west='', transitionSize='', chanA='2087', chanAbandwidth='0', chanB='2088', chanBbandwidth='0', txrxMode='', power='', infosrc='', timeinuse='', prefix='xx', appendEOL=True, validate=True)
Encode an AIS Regional Channel Assignment Message.
source code
 
acaDecode(msg, validate=True)
Decode AIS Regional Channel Assignment Message.
source code
 
cbmDecode(msg, validate=True)
Decode Configure Base Station Message Broadcast Reporting Rates message.
source code
 
dlmDecode(msg, validate=True)
Decode Data Link Management slot allocation for Base Station nmea message
source code
str
bbmEncode(totSent, sentNum, seqId, aisChan, msgId, data, numFillBits, prefix='xx', appendEOL=True, validate=True)
Encode a binary broadcast message.
source code
dict
bbmDecode(msg, validate=True)
Decode a binary broadcast message NMEA string
source code
dict
bcfDecode(msg, validate=True)
Decode a General Base Station Configuration NMEA string.
source code
 
bcfEncode(mmsi='', posSrc='', lat='', latNS='', lon='', lonEW='', posAccuracy='', RxChanA='', RxChanB='', TxChanA='', TxChanB='', PowerA='', PowerB='', VDLretries='', RepeatIndicator='', BaseStationTalkerID='', prefix='AI', appendEOL=True, validate=True)
Enocde a General Base Station Configuation Message.
source code
Variables [hide private]
  EOL = '\r\n'
DOS style end-of-line (<cr><lf>) for talking to AIS base stations
  nmeaChecksumRegExStr = '\\*[0-9A-F][0-9A-F]'
  nmeaChecksumRE = re.compile(r'\*[0-9A-F][0-9A-F]')
  txrxLUT = {0: 'tx a and b, rx on a and b', 1: 'tx a, rx a and ...
Transmit and Received modes.
  acaInfoSrcLUT = {'A': 'ITU-R M.1371 message 22: addressed mess...
Lookup table of codes to use in the Information Source of an ACA message.
  powerLUT = {0: 'high', 1: 'low'}
  powerEncode = {'high': 0, 'low': 1}
  ownershipLUT = {'C': 'clear reservation', 'L': 'local', 'R': '...
  posSrcLUT = {0: 'surveyed', 1: 'internal EPFD in use', 2: 'ext...
Position source used in a BCF message
Function Details [hide private]

checksumStr(data, verbose=False)

source code 

Take a NMEA 0183 string and compute the checksum.

Checksum is calculated by xor'ing everything between ? or ! and the *
>>> checksumStr("!AIVDM,1,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0*09")
'09'
>>> checksumStr("AIVDM,1,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0")
'09'
>>> checksumStr('$AIACA,0,,,,,,,,,5,2087,0,2088,0,0,0,I,1,000000*15')
'15'
This is an example I made up
>>> checksumStr('$xxCAB,1,1,1,1*5D')
'40'
Parameters:
  • data (str) - NMEA message. Leading ?/! and training checksum are optional
Returns: str
hexidecimal value

isChecksumValid(nmeaStr, allowTailData=True, verbose=False)

source code 
Return True if the string checks out with the checksum
>>> isChecksumValid("!AIVDM,1,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0*09")
True
Corrupted:
>>> isChecksumValid("!AIVDM,11,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0*09")
False
>>> isChecksumValid('$AIACA,0,,,,,,,,,5,2087,0,2088,0,0,0,I,1,000000*15')
True
Parameters:
  • allowTailData - Permit handing of Coast Guard format with data after the checksum
  • nmeaStr (str) - NMEA message. Leading ?/! are optional
Returns: bool
True if the checksum matches

buildNmea(aisBits, prefix='!', serviceType='AI', msgType='VDM', channelSeq=None, channel='A')

source code 
Create one long oversized nmea string for the bits
Parameters:
  • aisBits (BitVector) - message payload
  • prefix - '!' or '$' what is the difference?
  • serviceType - 'can this be anything other than AI?
  • msgType - VDM. Should not be VDO (own ship)
  • channelSeq - 1-9 or None
  • channel - AIS channel A or B

To Do: sync names of prefix and serviceType to NMEA spec.

See Also: reference the appropriate spec documents for all this stuff.

cabEncode(TransA=False, TransB=False, Restart=False, Reset=False, prefix='AI')

source code 
CAB - Control AIS Base Station. Defaults to a safe state with everything shutdown. 62320-1/CDV, 80/427/CDV, Page 77, A.1.7
>>> cabEncode()
'$AICAB,0,0,,*48'
>>> cabEncode(prefix='xx')
'$xxCAB,0,0,,*40'

Note that xx is probably not valid in this next example, but it is used by L3

Made up example:
>>> cabEncode(True,True,True,True,prefix='xx')
'$xxCAB,1,1,1,1*40'
>>> cabEncode(True,True,prefix='L3')
'$L3CAB,1,1,,*3F'
Parameters:
  • TransA (bool) - Transmissions enabled on channel A
  • TransB (bool) - Transmissions enabled on channel B
  • Restart (bool) - If true, command AIS Base station to restart operations in last known configuration
  • Reset (bool) -
  • prefix (str) - string to put between the $ and CAB
Returns: str
A CAB NMEA string

cabDecode(msg, validate=True)

source code 
>>> cabDecode('$AICAB,,,,*48')
{'Reset': False, 'nmeaPrefix': 'AI', 'nmeaCmd': 'CAB', 'TransB': False, 'TransA': False, 'Restart': False}
Note that ZZ is probably not valid in this next example
>>> cabDecode('$ZZCAB,1,1,1,1*40')
{'Reset': False, 'nmeaPrefix': 'ZZ', 'nmeaCmd': 'CAB', 'TransB': True, 'TransA': True, 'Restart': True}
Parameters:
  • msg (str) - NMEA string of a CAB message
  • validate (bool) - Set to False to turn off validation for speed.
Returns: dict
lookup table of key/values

To Do: How do I make stable doctests with dictionary returns

To Do (FIX): throw an exception if not valid

verQuery(prefix='xx', appendEOL=True)

source code 
Ask for the version string from a base station
>>> verQuery(appendEOL=False)
'$xxBSQ,VER*2D'
>>> verQuery('AI',appendEOL=False)
'$AIBSQ,VER*25'

encodeQuery(query, prefix='xx', appendEOL=True)

source code 
Ask for the version string from a base station
>>> encodeQuery('VER',appendEOL=False)
'$xxBSQ,VER*2D'
>>> encodeQuery('VER', prefix='L3', appendEOL=False)
'$L3BSQ,VER*52'
>>> encodeQuery('VER','AI',appendEOL=False)
'$AIBSQ,VER*25'
>>> encodeQuery('ACA',appendEOL=False)
'$xxBSQ,ACA*2F'
>>> encodeQuery('CBM',appendEOL=False)
'$xxBSQ,CBM*20'
>>> encodeQuery('DLM',appendEOL=False)
'$xxBSQ,DLM*29'
>>> encodeQuery('DLM', prefix='L3', appendEOL=False)
'$L3BSQ,DLM*56'
>>> encodeQuery('BCF', prefix='L3', appendEOL=False)
'$L3BSQ,BCF*54'
>>> encodeQuery('CAB', prefix='L3', appendEOL=False)
'$L3BSQ,CAB*53'

acaEncode(seqnum='', north='', east='', south='', west='', transitionSize='', chanA='2087', chanAbandwidth='0', chanB='2088', chanBbandwidth='0', txrxMode='', power='', infosrc='', timeinuse='', prefix='xx', appendEOL=True, validate=True)

source code 
Encode an AIS Regional Channel Assignment Message.
>>> acaEncode(appendEOL=False)
'$xxACA,,,,,,,2087,0,2088,0,,,,*4C'
Set to high power
>>> acaEncode(power=powerEncode['high'],appendEOL=False)
'$xxACA,,,,,,,2087,0,2088,0,,0,,*7C'
Set to low power
>>> acaEncode(power=powerEncode['low'],appendEOL=False)
'$xxACA,,,,,,,2087,0,2088,0,,1,,*7D'
Parameters:
  • seqnum (int) -
  • north (float string) - llll.ll northern boundary
  • east (float string) - yyyyy.yy (perhaps y was a bad choice)
  • south (float string) -
  • west (float string) - yyyyy.yy (perhaps y was a bad choice)
  • transitionSize (int) - (nautical miles)
  • chanA (int) - Channel A number
  • chanAbandwidth (int) - 0 is the default, 1 is 12.5 kHz
  • chanB (int) - Channel B number
  • chanBbandwidth (int) - 0 is the default, 1 is 12.5 kHz
  • txrxMode (int) - See txrxLUT for the numbers
  • power (int) - 0 for high, 1 for low
  • infosrc (letter) - should be empty for sending to an AIS device. See acaInfoSrcLUT
  • timeinuse (hhmmss.ss) - should be empty for sending to an AIS device. Time in UTC that the device changed to this state
  • prefix (Two letters) - Vendor specific prefix. FIX: what should be used here?
  • appendEOL (bool) - Do you want a DOS end of line appended?
  • validate (bool) - Set to true to validate the message

See Also: 61993-2 Page 87.

acaDecode(msg, validate=True)

source code 

Decode AIS Regional Channel Assignment Message.

This is an example of an unconfigured base station, plus there is a USCG timestamp at the end.
>>> acaDecode('$AIACA,0,,,,,,,,,5,2087,0,2088,0,0,0,I,1,000000*15,1172786646.1')
{'inuse': '1', 'north': None, 'txrxMode': '0', 'power': '0', 'nmeaPrefix': 'AI', 'timeinuse': '000000', 'seqnum': '0', 'chanBbandwidth': '0', 'nmeaCmd': 'ACA', 'chanAbandwidth': '0', 'west': None, 'transitionSize': '5', 'infosrc': 'I', 'east': None, 'chanA': '2087', 'south': None, 'chanB': '2088'}

See Also: 61993-2 Page 87.

To Do: get a complete example to decode as a doctest

cbmDecode(msg, validate=True)

source code 
Decode Configure Base Station Message Broadcast Reporting Rates message.
>>> cbmDecode('$AICBM,61,76,35,2,60,999,100,999,52,999,1,60,999,100,999*55,1172787005.46')
{'msg17chanAnumslots': '1', 'nmeaPrefix': 'AI', 'msg4slot': '61', 'msg17chanAslotinterval': '999', 'nmeaCmd': 'CBM', 'msg20chanAslotinterval': '999', 'msg20chanAstartslot': '60', 'msg17chanAstartslot': '52', 'msg22chanAslotinterval': '999', 'msg22chanAstartslot': '100'}

See Also: 62320-1/CDV 80/427/CDV page 78, A.1.8

dlmDecode(msg, validate=True)

source code 
Decode Data Link Management slot allocation for Base Station nmea message
>>> dlmDecode ('$AIDLM,0,A,L,0,2,7,540,L,4,1,7,250,L,2511,1,7,0,,,,,*40,1172787005.5')
{'nmeaPrefix': 'AI', 'timeout3': '7', 'timeout2': '7', 'timeout1': '7', 'timeout4': '', 'startslot2': '4', 'startslot3': '2511', 'incr4': '*40', 'incr3': '0', 'incr2': '250', 'incr1': '540', 'aisChannel': 'A', 'seqNum': '0', 'startslot1': '0', 'startslot4': '', 'nmeaCmd': 'DLM', 'ownership4': '', 'ownership3': 'L', 'ownership2': 'L', 'ownership1': 'L', 'numslots4': '', 'numslots1': '2', 'numslots2': '1', 'numslots3': '1'}

See Also: 62320-1/CDV 80/427/CDV page 79, A.1.9

bbmEncode(totSent, sentNum, seqId, aisChan, msgId, data, numFillBits, prefix='xx', appendEOL=True, validate=True)

source code 

Encode a binary broadcast message.

I have no idea what this message says...

!AIVDM,1,1,,A,85NqMF1Kf=Vsdt`l;0bnfFjd<uQeT2p<vmIRTB=mM5mtIT;sUL2t,0*54,rs003669982,1172918061

>>> bbmEncode(1,1,0,3,8,'Fs[Ifs?:=2h:ec]dc3?HKI0f3?eFHa4[MGAMO6I2vqG0g',4)
'!xxBBM,1,1,0,3,8,Fs[Ifs?:=2h:ec]dc3?HKI0f3?eFHa4[MGAMO6I2vqG0g,4*32'
Here are the test messages from 62320-1 80/427/CDV Page 58, 10.2.2.1.2:
>>> bbmEncode(1,1,0,1,8,'7E3B3C3E7E',0,appendEOL=False)
'!xxBBM,1,1,0,1,8,7E3B3C3E7E,0*1F'
Make it go on both
>>> bbmEncode(1,1,0,3,8,'7E3B3C3E7E',0,appendEOL=False)
'!xxBBM,1,1,0,3,8,7E3B3C3E7E,0*1D'
Parameters:
  • totSent (int) - Total number of sentences needed for the message (1-9)
  • sentNum (int) - Which sentence is this in the series (1-9)
  • seqId (int) - need to increment this for each message??!?!? (0-9) Linked to ABK
  • aisChan (str) - AIS channel to use to send the message
    1. No channel preference
    2. AIS Channel A
    3. AIS Channel B
    4. Broadcast on both A and B
  • msgId (int) - AIS message 8 (binary broadcast message) or 14 (safety related broadcast)
  • data - Content of the binary data. First sentence must be 58 characters or less. The rest can be up to 60 characters.
  • numFillBits (int) - Number of bits of padding in the last character of the data (0-5)
Returns: str
nmea string

To Do: put in some doc tests with know messages and what would be received as the VDM message(s)

See Also: IEC-PAS 61162-100 80/330/PAS, Page 19

bbmDecode(msg, validate=True)

source code 
Decode a binary broadcast message NMEA string
>>> bbmDecode('!xxBBM,1,1,0,3,8,Fs[Ifs?:=2h:ec]dc3?HKI0f3?eFHa4[MGAMO6I2vqG0g,4*32')
{'numFillBits': '4', 'nmeaPrefix': 'xx', 'msgId': '8', 'aisChan': '3', 'data': 'Fs[Ifs?:=2h:ec]dc3?HKI0f3?eFHa4[MGAMO6I2vqG0g', 'seqId': '0', 'nmeaCmd': 'BBM', 'sentNum': '1', 'totSent': '1'}
Parameters:
  • msg (str) - NMEA string of a CAB message
  • validate (bool) - Set to False to turn off validation for speed.
Returns: dict
lookup table of key/values
To Do:
  • make the doctest stable
  • doctests with known messages

See Also: IEC-PAS 61162-100 80/330/PAS, Page 19

bcfDecode(msg, validate=True)

source code 
Decode a General Base Station Configuration NMEA string.
>>> bcfDecode('$AIBCF,12345,7,4731.0,N,05249.0,W,1,2087,2088,2087,2088,1,1,3,0,AI*51')
{'posAccuracy': '1', 'nmeaPrefix': 'AI', 'TxChanB': '2088', 'mmsi': '12345', 'RepeatIndicator': '0', 'lon': -5249.0, 'PowerB': '1', 'posSrc': '7', 'nmeaCmd': 'BCF', 'PowerA': '1', 'BaseStationTalkerID': 'AI', 'RxChanB': '2088', 'lat': 4731.0, 'RxChanA': '2087', 'TxChanA': '2087', 'VDLretries': '3'}
Parameters:
  • msg (str) - NMEA string of a CAB message
  • validate (bool) - Set to False to turn off validation for speed.
Returns: dict
lookup table of key/values

See Also: 62320-1/CDV 80/427/CDV, Page 76, A.1.6

bcfEncode(mmsi='', posSrc='', lat='', latNS='', lon='', lonEW='', posAccuracy='', RxChanA='', RxChanB='', TxChanA='', TxChanB='', PowerA='', PowerB='', VDLretries='', RepeatIndicator='', BaseStationTalkerID='', prefix='AI', appendEOL=True, validate=True)

source code 
Enocde a General Base Station Configuation Message. Defaults to not changing anything
>>> bcfEncode(appendEOL=False)
'!AIBCF,,,,,,,,,,,,,,,,AI*47'
>>> bcfEncode(12345,7,4731.0,'N',5249.0,'W',1,2087,2088,2087,2088,1,1,3,0,'AI',appendEOL=False)
'!AIBCF,12345,7,4731.0,N,5249.0,W,1,2087,2088,2087,2087,1,1,3,0,AI*6E'

This is what the L-3 base station returns this. I am not sure what the format of the position is.

'$AIBCF,12345,7,4731.0,N,05249.0,W,1,2087,2088,2087,2088,1,1,3,0,AI*51'

Set to low power:
>>> bcfEncode(PowerA=powerEncode['low'],PowerB=powerEncode['low'],appendEOL=False)
'!AIBCF,,,,,,,,,,,,1,1,,,AI*47'
My MMSI that is registered for testing at UNH
>>> bcfEncode(mmsi=338040883, appendEOL=False,prefix='L3')
'!L3BCF,338040883,,,,,,,,,,,,,,,L3*78'
Parameters:
  • mmsi (int) - UserID for the base station
  • posSrc (int) - See posSrcLUT. 0..6
  • lat (float) - Surveyed latitude position. FIX: how is this encoded!!?!?!
  • latNS (str(1)) - N or S
  • lon (float) - Suveyed longitude position. FIX: how is this encoded!!?!?!
  • lonEW (str(1)) - E or W
  • posAccuracy (int) - 0 for low, 1 for hight
  • RxChanA (int) - Receive channel to use (default is 2087)
  • RxChanB (int) - Receive channel to use (default is 2088)
  • TxChanA (int) - Transmit channel to use (default is 2087)
  • TxChanB (int) - Transmit channel to use (default is 2089)
  • PowerA (int) - Transmit power for channel A - 0 high (12.5 W), 1 low (Nominal 2 watts). FIX: Seems there is a disagreement between specs on the power levels. 2 or 5 watts for low? 2..9 reservered
  • PowerB (int) - Transmit power for channel A - 0 high (12.5 W), 1 low (Nominal 2 watts). FIX: Seems there is a disagreement between specs on the power levels. 2 or 5 watts for low? 2..9 reservered
  • VDLretries (int) - FIX: what does this mean?
  • RepeatIndicator (int) - ?
  • BaseStationTalkerID (str(2)) - Usually AI. The prefix that does before NMEA string identifiers
  • prefix (Two letters) - Vendor specific prefix. FIX: what should be used here?
  • appendEOL (bool) - Do you want a DOS end of line appended?
  • validate (bool) - Set to true to validate the message

See Also: 62320-1/CDV 80/427/CDV, Page 76, A.1.6


Variables Details [hide private]

txrxLUT

Transmit and Received modes. See Page 88 61993-2 and XXXX???
Value:
{0: 'tx a and b, rx on a and b',
 1: 'tx a, rx a and b',
 2: 'tx b, rx a and b',
 3: 'no tx, rx a and b',
 4: 'no tx, rx a',
 5: 'no tx, no rx'}

acaInfoSrcLUT

Lookup table of codes to use in the Information Source of an ACA message. See acaEncode() and acaDecode()
Value:
{'A': 'ITU-R M.1371 message 22: addressed message',
 'B': 'ITU-R M.1371 message 22: broadcast message',
 'C': 'IEC 61162-1 AIS Channel Assignment setence',
 'D': 'DSC Channel 70 telecommand',
 'I': 'Why is the L-3 unit returning I???  It is not defined on page 8\
8 or 61993-2',
 'M': 'Operator manual input'}

ownershipLUT

Value:
{'C': 'clear reservation', 'L': 'local', 'R': 'remote'}

posSrcLUT

Position source used in a BCF message

Note: EPFD = el;ectronic position fixing device

See Also: 62320-1 80/427/CDV Page 76 A.1.6

Value:
{0: 'surveyed',
 1: 'internal EPFD in use',
 2: 'external EPFD in use',
 3: 'internal EPFD in use with auto fallback to surveyed',
 4: 'internal EPFD in use with auto fallback to external EPFD',
 5: 'external EPFD in use with auto fallback to surveyed',
 6: 'external EPFD in use with auto fallback to internal EPFD',
 7: 'FIX: What exactly is 7 supposed to be... not in the source list i\
...