1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2007-01-18 $'.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 'slotoffset',
66 ]
67
68 -def encode(params, validate=False):
69 '''Create a position binary message payload to pack into an AIS Msg position.
70
71 Fields in params:
72 - MessageID(uint): AIS message number. Must be 1 (field automatically set to "1")
73 - RepeatIndicator(uint): Indicated how many times a message has been repeated
74 - UserID(uint): Unique ship identification number (MMSI)
75 - NavigationStatus(uint): What is the vessel doing
76 - ROT(int): RateOfTurn
77 - SOG(udecimal): Speed over ground
78 - PositionAccuracy(uint): Accuracy of positioning fixes
79 - Position_longitude(decimal): Location of the vessel East West location
80 - Position_latitude(decimal): Location of the vessel North South location
81 - COG(udecimal): Course over ground
82 - TrueHeading(uint): True heading (relative to true North)
83 - TimeStamp(uint): UTC second when the report was generated
84 - RegionalReserved(uint): Reserved for definition by a regional authority. (field automatically set to "0")
85 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
86 - RAIM(bool): Receiver autonomous integrity monitoring flag
87 - syncstate(uint): Sycronization state
88 - slotoffset(uint): In what slot will the next transmission occur. BROKEN
89 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing
90 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
91 @rtype: BitVector
92 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
93 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.
94 '''
95
96 bvList = []
97 bvList.append(binary.setBitVectorSize(BitVector(intVal=1),6))
98 if 'RepeatIndicator' in params:
99 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
100 else:
101 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
102 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
103 if 'NavigationStatus' in params:
104 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['NavigationStatus']),4))
105 else:
106 bvList.append(binary.setBitVectorSize(BitVector(intVal=15),4))
107 if 'ROT' in params:
108 bvList.append(binary.bvFromSignedInt(params['ROT'],8))
109 else:
110 bvList.append(binary.bvFromSignedInt(-128,8))
111 if 'SOG' in params:
112 bvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params['SOG'])*Decimal('10')))),10))
113 else:
114 bvList.append(binary.setBitVectorSize(BitVector(intVal=int(1023)),10))
115 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['PositionAccuracy']),1))
116 if 'Position_longitude' in params:
117 bvList.append(binary.bvFromSignedInt(int(Decimal(params['Position_longitude'])*Decimal('600000')),28))
118 else:
119 bvList.append(binary.bvFromSignedInt(108600000,28))
120 if 'Position_latitude' in params:
121 bvList.append(binary.bvFromSignedInt(int(Decimal(params['Position_latitude'])*Decimal('600000')),27))
122 else:
123 bvList.append(binary.bvFromSignedInt(54600000,27))
124 if 'COG' in params:
125 bvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params['COG'])*Decimal('10')))),12))
126 else:
127 bvList.append(binary.setBitVectorSize(BitVector(intVal=int(3600)),12))
128 if 'TrueHeading' in params:
129 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TrueHeading']),9))
130 else:
131 bvList.append(binary.setBitVectorSize(BitVector(intVal=511),9))
132 if 'TimeStamp' in params:
133 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TimeStamp']),6))
134 else:
135 bvList.append(binary.setBitVectorSize(BitVector(intVal=60),6))
136 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),4))
137 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),1))
138 if params["RAIM"]: bvList.append(TrueBV)
139 else: bvList.append(FalseBV)
140 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['syncstate']),2))
141 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['slotoffset']),14))
142
143 return binary.joinBV(bvList)
144
145 -def decode(bv, validate=False):
146 '''Unpack a position message
147
148 Fields in params:
149 - MessageID(uint): AIS message number. Must be 1 (field automatically set to "1")
150 - RepeatIndicator(uint): Indicated how many times a message has been repeated
151 - UserID(uint): Unique ship identification number (MMSI)
152 - NavigationStatus(uint): What is the vessel doing
153 - ROT(int): RateOfTurn
154 - SOG(udecimal): Speed over ground
155 - PositionAccuracy(uint): Accuracy of positioning fixes
156 - Position_longitude(decimal): Location of the vessel East West location
157 - Position_latitude(decimal): Location of the vessel North South location
158 - COG(udecimal): Course over ground
159 - TrueHeading(uint): True heading (relative to true North)
160 - TimeStamp(uint): UTC second when the report was generated
161 - RegionalReserved(uint): Reserved for definition by a regional authority. (field automatically set to "0")
162 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
163 - RAIM(bool): Receiver autonomous integrity monitoring flag
164 - syncstate(uint): Sycronization state
165 - slotoffset(uint): In what slot will the next transmission occur. BROKEN
166 @type bv: BitVector
167 @param bv: Bits defining a message
168 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
169 @rtype: dict
170 @return: params
171 '''
172
173
174
175
176 r = {}
177 r['MessageID']=1
178 r['RepeatIndicator']=int(bv[6:8])
179 r['UserID']=int(bv[8:38])
180 r['NavigationStatus']=int(bv[38:42])
181 r['ROT']=binary.signedIntFromBV(bv[42:50])
182 r['SOG']=Decimal(int(bv[50:60]))/Decimal('10')
183 r['PositionAccuracy']=int(bv[60:61])
184 r['Position_longitude']=Decimal(binary.signedIntFromBV(bv[61:89]))/Decimal('600000')
185 r['Position_latitude']=Decimal(binary.signedIntFromBV(bv[89:116]))/Decimal('600000')
186 r['COG']=Decimal(int(bv[116:128]))/Decimal('10')
187 r['TrueHeading']=int(bv[128:137])
188 r['TimeStamp']=int(bv[137:143])
189 r['RegionalReserved']=0
190 r['Spare']=0
191 r['RAIM']=bool(int(bv[148:149]))
192 r['syncstate']=int(bv[149:151])
193 r['slotoffset']=int(bv[151:165])
194 return r
195
197 return 1
198
201
204
206 return int(bv[38:42])
207
210
212 return Decimal(int(bv[50:60]))/Decimal('10')
213
215 return int(bv[60:61])
216
219
222
224 return Decimal(int(bv[116:128]))/Decimal('10')
225
227 return int(bv[128:137])
228
230 return int(bv[137:143])
231
233 return 0
234
236 return 0
237
239 return bool(int(bv[148:149]))
240
242 return int(bv[149:151])
243
245 return int(bv[151:165])
246
247
249 out.write("<h3>position<h3>\n")
250 out.write("<table border=\"1\">\n")
251 out.write("<tr bgcolor=\"orange\">\n")
252 out.write("<th align=\"left\">Field Name</th>\n")
253 out.write("<th align=\"left\">Type</th>\n")
254 out.write("<th align=\"left\">Value</th>\n")
255 out.write("<th align=\"left\">Value in Lookup Table</th>\n")
256 out.write("<th align=\"left\">Units</th>\n")
257 out.write("\n")
258 out.write("<tr>\n")
259 out.write("<td>MessageID</td>\n")
260 out.write("<td>uint</td>\n")
261 if 'MessageID' in params:
262 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
263 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
264 out.write("</tr>\n")
265 out.write("\n")
266 out.write("<tr>\n")
267 out.write("<td>RepeatIndicator</td>\n")
268 out.write("<td>uint</td>\n")
269 if 'RepeatIndicator' in params:
270 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n")
271 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
272 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
273 else:
274 out.write("<td><i>Missing LUT entry</i></td>")
275 out.write("</tr>\n")
276 out.write("\n")
277 out.write("<tr>\n")
278 out.write("<td>UserID</td>\n")
279 out.write("<td>uint</td>\n")
280 if 'UserID' in params:
281 out.write(" <td>"+str(params['UserID'])+"</td>\n")
282 out.write(" <td>"+str(params['UserID'])+"</td>\n")
283 out.write("</tr>\n")
284 out.write("\n")
285 out.write("<tr>\n")
286 out.write("<td>NavigationStatus</td>\n")
287 out.write("<td>uint</td>\n")
288 if 'NavigationStatus' in params:
289 out.write(" <td>"+str(params['NavigationStatus'])+"</td>\n")
290 if str(params['NavigationStatus']) in NavigationStatusDecodeLut:
291 out.write("<td>"+NavigationStatusDecodeLut[str(params['NavigationStatus'])]+"</td>")
292 else:
293 out.write("<td><i>Missing LUT entry</i></td>")
294 out.write("</tr>\n")
295 out.write("\n")
296 out.write("<tr>\n")
297 out.write("<td>ROT</td>\n")
298 out.write("<td>int</td>\n")
299 if 'ROT' in params:
300 out.write(" <td>"+str(params['ROT'])+"</td>\n")
301 out.write(" <td>"+str(params['ROT'])+"</td>\n")
302 out.write("<td>4.733*sqrt(val) degrees/min</td>\n")
303 out.write("</tr>\n")
304 out.write("\n")
305 out.write("<tr>\n")
306 out.write("<td>SOG</td>\n")
307 out.write("<td>udecimal</td>\n")
308 if 'SOG' in params:
309 out.write(" <td>"+str(params['SOG'])+"</td>\n")
310 if str(params['SOG']) in SOGDecodeLut:
311 out.write("<td>"+SOGDecodeLut[str(params['SOG'])]+"</td>")
312 else:
313 out.write("<td><i>Missing LUT entry</i></td>")
314 out.write("<td>knots</td>\n")
315 out.write("</tr>\n")
316 out.write("\n")
317 out.write("<tr>\n")
318 out.write("<td>PositionAccuracy</td>\n")
319 out.write("<td>uint</td>\n")
320 if 'PositionAccuracy' in params:
321 out.write(" <td>"+str(params['PositionAccuracy'])+"</td>\n")
322 if str(params['PositionAccuracy']) in PositionAccuracyDecodeLut:
323 out.write("<td>"+PositionAccuracyDecodeLut[str(params['PositionAccuracy'])]+"</td>")
324 else:
325 out.write("<td><i>Missing LUT entry</i></td>")
326 out.write("</tr>\n")
327 out.write("\n")
328 out.write("<tr>\n")
329 out.write("<td>Position_longitude</td>\n")
330 out.write("<td>decimal</td>\n")
331 if 'Position_longitude' in params:
332 out.write(" <td>"+str(params['Position_longitude'])+"</td>\n")
333 out.write(" <td>"+str(params['Position_longitude'])+"</td>\n")
334 out.write("<td>degrees</td>\n")
335 out.write("</tr>\n")
336 out.write("\n")
337 out.write("<tr>\n")
338 out.write("<td>Position_latitude</td>\n")
339 out.write("<td>decimal</td>\n")
340 if 'Position_latitude' in params:
341 out.write(" <td>"+str(params['Position_latitude'])+"</td>\n")
342 out.write(" <td>"+str(params['Position_latitude'])+"</td>\n")
343 out.write("<td>degrees</td>\n")
344 out.write("</tr>\n")
345 out.write("\n")
346 out.write("<tr>\n")
347 out.write("<td>COG</td>\n")
348 out.write("<td>udecimal</td>\n")
349 if 'COG' in params:
350 out.write(" <td>"+str(params['COG'])+"</td>\n")
351 out.write(" <td>"+str(params['COG'])+"</td>\n")
352 out.write("<td>degrees</td>\n")
353 out.write("</tr>\n")
354 out.write("\n")
355 out.write("<tr>\n")
356 out.write("<td>TrueHeading</td>\n")
357 out.write("<td>uint</td>\n")
358 if 'TrueHeading' in params:
359 out.write(" <td>"+str(params['TrueHeading'])+"</td>\n")
360 out.write(" <td>"+str(params['TrueHeading'])+"</td>\n")
361 out.write("<td>degrees</td>\n")
362 out.write("</tr>\n")
363 out.write("\n")
364 out.write("<tr>\n")
365 out.write("<td>TimeStamp</td>\n")
366 out.write("<td>uint</td>\n")
367 if 'TimeStamp' in params:
368 out.write(" <td>"+str(params['TimeStamp'])+"</td>\n")
369 if str(params['TimeStamp']) in TimeStampDecodeLut:
370 out.write("<td>"+TimeStampDecodeLut[str(params['TimeStamp'])]+"</td>")
371 else:
372 out.write("<td><i>Missing LUT entry</i></td>")
373 out.write("<td>seconds</td>\n")
374 out.write("</tr>\n")
375 out.write("\n")
376 out.write("<tr>\n")
377 out.write("<td>RegionalReserved</td>\n")
378 out.write("<td>uint</td>\n")
379 if 'RegionalReserved' in params:
380 out.write(" <td>"+str(params['RegionalReserved'])+"</td>\n")
381 out.write(" <td>"+str(params['RegionalReserved'])+"</td>\n")
382 out.write("</tr>\n")
383 out.write("\n")
384 out.write("<tr>\n")
385 out.write("<td>Spare</td>\n")
386 out.write("<td>uint</td>\n")
387 if 'Spare' in params:
388 out.write(" <td>"+str(params['Spare'])+"</td>\n")
389 out.write(" <td>"+str(params['Spare'])+"</td>\n")
390 out.write("</tr>\n")
391 out.write("\n")
392 out.write("<tr>\n")
393 out.write("<td>RAIM</td>\n")
394 out.write("<td>bool</td>\n")
395 if 'RAIM' in params:
396 out.write(" <td>"+str(params['RAIM'])+"</td>\n")
397 if str(params['RAIM']) in RAIMDecodeLut:
398 out.write("<td>"+RAIMDecodeLut[str(params['RAIM'])]+"</td>")
399 else:
400 out.write("<td><i>Missing LUT entry</i></td>")
401 out.write("</tr>\n")
402 out.write("\n")
403 out.write("<tr>\n")
404 out.write("<td>syncstate</td>\n")
405 out.write("<td>uint</td>\n")
406 if 'syncstate' in params:
407 out.write(" <td>"+str(params['syncstate'])+"</td>\n")
408 if str(params['syncstate']) in syncstateDecodeLut:
409 out.write("<td>"+syncstateDecodeLut[str(params['syncstate'])]+"</td>")
410 else:
411 out.write("<td><i>Missing LUT entry</i></td>")
412 out.write("</tr>\n")
413 out.write("\n")
414 out.write("<tr>\n")
415 out.write("<td>slotoffset</td>\n")
416 out.write("<td>uint</td>\n")
417 if 'slotoffset' in params:
418 out.write(" <td>"+str(params['slotoffset'])+"</td>\n")
419 out.write(" <td>"+str(params['slotoffset'])+"</td>\n")
420 out.write("</tr>\n")
421 out.write("</table>\n")
422
423
425 '''KML (Keyhole Markup Language) for Google Earth, but without the header/footer'''
426 out.write("\ <Placemark>\n")
427 out.write("\t <name>"+str(params['UserID'])+"</name>\n")
428 out.write("\t\t<description>\n")
429 import StringIO
430 buf = StringIO.StringIO()
431 printHtml(params,buf)
432 import cgi
433 out.write(cgi.escape(buf.getvalue()))
434 out.write("\t\t</description>\n")
435 out.write("\t\t<styleUrl>#m_ylw-pushpin_copy0</styleUrl>\n")
436 out.write("\t\t<Point>\n")
437 out.write("\t\t\t<coordinates>")
438 out.write(str(params['Position_longitude']))
439 out.write(',')
440 out.write(str(params['Position_latitude']))
441 out.write(",0</coordinates>\n")
442 out.write("\t\t</Point>\n")
443 out.write("\t</Placemark>\n")
444
445 -def printFields(params, out=sys.stdout, format='std', fieldList=None):
446 '''Print a slotoffset message to stdout.
447
448 Fields in params:
449 - MessageID(uint): AIS message number. Must be 1 (field automatically set to "1")
450 - RepeatIndicator(uint): Indicated how many times a message has been repeated
451 - UserID(uint): Unique ship identification number (MMSI)
452 - NavigationStatus(uint): What is the vessel doing
453 - ROT(int): RateOfTurn
454 - SOG(udecimal): Speed over ground
455 - PositionAccuracy(uint): Accuracy of positioning fixes
456 - Position_longitude(decimal): Location of the vessel East West location
457 - Position_latitude(decimal): Location of the vessel North South location
458 - COG(udecimal): Course over ground
459 - TrueHeading(uint): True heading (relative to true North)
460 - TimeStamp(uint): UTC second when the report was generated
461 - RegionalReserved(uint): Reserved for definition by a regional authority. (field automatically set to "0")
462 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
463 - RAIM(bool): Receiver autonomous integrity monitoring flag
464 - syncstate(uint): Sycronization state
465 - slotoffset(uint): In what slot will the next transmission occur. BROKEN
466 @param params: Dictionary of field names/values.
467 @param out: File like object to write to
468 @rtype: stdout
469 @return: text to out
470 '''
471
472 if 'std'==format:
473 out.write("slotoffset:\n")
474 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n")
475 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n")
476 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n")
477 if 'NavigationStatus' in params: out.write(" NavigationStatus: "+str(params['NavigationStatus'])+"\n")
478 if 'ROT' in params: out.write(" ROT: "+str(params['ROT'])+"\n")
479 if 'SOG' in params: out.write(" SOG: "+str(params['SOG'])+"\n")
480 if 'PositionAccuracy' in params: out.write(" PositionAccuracy: "+str(params['PositionAccuracy'])+"\n")
481 if 'Position_longitude' in params: out.write(" Position_longitude: "+str(params['Position_longitude'])+"\n")
482 if 'Position_latitude' in params: out.write(" Position_latitude: "+str(params['Position_latitude'])+"\n")
483 if 'COG' in params: out.write(" COG: "+str(params['COG'])+"\n")
484 if 'TrueHeading' in params: out.write(" TrueHeading: "+str(params['TrueHeading'])+"\n")
485 if 'TimeStamp' in params: out.write(" TimeStamp: "+str(params['TimeStamp'])+"\n")
486 if 'RegionalReserved' in params: out.write(" RegionalReserved: "+str(params['RegionalReserved'])+"\n")
487 if 'Spare' in params: out.write(" Spare: "+str(params['Spare'])+"\n")
488 if 'RAIM' in params: out.write(" RAIM: "+str(params['RAIM'])+"\n")
489 if 'syncstate' in params: out.write(" syncstate: "+str(params['syncstate'])+"\n")
490 if 'slotoffset' in params: out.write(" slotoffset: "+str(params['slotoffset'])+"\n")
491 elif 'csv'==format:
492 if None == options.fieldList:
493 options.fieldList = fieldList
494 needComma = False;
495 for field in fieldList:
496 if needComma: out.write(',')
497 needComma = True
498 if field in params:
499 out.write(str(params[field]))
500
501 out.write("\n")
502 elif 'html'==format:
503 printHtml(params,out)
504 elif 'kml'==format:
505 printKml(params,out)
506 elif 'kml-full'==format:
507 out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
508 out.write("<kml xmlns=\"http://earth.google.com/kml/2.1\">\n")
509 out.write("<Document>\n")
510 out.write(" <name>Position</name>\n")
511 printKml(params,out)
512 out.write("</Document>\n")
513 out.write("</kml>\n")
514 else:
515 print "ERROR: unknown format:",format
516 assert False
517
518 return
519
520 RepeatIndicatorEncodeLut = {
521 'default':'0',
522 'do not repeat any more':'3',
523 }
524
525 RepeatIndicatorDecodeLut = {
526 '0':'default',
527 '3':'do not repeat any more',
528 }
529
530 NavigationStatusEncodeLut = {
531 'under way using engine':'0',
532 'at anchor':'1',
533 'not under command':'2',
534 'restricted maneuverability':'3',
535 'constrained by her draught':'4',
536 'moored':'5',
537 'aground':'6',
538 'engaged in fishing':'7',
539 'under way sailing':'8',
540 'reserved for future use (hazmat)':'9',
541 'reserved for future use':'10',
542 'reserved for future use':'11',
543 'reserved for future use':'12',
544 'reserved for future use':'13',
545 'reserved for future use':'14',
546 'not defined = default':'15',
547 }
548
549 NavigationStatusDecodeLut = {
550 '0':'under way using engine',
551 '1':'at anchor',
552 '2':'not under command',
553 '3':'restricted maneuverability',
554 '4':'constrained by her draught',
555 '5':'moored',
556 '6':'aground',
557 '7':'engaged in fishing',
558 '8':'under way sailing',
559 '9':'reserved for future use (hazmat)',
560 '10':'reserved for future use',
561 '11':'reserved for future use',
562 '12':'reserved for future use',
563 '13':'reserved for future use',
564 '14':'reserved for future use',
565 '15':'not defined = default',
566 }
567
568 SOGEncodeLut = {
569 '102.2 knots or higher':'102.2',
570 }
571
572 SOGDecodeLut = {
573 '102.2':'102.2 knots or higher',
574 }
575
576 PositionAccuracyEncodeLut = {
577 'low (greater than 10 m)':'0',
578 'high (less than 10 m)':'1',
579 }
580
581 PositionAccuracyDecodeLut = {
582 '0':'low (greater than 10 m)',
583 '1':'high (less than 10 m)',
584 }
585
586 TimeStampEncodeLut = {
587 'not available/default':'60',
588 'manual input':'61',
589 'dead reckoning':'62',
590 'inoperative':'63',
591 }
592
593 TimeStampDecodeLut = {
594 '60':'not available/default',
595 '61':'manual input',
596 '62':'dead reckoning',
597 '63':'inoperative',
598 }
599
600 RAIMEncodeLut = {
601 'not in use':'False',
602 'in use':'True',
603 }
604
605 RAIMDecodeLut = {
606 'False':'not in use',
607 'True':'in use',
608 }
609
610 syncstateEncodeLut = {
611 'UTC direct':'0',
612 'UTC indirect':'1',
613 'synchronized to a base station':'2',
614 'synchronized to another station':'3',
615 }
616
617 syncstateDecodeLut = {
618 '0':'UTC direct',
619 '1':'UTC indirect',
620 '2':'synchronized to a base station',
621 '3':'synchronized to another station',
622 }
623
624
625
626
627
628
629 import unittest
631 '''Return a params file base on the testvalue tags.
632 @rtype: dict
633 @return: params based on testvalue tags
634 '''
635 params = {}
636 params['MessageID'] = 1
637 params['RepeatIndicator'] = 1
638 params['UserID'] = 1193046
639 params['NavigationStatus'] = 3
640 params['ROT'] = -2
641 params['SOG'] = Decimal('101.9')
642 params['PositionAccuracy'] = 1
643 params['Position_longitude'] = Decimal('-122.16328055555556')
644 params['Position_latitude'] = Decimal('37.424458333333334')
645 params['COG'] = Decimal('34.5')
646 params['TrueHeading'] = 41
647 params['TimeStamp'] = 35
648 params['RegionalReserved'] = 0
649 params['Spare'] = 0
650 params['RAIM'] = False
651 params['syncstate'] = 2
652 params['slotoffset'] = 1221
653
654 return params
655
657 '''Use testvalue tag text from each type to build test case the position message'''
659
660 params = testParams()
661 bits = encode(params)
662 r = decode(bits)
663
664
665 self.failUnlessEqual(r['MessageID'],params['MessageID'])
666 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
667 self.failUnlessEqual(r['UserID'],params['UserID'])
668 self.failUnlessEqual(r['NavigationStatus'],params['NavigationStatus'])
669 self.failUnlessEqual(r['ROT'],params['ROT'])
670 self.failUnlessAlmostEqual(r['SOG'],params['SOG'],1)
671 self.failUnlessEqual(r['PositionAccuracy'],params['PositionAccuracy'])
672 self.failUnlessAlmostEqual(r['Position_longitude'],params['Position_longitude'],5)
673 self.failUnlessAlmostEqual(r['Position_latitude'],params['Position_latitude'],5)
674 self.failUnlessAlmostEqual(r['COG'],params['COG'],3)
675 self.failUnlessEqual(r['TrueHeading'],params['TrueHeading'])
676 self.failUnlessEqual(r['TimeStamp'],params['TimeStamp'])
677 self.failUnlessEqual(r['RegionalReserved'],params['RegionalReserved'])
678 self.failUnlessEqual(r['Spare'],params['Spare'])
679 self.failUnlessEqual(r['RAIM'],params['RAIM'])
680 self.failUnlessEqual(r['syncstate'],params['syncstate'])
681 self.failUnlessEqual(r['slotoffset'],params['slotoffset'])
682
684 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
685 help='decode a "position" AIS message')
686 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
687 help='encode a "position" AIS message')
688 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
689 ,help='Field parameter value [default: %default]')
690 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
691 ,help='Field parameter value [default: %default]')
692 parser.add_option('--NavigationStatus-field', dest='NavigationStatusField',default=15,metavar='uint',type='int'
693 ,help='Field parameter value [default: %default]')
694 parser.add_option('--ROT-field', dest='ROTField',default=-128,metavar='int',type='int'
695 ,help='Field parameter value [default: %default]')
696 parser.add_option('--SOG-field', dest='SOGField',default=Decimal('102.3'),metavar='udecimal',type='string'
697 ,help='Field parameter value [default: %default]')
698 parser.add_option('--PositionAccuracy-field', dest='PositionAccuracyField',metavar='uint',type='int'
699 ,help='Field parameter value [default: %default]')
700 parser.add_option('--Position_longitude-field', dest='Position_longitudeField',default=Decimal('181'),metavar='decimal',type='string'
701 ,help='Field parameter value [default: %default]')
702 parser.add_option('--Position_latitude-field', dest='Position_latitudeField',default=Decimal('91'),metavar='decimal',type='string'
703 ,help='Field parameter value [default: %default]')
704 parser.add_option('--COG-field', dest='COGField',default=Decimal('360'),metavar='udecimal',type='string'
705 ,help='Field parameter value [default: %default]')
706 parser.add_option('--TrueHeading-field', dest='TrueHeadingField',default=511,metavar='uint',type='int'
707 ,help='Field parameter value [default: %default]')
708 parser.add_option('--TimeStamp-field', dest='TimeStampField',default=60,metavar='uint',type='int'
709 ,help='Field parameter value [default: %default]')
710 parser.add_option('--RAIM-field', dest='RAIMField',metavar='bool',type='int'
711 ,help='Field parameter value [default: %default]')
712 parser.add_option('--syncstate-field', dest='syncstateField',metavar='uint',type='int'
713 ,help='Field parameter value [default: %default]')
714 parser.add_option('--slotoffset-field', dest='slotoffsetField',metavar='uint',type='int'
715 ,help='Field parameter value [default: %default]')
716
717
718 if __name__=='__main__':
719
720 from optparse import OptionParser
721 parser = OptionParser(usage="%prog [options]",
722 version="%prog "+__version__)
723
724 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
725 help='run the documentation tests')
726 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
727 help='run the unit tests')
728 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
729 help='Make the test output verbose')
730
731
732
733 typeChoices = ('binary','nmeapayload','nmea')
734 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
735 ,default='nmeapayload'
736 ,help='What kind of string to expect ('+', '.join(typeChoices)+') [default: %default]')
737
738
739 outputChoices = ('std','html','csv' , 'kml','kml-full')
740 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
741 ,default='std'
742 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
743
744 parser.add_option('-o','--output',dest='outputFileName',default=None,
745 help='Name of the python file to write [default: stdout]')
746
747 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
748 choices=fieldList,
749 help='Which fields to include in the output. Currently only for csv output [default: all]')
750
751 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
752 help='Print the field name for csv')
753
754 addMsgOptions(parser)
755
756 (options,args) = parser.parse_args()
757 success=True
758
759 if options.doctest:
760 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
761 sys.argv= [sys.argv[0]]
762 if options.verbose: sys.argv.append('-v')
763 import doctest
764 numfail,numtests=doctest.testmod()
765 if numfail==0: print 'ok'
766 else:
767 print 'FAILED'
768 success=False
769
770 if not success: sys.exit('Something Failed')
771 del success
772
773 if options.unittest:
774 sys.argv = [sys.argv[0]]
775 if options.verbose: sys.argv.append('-v')
776 unittest.main()
777
778 outfile = sys.stdout
779 if None!=options.outputFileName:
780 outfile = file(options.outputFileName,'w')
781
782
783 if options.doEncode:
784
785 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
786 if None==options.UserIDField: parser.error("missing value for UserIDField")
787 if None==options.NavigationStatusField: parser.error("missing value for NavigationStatusField")
788 if None==options.ROTField: parser.error("missing value for ROTField")
789 if None==options.SOGField: parser.error("missing value for SOGField")
790 if None==options.PositionAccuracyField: parser.error("missing value for PositionAccuracyField")
791 if None==options.Position_longitudeField: parser.error("missing value for Position_longitudeField")
792 if None==options.Position_latitudeField: parser.error("missing value for Position_latitudeField")
793 if None==options.COGField: parser.error("missing value for COGField")
794 if None==options.TrueHeadingField: parser.error("missing value for TrueHeadingField")
795 if None==options.TimeStampField: parser.error("missing value for TimeStampField")
796 if None==options.RAIMField: parser.error("missing value for RAIMField")
797 if None==options.syncstateField: parser.error("missing value for syncstateField")
798 if None==options.slotoffsetField: parser.error("missing value for slotoffsetField")
799 msgDict={
800 'MessageID': '1',
801 'RepeatIndicator': options.RepeatIndicatorField,
802 'UserID': options.UserIDField,
803 'NavigationStatus': options.NavigationStatusField,
804 'ROT': options.ROTField,
805 'SOG': options.SOGField,
806 'PositionAccuracy': options.PositionAccuracyField,
807 'Position_longitude': options.Position_longitudeField,
808 'Position_latitude': options.Position_latitudeField,
809 'COG': options.COGField,
810 'TrueHeading': options.TrueHeadingField,
811 'TimeStamp': options.TimeStampField,
812 'RegionalReserved': '0',
813 'Spare': '0',
814 'RAIM': options.RAIMField,
815 'syncstate': options.syncstateField,
816 'slotoffset': options.slotoffsetField,
817 }
818
819 bits = encode(msgDict)
820 if 'binary'==options.ioType: print str(bits)
821 elif 'nmeapayload'==options.ioType:
822
823 print "bitLen",len(bits)
824 bitLen=len(bits)
825 if bitLen%6!=0:
826 bits = bits + BitVector(size=(6 - (bitLen%6)))
827 print "result:",binary.bitvectoais6(bits)[0]
828
829
830
831 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
832 else: sys.exit('ERROR: unknown ioType. Help!')
833
834 if options.printCsvfieldList:
835
836 if None == options.fieldList: options.fieldList = fieldList
837 import StringIO
838 buf = StringIO.StringIO()
839 for field in options.fieldList:
840 buf.write(field+',')
841 result = buf.getvalue()
842 if result[-1] == ',': print result[:-1]
843 else: print result
844
845 if options.doDecode:
846 for msg in args:
847 bv = None
848 if 'binary' == options.ioType: bv = BitVector(bitstring=msg)
849 elif 'nmeapayload'== options.ioType: bv = binary.ais6tobitvec(msg)
850 elif 'nmea' == options.ioType: bv = binary.ais6tobitvec(msg.split(',')[5])
851 else: sys.exit('ERROR: unknown ioType. Help!')
852
853 printFields(decode(bv),out=outfile,format=options.outputType,fieldList=options.fieldList)
854