1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2007-12-04 $'.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 @todo: FIX: put in a description of the message here with fields and types.
34 '''
35
36 import sys
37 from decimal import Decimal
38 from BitVector import BitVector
39
40 import binary, aisstring
41
42
43 TrueBV = BitVector(bitstring="1")
44 "Why always rebuild the True bit? This should speed things up a bunch"
45 FalseBV = BitVector(bitstring="0")
46 "Why always rebuild the False bit? This should speed things up a bunch"
47
48
49 fieldList = (
50 'MessageID',
51 'RepeatIndicator',
52 'UserID',
53 'Spare1',
54 'DestID',
55 'Spare2',
56 )
57
58 fieldListPostgres = (
59 'MessageID',
60 'RepeatIndicator',
61 'UserID',
62 'Spare1',
63 'DestID',
64 'Spare2',
65 )
66
67 toPgFields = {
68 }
69 '''
70 Go to the Postgis field names from the straight field name
71 '''
72
73 fromPgFields = {
74 }
75 '''
76 Go from the Postgis field names to the straight field name
77 '''
78
79 pgTypes = {
80 }
81 '''
82 Lookup table for each postgis field name to get its type.
83 '''
84
85 -def encode(params, validate=False):
86 '''Create a utcquery binary message payload to pack into an AIS Msg utcquery.
87
88 Fields in params:
89 - MessageID(uint): AIS message number. Must be 10 (field automatically set to "10")
90 - RepeatIndicator(uint): Indicated how many times a message has been repeated
91 - UserID(uint): Unique ship identification number (MMSI)
92 - Spare1(uint): Not used. Should be set to zero. (field automatically set to "0")
93 - DestID(uint): Unique ship identification number (MMSI)
94 - Spare2(uint): Not used. Should be set to zero. (field automatically set to "0")
95 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing
96 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
97 @rtype: BitVector
98 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
99 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.
100 '''
101
102 bvList = []
103 bvList.append(binary.setBitVectorSize(BitVector(intVal=10),6))
104 if 'RepeatIndicator' in params:
105 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
106 else:
107 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
108 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
109 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
110 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['DestID']),30))
111 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
112
113 return binary.joinBV(bvList)
114
115 -def decode(bv, validate=False):
116 '''Unpack a utcquery message
117
118 Fields in params:
119 - MessageID(uint): AIS message number. Must be 10 (field automatically set to "10")
120 - RepeatIndicator(uint): Indicated how many times a message has been repeated
121 - UserID(uint): Unique ship identification number (MMSI)
122 - Spare1(uint): Not used. Should be set to zero. (field automatically set to "0")
123 - DestID(uint): Unique ship identification number (MMSI)
124 - Spare2(uint): Not used. Should be set to zero. (field automatically set to "0")
125 @type bv: BitVector
126 @param bv: Bits defining a message
127 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
128 @rtype: dict
129 @return: params
130 '''
131
132
133
134
135 r = {}
136 r['MessageID']=10
137 r['RepeatIndicator']=int(bv[6:8])
138 r['UserID']=int(bv[8:38])
139 r['Spare1']=0
140 r['DestID']=int(bv[40:70])
141 r['Spare2']=0
142 return r
143
146
149
152
155
157 return int(bv[40:70])
158
161
162
164 out.write("<h3>utcquery</h3>\n")
165 out.write("<table border=\"1\">\n")
166 out.write("<tr bgcolor=\"orange\">\n")
167 out.write("<th align=\"left\">Field Name</th>\n")
168 out.write("<th align=\"left\">Type</th>\n")
169 out.write("<th align=\"left\">Value</th>\n")
170 out.write("<th align=\"left\">Value in Lookup Table</th>\n")
171 out.write("<th align=\"left\">Units</th>\n")
172 out.write("\n")
173 out.write("<tr>\n")
174 out.write("<td>MessageID</td>\n")
175 out.write("<td>uint</td>\n")
176 if 'MessageID' in params:
177 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
178 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
179 out.write("</tr>\n")
180 out.write("\n")
181 out.write("<tr>\n")
182 out.write("<td>RepeatIndicator</td>\n")
183 out.write("<td>uint</td>\n")
184 if 'RepeatIndicator' in params:
185 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n")
186 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
187 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
188 else:
189 out.write("<td><i>Missing LUT entry</i></td>")
190 out.write("</tr>\n")
191 out.write("\n")
192 out.write("<tr>\n")
193 out.write("<td>UserID</td>\n")
194 out.write("<td>uint</td>\n")
195 if 'UserID' in params:
196 out.write(" <td>"+str(params['UserID'])+"</td>\n")
197 out.write(" <td>"+str(params['UserID'])+"</td>\n")
198 out.write("</tr>\n")
199 out.write("\n")
200 out.write("<tr>\n")
201 out.write("<td>Spare1</td>\n")
202 out.write("<td>uint</td>\n")
203 if 'Spare1' in params:
204 out.write(" <td>"+str(params['Spare1'])+"</td>\n")
205 out.write(" <td>"+str(params['Spare1'])+"</td>\n")
206 out.write("</tr>\n")
207 out.write("\n")
208 out.write("<tr>\n")
209 out.write("<td>DestID</td>\n")
210 out.write("<td>uint</td>\n")
211 if 'DestID' in params:
212 out.write(" <td>"+str(params['DestID'])+"</td>\n")
213 out.write(" <td>"+str(params['DestID'])+"</td>\n")
214 out.write("</tr>\n")
215 out.write("\n")
216 out.write("<tr>\n")
217 out.write("<td>Spare2</td>\n")
218 out.write("<td>uint</td>\n")
219 if 'Spare2' in params:
220 out.write(" <td>"+str(params['Spare2'])+"</td>\n")
221 out.write(" <td>"+str(params['Spare2'])+"</td>\n")
222 out.write("</tr>\n")
223 out.write("</table>\n")
224
225 -def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
226 '''Print a utcquery message to stdout.
227
228 Fields in params:
229 - MessageID(uint): AIS message number. Must be 10 (field automatically set to "10")
230 - RepeatIndicator(uint): Indicated how many times a message has been repeated
231 - UserID(uint): Unique ship identification number (MMSI)
232 - Spare1(uint): Not used. Should be set to zero. (field automatically set to "0")
233 - DestID(uint): Unique ship identification number (MMSI)
234 - Spare2(uint): Not used. Should be set to zero. (field automatically set to "0")
235 @param params: Dictionary of field names/values.
236 @param out: File like object to write to
237 @rtype: stdout
238 @return: text to out
239 '''
240
241 if 'std'==format:
242 out.write("utcquery:\n")
243 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n")
244 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n")
245 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n")
246 if 'Spare1' in params: out.write(" Spare1: "+str(params['Spare1'])+"\n")
247 if 'DestID' in params: out.write(" DestID: "+str(params['DestID'])+"\n")
248 if 'Spare2' in params: out.write(" Spare2: "+str(params['Spare2'])+"\n")
249 elif 'csv'==format:
250 if None == options.fieldList:
251 options.fieldList = fieldList
252 needComma = False;
253 for field in fieldList:
254 if needComma: out.write(',')
255 needComma = True
256 if field in params:
257 out.write(str(params[field]))
258
259 out.write("\n")
260 elif 'html'==format:
261 printHtml(params,out)
262 elif 'sql'==format:
263 sqlInsertStr(params,out,dbType=dbType)
264 else:
265 print "ERROR: unknown format:",format
266 assert False
267
268 return
269
270 RepeatIndicatorEncodeLut = {
271 'default':'0',
272 'do not repeat any more':'3',
273 }
274
275 RepeatIndicatorDecodeLut = {
276 '0':'default',
277 '3':'do not repeat any more',
278 }
279
280
281
282
283
284 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
285 ,addCoastGuardFields=True
286 ,dbType='postgres'
287 ):
288 '''
289 Return the SQL CREATE command for this message type
290 @param outfile: file like object to print to.
291 @param fields: which fields to put in the create. Defaults to all.
292 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
293 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
294 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
295 @type addCoastGuardFields: bool
296 @return: sql create string
297 @rtype: str
298
299 @see: sqlCreate
300 '''
301
302 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
303
304 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
305 '''
306 Return the sqlhelp object to create the table.
307
308 @param fields: which fields to put in the create. Defaults to all.
309 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
310 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
311 @type addCoastGuardFields: bool
312 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
313 @return: An object that can be used to generate a return
314 @rtype: sqlhelp.create
315 '''
316 if None == fields: fields = fieldList
317 import sqlhelp
318 c = sqlhelp.create('utcquery',dbType=dbType)
319 c.addPrimaryKey()
320 if 'MessageID' in fields: c.addInt ('MessageID')
321 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
322 if 'UserID' in fields: c.addInt ('UserID')
323 if 'Spare1' in fields: c.addInt ('Spare1')
324 if 'DestID' in fields: c.addInt ('DestID')
325 if 'Spare2' in fields: c.addInt ('Spare2')
326
327 if addCoastGuardFields:
328
329
330
331
332
333 c.addVarChar('cg_r',15)
334 c.addInt('cg_sec')
335
336 c.addTimestamp('cg_timestamp')
337
338 return c
339
340 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
341 '''
342 Return the SQL INSERT command for this message type
343 @param params: dictionary of values keyed by field name
344 @param outfile: file like object to print to.
345 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
346 @return: sql create string
347 @rtype: str
348
349 @see: sqlCreate
350 '''
351 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
352
353
354 -def sqlInsert(params,extraParams=None,dbType='postgres'):
355 '''
356 Give the SQL INSERT statement
357 @param params: dict keyed by field name of values
358 @param extraParams: any extra fields that you have created beyond the normal ais message fields
359 @rtype: sqlhelp.insert
360 @return: insert class instance
361 @todo: allow optional type checking of params?
362 @warning: this will take invalid keys happily and do what???
363 '''
364 import sqlhelp
365 i = sqlhelp.insert('utcquery',dbType=dbType)
366
367 if dbType=='postgres':
368 finished = []
369 for key in params:
370 if key in finished:
371 continue
372
373 if key not in toPgFields and key not in fromPgFields:
374 if type(params[key])==Decimal: i.add(key,float(params[key]))
375 else: i.add(key,params[key])
376 else:
377 if key in fromPgFields:
378 val = params[key]
379
380 i.addPostGIS(key,val)
381 finished.append(key)
382 else:
383
384 pgName = toPgFields[key]
385
386 valStr=pgTypes[pgName]+'('
387 vals = []
388 for nonPgKey in fromPgFields[pgName]:
389 vals.append(str(params[nonPgKey]))
390 finished.append(nonPgKey)
391 valStr+=' '.join(vals)+')'
392 i.addPostGIS(pgName,valStr)
393 else:
394 for key in params:
395 if type(params[key])==Decimal: i.add(key,float(params[key]))
396 else: i.add(key,params[key])
397
398 if None != extraParams:
399 for key in extraParams:
400 i.add(key,extraParams[key])
401
402 return i
403
404
405
406
407
410 '''
411 Return the LaTeX definition table for this message type
412 @param outfile: file like object to print to.
413 @type outfile: file obj
414 @return: LaTeX table string via the outfile
415 @rtype: str
416
417 '''
418 o = outfile
419
420 o.write('''
421 \\begin{table}%[htb]
422 \\centering
423 \\begin{tabular}{|l|c|l|}
424 \\hline
425 Parameter & Number of bits & Description
426 \\\\ \\hline\\hline
427 MessageID & 6 & AIS message number. Must be 10 \\\\ \hline
428 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
429 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
430 Spare1 & 2 & Not used. Should be set to zero. \\\\ \hline
431 DestID & 30 & Unique ship identification number (MMSI) \\\\ \hline
432 Spare2 & 2 & Not used. Should be set to zero.\\\\ \\hline \\hline
433 Total bits & 72 & Appears to take 1 slot with 96 pad bits to fill the last slot \\\\ \\hline
434 \\end{tabular}
435 \\caption{AIS message number 10: Search and rescue position report}
436 \\label{tab:utcquery}
437 \\end{table}
438 ''')
439
440
441
442
443
444 -def textDefinitionTable(outfile=sys.stdout
445 ,delim='\t'
446 ):
447 '''
448 Return the text definition table for this message type
449 @param outfile: file like object to print to.
450 @type outfile: file obj
451 @return: text table string via the outfile
452 @rtype: str
453
454 '''
455 o = outfile
456 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description
457 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 10
458 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated
459 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
460 Spare1'''+delim+'''2'''+delim+'''Not used. Should be set to zero.
461 DestID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
462 Spare2'''+delim+'''2'''+delim+'''Not used. Should be set to zero.
463 Total bits'''+delim+'''72'''+delim+'''Appears to take 1 slot with 96 pad bits to fill the last slot''')
464
465
466
467
468
469 import unittest
471 '''Return a params file base on the testvalue tags.
472 @rtype: dict
473 @return: params based on testvalue tags
474 '''
475 params = {}
476 params['MessageID'] = 10
477 params['RepeatIndicator'] = 1
478 params['UserID'] = 1193046
479 params['Spare1'] = 0
480 params['DestID'] = 1193046
481 params['Spare2'] = 0
482
483 return params
484
486 '''Use testvalue tag text from each type to build test case the utcquery message'''
488
489 params = testParams()
490 bits = encode(params)
491 r = decode(bits)
492
493
494 self.failUnlessEqual(r['MessageID'],params['MessageID'])
495 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
496 self.failUnlessEqual(r['UserID'],params['UserID'])
497 self.failUnlessEqual(r['Spare1'],params['Spare1'])
498 self.failUnlessEqual(r['DestID'],params['DestID'])
499 self.failUnlessEqual(r['Spare2'],params['Spare2'])
500
502 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
503 help='decode a "utcquery" AIS message')
504 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
505 help='encode a "utcquery" AIS message')
506 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
507 ,help='Field parameter value [default: %default]')
508 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
509 ,help='Field parameter value [default: %default]')
510 parser.add_option('--DestID-field', dest='DestIDField',metavar='uint',type='int'
511 ,help='Field parameter value [default: %default]')
512
513
514 if __name__=='__main__':
515
516 from optparse import OptionParser
517 parser = OptionParser(usage="%prog [options]",
518 version="%prog "+__version__)
519
520 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
521 help='run the documentation tests')
522 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
523 help='run the unit tests')
524 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
525 help='Make the test output verbose')
526
527
528
529 typeChoices = ('binary','nmeapayload','nmea')
530 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
531 ,default='nmeapayload'
532 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
533
534
535 outputChoices = ('std','html','csv','sql' )
536 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
537 ,default='std'
538 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
539
540 parser.add_option('-o','--output',dest='outputFileName',default=None,
541 help='Name of the python file to write [default: stdout]')
542
543 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
544 choices=fieldList,
545 help='Which fields to include in the output. Currently only for csv output [default: all]')
546
547 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
548 help='Print the field name for csv')
549
550 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
551 help='Print out an sql create command for the table.')
552
553 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true',
554 help='Print a LaTeX table of the type')
555
556 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true',
557 help='Print delimited table of the type (for Word table importing)')
558 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t'
559 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)')
560
561
562 dbChoices = ('sqlite','postgres')
563 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
564 ,choices=dbChoices,type='choice'
565 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
566
567 addMsgOptions(parser)
568
569 (options,args) = parser.parse_args()
570 success=True
571
572 if options.doctest:
573 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
574 sys.argv= [sys.argv[0]]
575 if options.verbose: sys.argv.append('-v')
576 import doctest
577 numfail,numtests=doctest.testmod()
578 if numfail==0: print 'ok'
579 else:
580 print 'FAILED'
581 success=False
582
583 if not success: sys.exit('Something Failed')
584 del success
585
586 if options.unittest:
587 sys.argv = [sys.argv[0]]
588 if options.verbose: sys.argv.append('-v')
589 unittest.main()
590
591 outfile = sys.stdout
592 if None!=options.outputFileName:
593 outfile = file(options.outputFileName,'w')
594
595
596 if options.doEncode:
597
598 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
599 if None==options.UserIDField: parser.error("missing value for UserIDField")
600 if None==options.DestIDField: parser.error("missing value for DestIDField")
601 msgDict={
602 'MessageID': '10',
603 'RepeatIndicator': options.RepeatIndicatorField,
604 'UserID': options.UserIDField,
605 'Spare1': '0',
606 'DestID': options.DestIDField,
607 'Spare2': '0',
608 }
609
610 bits = encode(msgDict)
611 if 'binary'==options.ioType: print str(bits)
612 elif 'nmeapayload'==options.ioType:
613
614 print "bitLen",len(bits)
615 bitLen=len(bits)
616 if bitLen%6!=0:
617 bits = bits + BitVector(size=(6 - (bitLen%6)))
618 print "result:",binary.bitvectoais6(bits)[0]
619
620
621
622 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
623 else: sys.exit('ERROR: unknown ioType. Help!')
624
625
626 if options.sqlCreate:
627 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
628
629 if options.latexDefinitionTable:
630 latexDefinitionTable(outfile)
631
632
633 if options.textDefinitionTable:
634 textDefinitionTable(outfile,options.delimTextDefinitionTable)
635
636 if options.printCsvfieldList:
637
638 if None == options.fieldList: options.fieldList = fieldList
639 import StringIO
640 buf = StringIO.StringIO()
641 for field in options.fieldList:
642 buf.write(field+',')
643 result = buf.getvalue()
644 if result[-1] == ',': print result[:-1]
645 else: print result
646
647 if options.doDecode:
648 if len(args)==0: args = sys.stdin
649 for msg in args:
650 bv = None
651
652 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
653
654
655 bv = binary.ais6tobitvec(msg.split(',')[5])
656 else:
657
658 binaryMsg=True
659 for c in msg:
660 if c not in ('0','1'):
661 binaryMsg=False
662 break
663 if binaryMsg:
664 bv = BitVector(bitstring=msg)
665 else:
666 bv = binary.ais6tobitvec(msg)
667
668 printFields(decode(bv)
669 ,out=outfile
670 ,format=options.outputType
671 ,fieldList=options.fieldList
672 ,dbType=options.dbType
673 )
674