1
2
3 __version__ = '$Revision: 4799 $'.split()[1]
4 __date__ = '$Date: 2006-09-25 11:09:02 -0400 (Mon, 25 Sep 2006) $'.split()[1]
5 __author__ = 'John Doe FIX: put in the authors name'
6
7 __doc__='''
8 Handle creation and extraction of NMEA strings. Maybe need a separate VDM class like ais-py?
9
10 @requires: U{lxml<http://codespeak.net/lxml/>} - For libxml2 ElementTree interface. Not actually required for the template, but this is just a demo or requirements.
11 @requires: U{Python<http://python.org/>} >= 2.4
12 @requires: U{epydoc<http://epydoc.sourceforge.net/>} >= 3.0alpha3
13 @requires: BitVector
14
15 @author: U{'''+__author__+'''<http://schwehr.org/>}
16 @version: ''' + __version__ +'''
17 @copyright: 2006
18 @var __date__: Date of last svn commit
19 @undocumented: __version__ __author__ __doc__ myparser
20 @since: 2006-Sep-26 FIX: replace with the file creation date
21 @status: under development
22 @organization: U{CCOM<http://ccom.unh.edu/>} - FIX: if not CCOM change the name and link
23
24 @license: GPL v2
25 '''
26
27
28 import time, sys
29
30
31 import binary
32
33 import re
34
35
37 """
38 Take a NMEA 0183 string and compute the checksum.
39 @param data: NMEA message. Leading ?/! and training checksum are optional
40 @type data: str
41 @return: hexidecimal value
42 @rtype: str
43
44 Checksum is calculated by xor'ing everything between ? or ! and the *
45
46 >>> checksumStr("!AIVDM,1,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0*09")
47 '09'
48 >>> checksumStr("AIVDM,1,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0")
49 '09'
50 """
51
52
53 if data[0]=='!' or data[0]=='?': data = data[1:]
54 if data[-1]=='*': data = data[:-1]
55 if data[-3]=='*': data = data[:-3]
56
57 sum=0
58 for c in data: sum = sum ^ ord(c)
59 sumHex = "%x" % sum
60 if len(sumHex)==1: sumHex = '0'+sumHex
61 return sumHex.upper()
62
63
64
65
66 nmeaChecksumRegExStr = r"""\,[0-9]\*[0-9A-F][0-9A-F]"""
67 nmeaChecksumRE = re.compile(nmeaChecksumRegExStr)
68
70 """Return True if the string checks out with the checksum
71
72 @param allowTailData: Permit handing of Coast Guard format with data after the checksum
73 @param data: NMEA message. Leading ?/! are optional
74 @type data: str
75 @return: True if the checksum matches
76 @rtype: bool
77
78 >>> isChecksumValid("!AIVDM,1,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0*09")
79 True
80
81 Corrupted:
82
83 >>> isChecksumValid("!AIVDM,11,1,,B,35MsUdPOh8JwI:0HUwquiIFH21>i,0*09")
84 False
85 """
86
87 if allowTailData:
88 match = nmeaChecksumRE.search(nmeaStr)
89 if not match: return False
90 nmeaStr = nmeaStr[:match.end()]
91
92
93
94 if nmeaStr[-3]!='*':
95 print 'FIX: warning... bad nmea string'
96 return False
97 checksum=nmeaStr[-2:]
98 if checksum.upper()==checksumStr(nmeaStr).upper(): return True
99 return False
100
101 -def buildNmea(aisBits,prefix='!',serviceType='AI',msgType='VDM',channelSeq=None,channel='A'):
102 '''
103 Create one long oversized nmea string for the bits
104 @param aisBits: message payload
105 @type aisBits: BitVector
106 @param prefix: '!' or '$' what is the difference?
107 @param serviceType: 'can this be anything other than AI?
108 @param msgType: VDM. Should not be VDO (own ship)
109 @param channelSeq: 1-9 or None
110 @param channel: AIS channel A or B
111 @todo: sync names of prefix and serviceType to NMEA spec.
112 @see: reference the appropriate spec documents for all this stuff.
113 '''
114
115 rList = [prefix,serviceType,msgType,',1,1,']
116 if None != channelSeq: rList.append(str(channelSeq))
117 rList.append(',')
118 rList.append(channel)
119 rList.append(',')
120
121 payloadStr,pad = binary.bitvectoais6(aisBits)
122 rList.append(payloadStr)
123 rList.append(','+str(pad))
124 rStr = ''.join(rList)
125 rStr += '*'+checksumStr(rStr)
126
127 return rStr
128
129
130 if __name__=='__main__':
131 from optparse import OptionParser
132 myparser = OptionParser(usage="%prog [options]",
133 version="%prog "+__version__)
134 myparser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true',
135 help='run the documentation tests')
136 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
137 help='run the tests run in verbose mode')
138 (options,args) = myparser.parse_args()
139
140 success=True
141
142 if options.doctest:
143 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
144 argvOrig = sys.argv
145 sys.argv= [sys.argv[0]]
146 if options.verbose: sys.argv.append('-v')
147 import doctest
148 numfail,numtests=doctest.testmod()
149 if numfail==0: print 'ok'
150 else:
151 print 'FAILED'
152 success=False
153 sys.argv = argvOrig
154 del argvOrig
155
156 if not success:
157 sys.exit('Something Failed')
158
159 del success
160