1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2007-02-08 $'.split()[1]
5 __author__ = 'xmlbinmsg'
6
7 __doc__='''
8
9 Autogenerated python functions to serialize/deserialize binary messages.
10
11 Generated by: ./aisxmlbinmsg2py.py
12
13 Need to then wrap these functions with the outer AIS packet and then
14 convert the whole binary blob to a NMEA string. Those functions are
15 not currently provided in this file.
16
17 serialize: python to ais binary
18 deserialize: ais binary to python
19
20 The generated code uses translators.py, binary.py, and aisstring.py
21 which should be packaged with the resulting files.
22
23
24 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3
25 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>}
26
27 @author: '''+__author__+'''
28 @version: ''' + __version__ +'''
29 @var __date__: Date of last svn commit
30 @undocumented: __version__ __author__ __doc__ parser
31 @status: under development
32 @license: Generated code has no license
33 '''
34
35 import sys
36 from decimal import Decimal
37 from BitVector import BitVector
38
39 import binary, aisstring
40
41
42 TrueBV = BitVector(bitstring="1")
43 "Why always rebuild the True bit? This should speed things up a bunch"
44 FalseBV = BitVector(bitstring="0")
45 "Why always rebuild the False bit? This should speed things up a bunch"
46
47
48 fieldList = [
49 'MessageID',
50 'RepeatIndicator',
51 'UserID',
52 'NavigationStatus',
53 'ROT',
54 'SOG',
55 'PositionAccuracy',
56 'Position_longitude',
57 'Position_latitude',
58 'COG',
59 'TrueHeading',
60 'TimeStamp',
61 'RegionalReserved',
62 'Spare',
63 'RAIM',
64 'syncstate',
65 'slottimeout',
66 'slotoffset',
67 ]
68
69 -def encode(params, validate=False):
70 '''Create a position binary message payload to pack into an AIS Msg position.
71
72 Fields in params:
73 - MessageID(uint): AIS message number. Must be 1 (field automatically set to "2")
74 - RepeatIndicator(uint): Indicated how many times a message has been repeated
75 - UserID(uint): Unique ship identification number (MMSI)
76 - NavigationStatus(uint): What is the vessel doing
77 - ROT(int): RateOfTurn
78 - SOG(udecimal): Speed over ground
79 - PositionAccuracy(uint): Accuracy of positioning fixes
80 - Position_longitude(decimal): Location of the vessel East West location
81 - Position_latitude(decimal): Location of the vessel North South location
82 - COG(udecimal): Course over ground
83 - TrueHeading(uint): True heading (relative to true North)
84 - TimeStamp(uint): UTC second when the report was generated
85 - RegionalReserved(uint): Reserved for definition by a regional authority. (field automatically set to "0")
86 - Spare(uint): Not used. Should be set to zero. (field automatically set to "0")
87 - RAIM(bool): Receiver autonomous integrity monitoring flag
88 - syncstate(uint): Sycronization state
89 - slottimeout(uint): Frames remaining until a new slot is selected
90 - slotoffset(uint): In what slot will the next transmission occur. BROKEN
91 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing
92 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
93 @rtype: BitVector
94 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
95 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.
96 '''
97
98 bvList = []
99 bvList.append(binary.setBitVectorSize(BitVector(intVal=2),6))
100 if 'RepeatIndicator' in params:
101 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
102 else:
103 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
104 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
105 if 'NavigationStatus' in params:
106 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['NavigationStatus']),4))
107 else:
108 bvList.append(binary.setBitVectorSize(BitVector(intVal=15),4))
109 if 'ROT' in params:
110 bvList.append(binary.bvFromSignedInt(params['ROT'],8))
111 else:
112 bvList.append(binary.bvFromSignedInt(-128,8))
113 if 'SOG' in params:
114 bvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params['SOG'])*Decimal('10')))),10))
115 else:
116 bvList.append(binary.setBitVectorSize(BitVector(intVal=int(1023)),10))
117 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['PositionAccuracy']),1))
118 if 'Position_longitude' in params:
119 bvList.append(binary.bvFromSignedInt(int(Decimal(params['Position_longitude'])*Decimal('600000')),28))
120 else:
121 bvList.append(binary.bvFromSignedInt(108600000,28))
122 if 'Position_latitude' in params:
123 bvList.append(binary.bvFromSignedInt(int(Decimal(params['Position_latitude'])*Decimal('600000')),27))
124 else:
125 bvList.append(binary.bvFromSignedInt(54600000,27))
126 if 'COG' in params:
127 bvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params['COG'])*Decimal('10')))),12))
128 else:
129 bvList.append(binary.setBitVectorSize(BitVector(intVal=int(3600)),12))
130 if 'TrueHeading' in params:
131 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TrueHeading']),9))
132 else:
133 bvList.append(binary.setBitVectorSize(BitVector(intVal=511),9))
134 if 'TimeStamp' in params:
135 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TimeStamp']),6))
136 else:
137 bvList.append(binary.setBitVectorSize(BitVector(intVal=60),6))
138 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),4))
139 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),1))
140 if params["RAIM"]: bvList.append(TrueBV)
141 else: bvList.append(FalseBV)
142 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['syncstate']),2))
143 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['slottimeout']),3))
144 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['slotoffset']),14))
145
146 return binary.joinBV(bvList)
147
148 -def decode(bv, validate=False):
149 '''Unpack a position message
150
151 Fields in params:
152 - MessageID(uint): AIS message number. Must be 1 (field automatically set to "2")
153 - RepeatIndicator(uint): Indicated how many times a message has been repeated
154 - UserID(uint): Unique ship identification number (MMSI)
155 - NavigationStatus(uint): What is the vessel doing
156 - ROT(int): RateOfTurn
157 - SOG(udecimal): Speed over ground
158 - PositionAccuracy(uint): Accuracy of positioning fixes
159 - Position_longitude(decimal): Location of the vessel East West location
160 - Position_latitude(decimal): Location of the vessel North South location
161 - COG(udecimal): Course over ground
162 - TrueHeading(uint): True heading (relative to true North)
163 - TimeStamp(uint): UTC second when the report was generated
164 - RegionalReserved(uint): Reserved for definition by a regional authority. (field automatically set to "0")
165 - Spare(uint): Not used. Should be set to zero. (field automatically set to "0")
166 - RAIM(bool): Receiver autonomous integrity monitoring flag
167 - syncstate(uint): Sycronization state
168 - slottimeout(uint): Frames remaining until a new slot is selected
169 - slotoffset(uint): In what slot will the next transmission occur. BROKEN
170 @type bv: BitVector
171 @param bv: Bits defining a message
172 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
173 @rtype: dict
174 @return: params
175 '''
176
177
178
179
180 r = {}
181 r['MessageID']=2
182 r['RepeatIndicator']=int(bv[6:8])
183 r['UserID']=int(bv[8:38])
184 r['NavigationStatus']=int(bv[38:42])
185 r['ROT']=binary.signedIntFromBV(bv[42:50])
186 r['SOG']=Decimal(int(bv[50:60]))/Decimal('10')
187 r['PositionAccuracy']=int(bv[60:61])
188 r['Position_longitude']=Decimal(binary.signedIntFromBV(bv[61:89]))/Decimal('600000')
189 r['Position_latitude']=Decimal(binary.signedIntFromBV(bv[89:116]))/Decimal('600000')
190 r['COG']=Decimal(int(bv[116:128]))/Decimal('10')
191 r['TrueHeading']=int(bv[128:137])
192 r['TimeStamp']=int(bv[137:143])
193 r['RegionalReserved']=0
194 r['Spare']=0
195 r['RAIM']=bool(int(bv[148:149]))
196 r['syncstate']=int(bv[149:151])
197 r['slottimeout']=int(bv[151:154])
198 r['slotoffset']=int(bv[154:168])
199 return r
200
202 return 2
203
206
209
211 return int(bv[38:42])
212
215
217 return Decimal(int(bv[50:60]))/Decimal('10')
218
220 return int(bv[60:61])
221
224
227
229 return Decimal(int(bv[116:128]))/Decimal('10')
230
232 return int(bv[128:137])
233
235 return int(bv[137:143])
236
238 return 0
239
241 return 0
242
244 return bool(int(bv[148:149]))
245
247 return int(bv[149:151])
248
250 return int(bv[151:154])
251
253 return int(bv[154:168])
254
255
257 out.write("<h3>position<h3>\n")
258 out.write("<table border=\"1\">\n")
259 out.write("<tr bgcolor=\"orange\">\n")
260 out.write("<th align=\"left\">Field Name</th>\n")
261 out.write("<th align=\"left\">Type</th>\n")
262 out.write("<th align=\"left\">Value</th>\n")
263 out.write("<th align=\"left\">Value in Lookup Table</th>\n")
264 out.write("<th align=\"left\">Units</th>\n")
265 out.write("\n")
266 out.write("<tr>\n")
267 out.write("<td>MessageID</td>\n")
268 out.write("<td>uint</td>\n")
269 if 'MessageID' in params:
270 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
271 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
272 out.write("</tr>\n")
273 out.write("\n")
274 out.write("<tr>\n")
275 out.write("<td>RepeatIndicator</td>\n")
276 out.write("<td>uint</td>\n")
277 if 'RepeatIndicator' in params:
278 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n")
279 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
280 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
281 else:
282 out.write("<td><i>Missing LUT entry</i></td>")
283 out.write("</tr>\n")
284 out.write("\n")
285 out.write("<tr>\n")
286 out.write("<td>UserID</td>\n")
287 out.write("<td>uint</td>\n")
288 if 'UserID' in params:
289 out.write(" <td>"+str(params['UserID'])+"</td>\n")
290 out.write(" <td>"+str(params['UserID'])+"</td>\n")
291 out.write("</tr>\n")
292 out.write("\n")
293 out.write("<tr>\n")
294 out.write("<td>NavigationStatus</td>\n")
295 out.write("<td>uint</td>\n")
296 if 'NavigationStatus' in params:
297 out.write(" <td>"+str(params['NavigationStatus'])+"</td>\n")
298 if str(params['NavigationStatus']) in NavigationStatusDecodeLut:
299 out.write("<td>"+NavigationStatusDecodeLut[str(params['NavigationStatus'])]+"</td>")
300 else:
301 out.write("<td><i>Missing LUT entry</i></td>")
302 out.write("</tr>\n")
303 out.write("\n")
304 out.write("<tr>\n")
305 out.write("<td>ROT</td>\n")
306 out.write("<td>int</td>\n")
307 if 'ROT' in params:
308 out.write(" <td>"+str(params['ROT'])+"</td>\n")
309 out.write(" <td>"+str(params['ROT'])+"</td>\n")
310 out.write("<td>4.733*sqrt(val) degrees/min</td>\n")
311 out.write("</tr>\n")
312 out.write("\n")
313 out.write("<tr>\n")
314 out.write("<td>SOG</td>\n")
315 out.write("<td>udecimal</td>\n")
316 if 'SOG' in params:
317 out.write(" <td>"+str(params['SOG'])+"</td>\n")
318 if str(params['SOG']) in SOGDecodeLut:
319 out.write("<td>"+SOGDecodeLut[str(params['SOG'])]+"</td>")
320 else:
321 out.write("<td><i>Missing LUT entry</i></td>")
322 out.write("<td>knots</td>\n")
323 out.write("</tr>\n")
324 out.write("\n")
325 out.write("<tr>\n")
326 out.write("<td>PositionAccuracy</td>\n")
327 out.write("<td>uint</td>\n")
328 if 'PositionAccuracy' in params:
329 out.write(" <td>"+str(params['PositionAccuracy'])+"</td>\n")
330 if str(params['PositionAccuracy']) in PositionAccuracyDecodeLut:
331 out.write("<td>"+PositionAccuracyDecodeLut[str(params['PositionAccuracy'])]+"</td>")
332 else:
333 out.write("<td><i>Missing LUT entry</i></td>")
334 out.write("</tr>\n")
335 out.write("\n")
336 out.write("<tr>\n")
337 out.write("<td>Position_longitude</td>\n")
338 out.write("<td>decimal</td>\n")
339 if 'Position_longitude' in params:
340 out.write(" <td>"+str(params['Position_longitude'])+"</td>\n")
341 out.write(" <td>"+str(params['Position_longitude'])+"</td>\n")
342 out.write("<td>degrees</td>\n")
343 out.write("</tr>\n")
344 out.write("\n")
345 out.write("<tr>\n")
346 out.write("<td>Position_latitude</td>\n")
347 out.write("<td>decimal</td>\n")
348 if 'Position_latitude' in params:
349 out.write(" <td>"+str(params['Position_latitude'])+"</td>\n")
350 out.write(" <td>"+str(params['Position_latitude'])+"</td>\n")
351 out.write("<td>degrees</td>\n")
352 out.write("</tr>\n")
353 out.write("\n")
354 out.write("<tr>\n")
355 out.write("<td>COG</td>\n")
356 out.write("<td>udecimal</td>\n")
357 if 'COG' in params:
358 out.write(" <td>"+str(params['COG'])+"</td>\n")
359 out.write(" <td>"+str(params['COG'])+"</td>\n")
360 out.write("<td>degrees</td>\n")
361 out.write("</tr>\n")
362 out.write("\n")
363 out.write("<tr>\n")
364 out.write("<td>TrueHeading</td>\n")
365 out.write("<td>uint</td>\n")
366 if 'TrueHeading' in params:
367 out.write(" <td>"+str(params['TrueHeading'])+"</td>\n")
368 out.write(" <td>"+str(params['TrueHeading'])+"</td>\n")
369 out.write("<td>degrees</td>\n")
370 out.write("</tr>\n")
371 out.write("\n")
372 out.write("<tr>\n")
373 out.write("<td>TimeStamp</td>\n")
374 out.write("<td>uint</td>\n")
375 if 'TimeStamp' in params:
376 out.write(" <td>"+str(params['TimeStamp'])+"</td>\n")
377 if str(params['TimeStamp']) in TimeStampDecodeLut:
378 out.write("<td>"+TimeStampDecodeLut[str(params['TimeStamp'])]+"</td>")
379 else:
380 out.write("<td><i>Missing LUT entry</i></td>")
381 out.write("<td>seconds</td>\n")
382 out.write("</tr>\n")
383 out.write("\n")
384 out.write("<tr>\n")
385 out.write("<td>RegionalReserved</td>\n")
386 out.write("<td>uint</td>\n")
387 if 'RegionalReserved' in params:
388 out.write(" <td>"+str(params['RegionalReserved'])+"</td>\n")
389 out.write(" <td>"+str(params['RegionalReserved'])+"</td>\n")
390 out.write("</tr>\n")
391 out.write("\n")
392 out.write("<tr>\n")
393 out.write("<td>Spare</td>\n")
394 out.write("<td>uint</td>\n")
395 if 'Spare' in params:
396 out.write(" <td>"+str(params['Spare'])+"</td>\n")
397 out.write(" <td>"+str(params['Spare'])+"</td>\n")
398 out.write("</tr>\n")
399 out.write("\n")
400 out.write("<tr>\n")
401 out.write("<td>RAIM</td>\n")
402 out.write("<td>bool</td>\n")
403 if 'RAIM' in params:
404 out.write(" <td>"+str(params['RAIM'])+"</td>\n")
405 if str(params['RAIM']) in RAIMDecodeLut:
406 out.write("<td>"+RAIMDecodeLut[str(params['RAIM'])]+"</td>")
407 else:
408 out.write("<td><i>Missing LUT entry</i></td>")
409 out.write("</tr>\n")
410 out.write("\n")
411 out.write("<tr>\n")
412 out.write("<td>syncstate</td>\n")
413 out.write("<td>uint</td>\n")
414 if 'syncstate' in params:
415 out.write(" <td>"+str(params['syncstate'])+"</td>\n")
416 if str(params['syncstate']) in syncstateDecodeLut:
417 out.write("<td>"+syncstateDecodeLut[str(params['syncstate'])]+"</td>")
418 else:
419 out.write("<td><i>Missing LUT entry</i></td>")
420 out.write("</tr>\n")
421 out.write("\n")
422 out.write("<tr>\n")
423 out.write("<td>slottimeout</td>\n")
424 out.write("<td>uint</td>\n")
425 if 'slottimeout' in params:
426 out.write(" <td>"+str(params['slottimeout'])+"</td>\n")
427 if str(params['slottimeout']) in slottimeoutDecodeLut:
428 out.write("<td>"+slottimeoutDecodeLut[str(params['slottimeout'])]+"</td>")
429 else:
430 out.write("<td><i>Missing LUT entry</i></td>")
431 out.write("<td>frames</td>\n")
432 out.write("</tr>\n")
433 out.write("\n")
434 out.write("<tr>\n")
435 out.write("<td>slotoffset</td>\n")
436 out.write("<td>uint</td>\n")
437 if 'slotoffset' in params:
438 out.write(" <td>"+str(params['slotoffset'])+"</td>\n")
439 out.write(" <td>"+str(params['slotoffset'])+"</td>\n")
440 out.write("</tr>\n")
441 out.write("</table>\n")
442
443
445 '''KML (Keyhole Markup Language) for Google Earth, but without the header/footer'''
446 out.write("\ <Placemark>\n")
447 out.write("\t <name>"+str(params['UserID'])+"</name>\n")
448 out.write("\t\t<description>\n")
449 import StringIO
450 buf = StringIO.StringIO()
451 printHtml(params,buf)
452 import cgi
453 out.write(cgi.escape(buf.getvalue()))
454 out.write("\t\t</description>\n")
455 out.write("\t\t<styleUrl>#m_ylw-pushpin_copy0</styleUrl>\n")
456 out.write("\t\t<Point>\n")
457 out.write("\t\t\t<coordinates>")
458 out.write(str(params['Position_longitude']))
459 out.write(',')
460 out.write(str(params['Position_latitude']))
461 out.write(",0</coordinates>\n")
462 out.write("\t\t</Point>\n")
463 out.write("\t</Placemark>\n")
464
465 -def printFields(params, out=sys.stdout, format='std', fieldList=None):
466 '''Print a slotoffset message to stdout.
467
468 Fields in params:
469 - MessageID(uint): AIS message number. Must be 1 (field automatically set to "2")
470 - RepeatIndicator(uint): Indicated how many times a message has been repeated
471 - UserID(uint): Unique ship identification number (MMSI)
472 - NavigationStatus(uint): What is the vessel doing
473 - ROT(int): RateOfTurn
474 - SOG(udecimal): Speed over ground
475 - PositionAccuracy(uint): Accuracy of positioning fixes
476 - Position_longitude(decimal): Location of the vessel East West location
477 - Position_latitude(decimal): Location of the vessel North South location
478 - COG(udecimal): Course over ground
479 - TrueHeading(uint): True heading (relative to true North)
480 - TimeStamp(uint): UTC second when the report was generated
481 - RegionalReserved(uint): Reserved for definition by a regional authority. (field automatically set to "0")
482 - Spare(uint): Not used. Should be set to zero. (field automatically set to "0")
483 - RAIM(bool): Receiver autonomous integrity monitoring flag
484 - syncstate(uint): Sycronization state
485 - slottimeout(uint): Frames remaining until a new slot is selected
486 - slotoffset(uint): In what slot will the next transmission occur. BROKEN
487 @param params: Dictionary of field names/values.
488 @param out: File like object to write to
489 @rtype: stdout
490 @return: text to out
491 '''
492
493 if 'std'==format:
494 out.write("slotoffset:\n")
495 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n")
496 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n")
497 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n")
498 if 'NavigationStatus' in params: out.write(" NavigationStatus: "+str(params['NavigationStatus'])+"\n")
499 if 'ROT' in params: out.write(" ROT: "+str(params['ROT'])+"\n")
500 if 'SOG' in params: out.write(" SOG: "+str(params['SOG'])+"\n")
501 if 'PositionAccuracy' in params: out.write(" PositionAccuracy: "+str(params['PositionAccuracy'])+"\n")
502 if 'Position_longitude' in params: out.write(" Position_longitude: "+str(params['Position_longitude'])+"\n")
503 if 'Position_latitude' in params: out.write(" Position_latitude: "+str(params['Position_latitude'])+"\n")
504 if 'COG' in params: out.write(" COG: "+str(params['COG'])+"\n")
505 if 'TrueHeading' in params: out.write(" TrueHeading: "+str(params['TrueHeading'])+"\n")
506 if 'TimeStamp' in params: out.write(" TimeStamp: "+str(params['TimeStamp'])+"\n")
507 if 'RegionalReserved' in params: out.write(" RegionalReserved: "+str(params['RegionalReserved'])+"\n")
508 if 'Spare' in params: out.write(" Spare: "+str(params['Spare'])+"\n")
509 if 'RAIM' in params: out.write(" RAIM: "+str(params['RAIM'])+"\n")
510 if 'syncstate' in params: out.write(" syncstate: "+str(params['syncstate'])+"\n")
511 if 'slottimeout' in params: out.write(" slottimeout: "+str(params['slottimeout'])+"\n")
512 if 'slotoffset' in params: out.write(" slotoffset: "+str(params['slotoffset'])+"\n")
513 elif 'csv'==format:
514 if None == options.fieldList:
515 options.fieldList = fieldList
516 needComma = False;
517 for field in fieldList:
518 if needComma: out.write(',')
519 needComma = True
520 if field in params:
521 out.write(str(params[field]))
522
523 out.write("\n")
524 elif 'html'==format:
525 printHtml(params,out)
526 elif 'sql'==format:
527 sqlInsertStr(params,out)
528 elif 'kml'==format:
529 printKml(params,out)
530 elif 'kml-full'==format:
531 out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
532 out.write("<kml xmlns=\"http://earth.google.com/kml/2.1\">\n")
533 out.write("<Document>\n")
534 out.write(" <name>position</name>\n")
535 printKml(params,out)
536 out.write("</Document>\n")
537 out.write("</kml>\n")
538 else:
539 print "ERROR: unknown format:",format
540 assert False
541
542 return
543
544 RepeatIndicatorEncodeLut = {
545 'default':'0',
546 'do not repeat any more':'3',
547 }
548
549 RepeatIndicatorDecodeLut = {
550 '0':'default',
551 '3':'do not repeat any more',
552 }
553
554 NavigationStatusEncodeLut = {
555 'under way using engine':'0',
556 'at anchor':'1',
557 'not under command':'2',
558 'restricted maneuverability':'3',
559 'constrained by her draught':'4',
560 'moored':'5',
561 'aground':'6',
562 'engaged in fishing':'7',
563 'under way sailing':'8',
564 'reserved for future use (hazmat)':'9',
565 'reserved for future use':'10',
566 'reserved for future use':'11',
567 'reserved for future use':'12',
568 'reserved for future use':'13',
569 'reserved for future use':'14',
570 'not defined = default':'15',
571 }
572
573 NavigationStatusDecodeLut = {
574 '0':'under way using engine',
575 '1':'at anchor',
576 '2':'not under command',
577 '3':'restricted maneuverability',
578 '4':'constrained by her draught',
579 '5':'moored',
580 '6':'aground',
581 '7':'engaged in fishing',
582 '8':'under way sailing',
583 '9':'reserved for future use (hazmat)',
584 '10':'reserved for future use',
585 '11':'reserved for future use',
586 '12':'reserved for future use',
587 '13':'reserved for future use',
588 '14':'reserved for future use',
589 '15':'not defined = default',
590 }
591
592 SOGEncodeLut = {
593 '102.2 knots or higher':'102.2',
594 }
595
596 SOGDecodeLut = {
597 '102.2':'102.2 knots or higher',
598 }
599
600 PositionAccuracyEncodeLut = {
601 'low (greater than 10 m)':'0',
602 'high (less than 10 m)':'1',
603 }
604
605 PositionAccuracyDecodeLut = {
606 '0':'low (greater than 10 m)',
607 '1':'high (less than 10 m)',
608 }
609
610 TimeStampEncodeLut = {
611 'not available/default':'60',
612 'manual input':'61',
613 'dead reckoning':'62',
614 'inoperative':'63',
615 }
616
617 TimeStampDecodeLut = {
618 '60':'not available/default',
619 '61':'manual input',
620 '62':'dead reckoning',
621 '63':'inoperative',
622 }
623
624 RAIMEncodeLut = {
625 'not in use':'False',
626 'in use':'True',
627 }
628
629 RAIMDecodeLut = {
630 'False':'not in use',
631 'True':'in use',
632 }
633
634 syncstateEncodeLut = {
635 'UTC direct':'0',
636 'UTC indirect':'1',
637 'synchronized to a base station':'2',
638 'synchronized to another station':'3',
639 }
640
641 syncstateDecodeLut = {
642 '0':'UTC direct',
643 '1':'UTC indirect',
644 '2':'synchronized to a base station',
645 '3':'synchronized to another station',
646 }
647
648 slottimeoutEncodeLut = {
649 'Last frame in this slot':'0',
650 '1 frames left':'1',
651 '2 frames left':'2',
652 '3 frames left':'3',
653 '4 frames left':'4',
654 '5 frames left':'5',
655 '6 frames left':'6',
656 '7 frames left':'7',
657 }
658
659 slottimeoutDecodeLut = {
660 '0':'Last frame in this slot',
661 '1':'1 frames left',
662 '2':'2 frames left',
663 '3':'3 frames left',
664 '4':'4 frames left',
665 '5':'5 frames left',
666 '6':'6 frames left',
667 '7':'7 frames left',
668 }
669
670
671
672
673
674 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None, addCoastGuardFields=True):
675 '''
676 Return the SQL CREATE command for this message type
677 @param outfile: file like object to print to.
678 @param fields: which fields to put in the create. Defaults to all.
679 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
680 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
681 @type addCoastGuardFields: bool
682 @return: sql create string
683 @rtype: str
684
685 @see: sqlCreate
686 '''
687 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields)))
688
689 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True):
690 '''
691 Return the sqlhelp object to create the table.
692
693 @param fields: which fields to put in the create. Defaults to all.
694 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
695 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
696 @type addCoastGuardFields: bool
697 @return: An object that can be used to generate a return
698 @rtype: sqlhelp.create
699 '''
700 if None == fields: fields = fieldList
701 import sqlhelp
702 c = sqlhelp.create('position')
703 if 'MessageID' in fields: c.addInt ('MessageID')
704 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
705 if 'UserID' in fields: c.addInt ('UserID')
706 if 'NavigationStatus' in fields: c.addInt ('NavigationStatus')
707 if 'ROT' in fields: c.addInt ('ROT')
708 if 'SOG' in fields: c.addDecimal('SOG',4,1)
709 if 'PositionAccuracy' in fields: c.addInt ('PositionAccuracy')
710 if 'Position_longitude' in fields: c.addDecimal('Position_longitude',8,5)
711 if 'Position_latitude' in fields: c.addDecimal('Position_latitude',8,5)
712 if 'COG' in fields: c.addDecimal('COG',4,1)
713 if 'TrueHeading' in fields: c.addInt ('TrueHeading')
714 if 'TimeStamp' in fields: c.addInt ('TimeStamp')
715 if 'RegionalReserved' in fields: c.addInt ('RegionalReserved')
716 if 'Spare' in fields: c.addInt ('Spare')
717 if 'RAIM' in fields: c.addBool('RAIM')
718 if 'syncstate' in fields: c.addInt ('syncstate')
719 if 'slottimeout' in fields: c.addInt ('slottimeout')
720 if 'slotoffset' in fields: c.addInt ('slotoffset')
721
722 if addCoastGuardFields:
723
724
725
726
727
728 c.addVarChar('cg_r',15)
729 c.addInt('cg_timestamp')
730
731
732 return c
733
734 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None):
735 '''
736 Return the SQL CREATE command for this message type
737 @param params: dictionary of values keyed by field name
738 @param outfile: file like object to print to.
739 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
740 @return: sql create string
741 @rtype: str
742
743 @see: sqlCreate
744 '''
745 outfile.write(str(sqlInsert(params,extraParams)))
746
747
749 '''
750 Give the SQL insert statement
751 @param params: dict keyed by field name of values
752 @param extraParams: any extra fields that you have created beyond the normal ais message fields
753 @rtype: sqlhelp.insert
754 @return: insert class instance
755 @todo: allow optional type checking of params?
756 @warning: this will take invalid keys happily and do what???
757 '''
758 import sqlhelp
759 i = sqlhelp.insert('position')
760 for key in params:
761
762 if type(params[key])==Decimal: i.add(key,float(params[key]))
763 else: i.add(key,params[key])
764 if None != extraParams:
765 for key in extraParams:
766 i.add(key,extraParams[key])
767
768 return i
769
770
771
772
773
774 import unittest
776 '''Return a params file base on the testvalue tags.
777 @rtype: dict
778 @return: params based on testvalue tags
779 '''
780 params = {}
781 params['MessageID'] = 2
782 params['RepeatIndicator'] = 1
783 params['UserID'] = 1193046
784 params['NavigationStatus'] = 3
785 params['ROT'] = -2
786 params['SOG'] = Decimal('101.9')
787 params['PositionAccuracy'] = 1
788 params['Position_longitude'] = Decimal('-122.16328055555556')
789 params['Position_latitude'] = Decimal('37.424458333333334')
790 params['COG'] = Decimal('34.5')
791 params['TrueHeading'] = 41
792 params['TimeStamp'] = 35
793 params['RegionalReserved'] = 0
794 params['Spare'] = 0
795 params['RAIM'] = False
796 params['syncstate'] = 2
797 params['slottimeout'] = 0
798 params['slotoffset'] = 1221
799
800 return params
801
803 '''Use testvalue tag text from each type to build test case the position message'''
805
806 params = testParams()
807 bits = encode(params)
808 r = decode(bits)
809
810
811 self.failUnlessEqual(r['MessageID'],params['MessageID'])
812 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
813 self.failUnlessEqual(r['UserID'],params['UserID'])
814 self.failUnlessEqual(r['NavigationStatus'],params['NavigationStatus'])
815 self.failUnlessEqual(r['ROT'],params['ROT'])
816 self.failUnlessAlmostEqual(r['SOG'],params['SOG'],1)
817 self.failUnlessEqual(r['PositionAccuracy'],params['PositionAccuracy'])
818 self.failUnlessAlmostEqual(r['Position_longitude'],params['Position_longitude'],5)
819 self.failUnlessAlmostEqual(r['Position_latitude'],params['Position_latitude'],5)
820 self.failUnlessAlmostEqual(r['COG'],params['COG'],1)
821 self.failUnlessEqual(r['TrueHeading'],params['TrueHeading'])
822 self.failUnlessEqual(r['TimeStamp'],params['TimeStamp'])
823 self.failUnlessEqual(r['RegionalReserved'],params['RegionalReserved'])
824 self.failUnlessEqual(r['Spare'],params['Spare'])
825 self.failUnlessEqual(r['RAIM'],params['RAIM'])
826 self.failUnlessEqual(r['syncstate'],params['syncstate'])
827 self.failUnlessEqual(r['slottimeout'],params['slottimeout'])
828 self.failUnlessEqual(r['slotoffset'],params['slotoffset'])
829
831 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
832 help='decode a "position" AIS message')
833 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
834 help='encode a "position" AIS message')
835 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
836 ,help='Field parameter value [default: %default]')
837 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
838 ,help='Field parameter value [default: %default]')
839 parser.add_option('--NavigationStatus-field', dest='NavigationStatusField',default=15,metavar='uint',type='int'
840 ,help='Field parameter value [default: %default]')
841 parser.add_option('--ROT-field', dest='ROTField',default=-128,metavar='int',type='int'
842 ,help='Field parameter value [default: %default]')
843 parser.add_option('--SOG-field', dest='SOGField',default=Decimal('102.3'),metavar='udecimal',type='string'
844 ,help='Field parameter value [default: %default]')
845 parser.add_option('--PositionAccuracy-field', dest='PositionAccuracyField',metavar='uint',type='int'
846 ,help='Field parameter value [default: %default]')
847 parser.add_option('--Position_longitude-field', dest='Position_longitudeField',default=Decimal('181'),metavar='decimal',type='string'
848 ,help='Field parameter value [default: %default]')
849 parser.add_option('--Position_latitude-field', dest='Position_latitudeField',default=Decimal('91'),metavar='decimal',type='string'
850 ,help='Field parameter value [default: %default]')
851 parser.add_option('--COG-field', dest='COGField',default=Decimal('360'),metavar='udecimal',type='string'
852 ,help='Field parameter value [default: %default]')
853 parser.add_option('--TrueHeading-field', dest='TrueHeadingField',default=511,metavar='uint',type='int'
854 ,help='Field parameter value [default: %default]')
855 parser.add_option('--TimeStamp-field', dest='TimeStampField',default=60,metavar='uint',type='int'
856 ,help='Field parameter value [default: %default]')
857 parser.add_option('--RAIM-field', dest='RAIMField',metavar='bool',type='int'
858 ,help='Field parameter value [default: %default]')
859 parser.add_option('--syncstate-field', dest='syncstateField',metavar='uint',type='int'
860 ,help='Field parameter value [default: %default]')
861 parser.add_option('--slottimeout-field', dest='slottimeoutField',metavar='uint',type='int'
862 ,help='Field parameter value [default: %default]')
863 parser.add_option('--slotoffset-field', dest='slotoffsetField',metavar='uint',type='int'
864 ,help='Field parameter value [default: %default]')
865
866
867 if __name__=='__main__':
868
869 from optparse import OptionParser
870 parser = OptionParser(usage="%prog [options]",
871 version="%prog "+__version__)
872
873 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
874 help='run the documentation tests')
875 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
876 help='run the unit tests')
877 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
878 help='Make the test output verbose')
879
880
881
882 typeChoices = ('binary','nmeapayload','nmea')
883 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
884 ,default='nmeapayload'
885 ,help='What kind of string to expect ('+', '.join(typeChoices)+') [default: %default]')
886
887
888 outputChoices = ('std','html','csv','sql' , 'kml','kml-full')
889 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
890 ,default='std'
891 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
892
893 parser.add_option('-o','--output',dest='outputFileName',default=None,
894 help='Name of the python file to write [default: stdout]')
895
896 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
897 choices=fieldList,
898 help='Which fields to include in the output. Currently only for csv output [default: all]')
899
900 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
901 help='Print the field name for csv')
902
903 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
904 help='Print out an sql create command for the table.')
905
906 addMsgOptions(parser)
907
908 (options,args) = parser.parse_args()
909 success=True
910
911 if options.doctest:
912 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
913 sys.argv= [sys.argv[0]]
914 if options.verbose: sys.argv.append('-v')
915 import doctest
916 numfail,numtests=doctest.testmod()
917 if numfail==0: print 'ok'
918 else:
919 print 'FAILED'
920 success=False
921
922 if not success: sys.exit('Something Failed')
923 del success
924
925 if options.unittest:
926 sys.argv = [sys.argv[0]]
927 if options.verbose: sys.argv.append('-v')
928 unittest.main()
929
930 outfile = sys.stdout
931 if None!=options.outputFileName:
932 outfile = file(options.outputFileName,'w')
933
934
935 if options.doEncode:
936
937 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
938 if None==options.UserIDField: parser.error("missing value for UserIDField")
939 if None==options.NavigationStatusField: parser.error("missing value for NavigationStatusField")
940 if None==options.ROTField: parser.error("missing value for ROTField")
941 if None==options.SOGField: parser.error("missing value for SOGField")
942 if None==options.PositionAccuracyField: parser.error("missing value for PositionAccuracyField")
943 if None==options.Position_longitudeField: parser.error("missing value for Position_longitudeField")
944 if None==options.Position_latitudeField: parser.error("missing value for Position_latitudeField")
945 if None==options.COGField: parser.error("missing value for COGField")
946 if None==options.TrueHeadingField: parser.error("missing value for TrueHeadingField")
947 if None==options.TimeStampField: parser.error("missing value for TimeStampField")
948 if None==options.RAIMField: parser.error("missing value for RAIMField")
949 if None==options.syncstateField: parser.error("missing value for syncstateField")
950 if None==options.slottimeoutField: parser.error("missing value for slottimeoutField")
951 if None==options.slotoffsetField: parser.error("missing value for slotoffsetField")
952 msgDict={
953 'MessageID': '2',
954 'RepeatIndicator': options.RepeatIndicatorField,
955 'UserID': options.UserIDField,
956 'NavigationStatus': options.NavigationStatusField,
957 'ROT': options.ROTField,
958 'SOG': options.SOGField,
959 'PositionAccuracy': options.PositionAccuracyField,
960 'Position_longitude': options.Position_longitudeField,
961 'Position_latitude': options.Position_latitudeField,
962 'COG': options.COGField,
963 'TrueHeading': options.TrueHeadingField,
964 'TimeStamp': options.TimeStampField,
965 'RegionalReserved': '0',
966 'Spare': '0',
967 'RAIM': options.RAIMField,
968 'syncstate': options.syncstateField,
969 'slottimeout': options.slottimeoutField,
970 'slotoffset': options.slotoffsetField,
971 }
972
973 bits = encode(msgDict)
974 if 'binary'==options.ioType: print str(bits)
975 elif 'nmeapayload'==options.ioType:
976
977 print "bitLen",len(bits)
978 bitLen=len(bits)
979 if bitLen%6!=0:
980 bits = bits + BitVector(size=(6 - (bitLen%6)))
981 print "result:",binary.bitvectoais6(bits)[0]
982
983
984
985 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
986 else: sys.exit('ERROR: unknown ioType. Help!')
987
988
989 if options.sqlCreate:
990 sqlCreateStr(outfile,options.fieldList)
991
992 if options.printCsvfieldList:
993
994 if None == options.fieldList: options.fieldList = fieldList
995 import StringIO
996 buf = StringIO.StringIO()
997 for field in options.fieldList:
998 buf.write(field+',')
999 result = buf.getvalue()
1000 if result[-1] == ',': print result[:-1]
1001 else: print result
1002
1003 if options.doDecode:
1004 for msg in args:
1005 bv = None
1006 if 'binary' == options.ioType: bv = BitVector(bitstring=msg)
1007 elif 'nmeapayload'== options.ioType: bv = binary.ais6tobitvec(msg)
1008 elif 'nmea' == options.ioType: bv = binary.ais6tobitvec(msg.split(',')[5])
1009 else: sys.exit('ERROR: unknown ioType. Help!')
1010
1011 printFields(decode(bv),out=outfile,format=options.outputType,fieldList=options.fieldList)
1012