1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2008-01-09 $'.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 dbTableName='utcquery'
285 'Database table name'
286
287 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
288 ,addCoastGuardFields=True
289 ,dbType='postgres'
290 ):
291 '''
292 Return the SQL CREATE command for this message type
293 @param outfile: file like object to print to.
294 @param fields: which fields to put in the create. Defaults to all.
295 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
296 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
297 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
298 @type addCoastGuardFields: bool
299 @return: sql create string
300 @rtype: str
301
302 @see: sqlCreate
303 '''
304
305 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
306
307 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
308 '''
309 Return the sqlhelp object to create the table.
310
311 @param fields: which fields to put in the create. Defaults to all.
312 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
313 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
314 @type addCoastGuardFields: bool
315 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
316 @return: An object that can be used to generate a return
317 @rtype: sqlhelp.create
318 '''
319 if None == fields: fields = fieldList
320 import sqlhelp
321 c = sqlhelp.create('utcquery',dbType=dbType)
322 c.addPrimaryKey()
323 if 'MessageID' in fields: c.addInt ('MessageID')
324 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
325 if 'UserID' in fields: c.addInt ('UserID')
326 if 'Spare1' in fields: c.addInt ('Spare1')
327 if 'DestID' in fields: c.addInt ('DestID')
328 if 'Spare2' in fields: c.addInt ('Spare2')
329
330 if addCoastGuardFields:
331
332
333
334
335
336 c.addVarChar('cg_r',15)
337 c.addInt('cg_sec')
338
339 c.addTimestamp('cg_timestamp')
340
341 return c
342
343 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
344 '''
345 Return the SQL INSERT command for this message type
346 @param params: dictionary of values keyed by field name
347 @param outfile: file like object to print to.
348 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
349 @return: sql create string
350 @rtype: str
351
352 @see: sqlCreate
353 '''
354 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
355
356
357 -def sqlInsert(params,extraParams=None,dbType='postgres'):
358 '''
359 Give the SQL INSERT statement
360 @param params: dict keyed by field name of values
361 @param extraParams: any extra fields that you have created beyond the normal ais message fields
362 @rtype: sqlhelp.insert
363 @return: insert class instance
364 @todo: allow optional type checking of params?
365 @warning: this will take invalid keys happily and do what???
366 '''
367 import sqlhelp
368 i = sqlhelp.insert('utcquery',dbType=dbType)
369
370 if dbType=='postgres':
371 finished = []
372 for key in params:
373 if key in finished:
374 continue
375
376 if key not in toPgFields and key not in fromPgFields:
377 if type(params[key])==Decimal: i.add(key,float(params[key]))
378 else: i.add(key,params[key])
379 else:
380 if key in fromPgFields:
381 val = params[key]
382
383 i.addPostGIS(key,val)
384 finished.append(key)
385 else:
386
387 pgName = toPgFields[key]
388
389 valStr=pgTypes[pgName]+'('
390 vals = []
391 for nonPgKey in fromPgFields[pgName]:
392 vals.append(str(params[nonPgKey]))
393 finished.append(nonPgKey)
394 valStr+=' '.join(vals)+')'
395 i.addPostGIS(pgName,valStr)
396 else:
397 for key in params:
398 if type(params[key])==Decimal: i.add(key,float(params[key]))
399 else: i.add(key,params[key])
400
401 if None != extraParams:
402 for key in extraParams:
403 i.add(key,extraParams[key])
404
405 return i
406
407
408
409
410
413 '''
414 Return the LaTeX definition table for this message type
415 @param outfile: file like object to print to.
416 @type outfile: file obj
417 @return: LaTeX table string via the outfile
418 @rtype: str
419
420 '''
421 o = outfile
422
423 o.write('''
424 \\begin{table}%[htb]
425 \\centering
426 \\begin{tabular}{|l|c|l|}
427 \\hline
428 Parameter & Number of bits & Description
429 \\\\ \\hline\\hline
430 MessageID & 6 & AIS message number. Must be 10 \\\\ \hline
431 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
432 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
433 Spare1 & 2 & Not used. Should be set to zero. \\\\ \hline
434 DestID & 30 & Unique ship identification number (MMSI) \\\\ \hline
435 Spare2 & 2 & Not used. Should be set to zero.\\\\ \\hline \\hline
436 Total bits & 72 & Appears to take 1 slot with 96 pad bits to fill the last slot \\\\ \\hline
437 \\end{tabular}
438 \\caption{AIS message number 10: Search and rescue position report}
439 \\label{tab:utcquery}
440 \\end{table}
441 ''')
442
443
444
445
446
447 -def textDefinitionTable(outfile=sys.stdout
448 ,delim='\t'
449 ):
450 '''
451 Return the text definition table for this message type
452 @param outfile: file like object to print to.
453 @type outfile: file obj
454 @return: text table string via the outfile
455 @rtype: str
456
457 '''
458 o = outfile
459 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description
460 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 10
461 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated
462 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
463 Spare1'''+delim+'''2'''+delim+'''Not used. Should be set to zero.
464 DestID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
465 Spare2'''+delim+'''2'''+delim+'''Not used. Should be set to zero.
466 Total bits'''+delim+'''72'''+delim+'''Appears to take 1 slot with 96 pad bits to fill the last slot''')
467
468
469
470
471
472 import unittest
474 '''Return a params file base on the testvalue tags.
475 @rtype: dict
476 @return: params based on testvalue tags
477 '''
478 params = {}
479 params['MessageID'] = 10
480 params['RepeatIndicator'] = 1
481 params['UserID'] = 1193046
482 params['Spare1'] = 0
483 params['DestID'] = 1193046
484 params['Spare2'] = 0
485
486 return params
487
489 '''Use testvalue tag text from each type to build test case the utcquery message'''
491
492 params = testParams()
493 bits = encode(params)
494 r = decode(bits)
495
496
497 self.failUnlessEqual(r['MessageID'],params['MessageID'])
498 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
499 self.failUnlessEqual(r['UserID'],params['UserID'])
500 self.failUnlessEqual(r['Spare1'],params['Spare1'])
501 self.failUnlessEqual(r['DestID'],params['DestID'])
502 self.failUnlessEqual(r['Spare2'],params['Spare2'])
503
505 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
506 help='decode a "utcquery" AIS message')
507 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
508 help='encode a "utcquery" AIS message')
509 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
510 ,help='Field parameter value [default: %default]')
511 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
512 ,help='Field parameter value [default: %default]')
513 parser.add_option('--DestID-field', dest='DestIDField',metavar='uint',type='int'
514 ,help='Field parameter value [default: %default]')
515
516
517 if __name__=='__main__':
518
519 from optparse import OptionParser
520 parser = OptionParser(usage="%prog [options]",
521 version="%prog "+__version__)
522
523 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
524 help='run the documentation tests')
525 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
526 help='run the unit tests')
527 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
528 help='Make the test output verbose')
529
530
531
532 typeChoices = ('binary','nmeapayload','nmea')
533 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
534 ,default='nmeapayload'
535 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
536
537
538 outputChoices = ('std','html','csv','sql' )
539 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
540 ,default='std'
541 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
542
543 parser.add_option('-o','--output',dest='outputFileName',default=None,
544 help='Name of the python file to write [default: stdout]')
545
546 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
547 choices=fieldList,
548 help='Which fields to include in the output. Currently only for csv output [default: all]')
549
550 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
551 help='Print the field name for csv')
552
553 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
554 help='Print out an sql create command for the table.')
555
556 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true',
557 help='Print a LaTeX table of the type')
558
559 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true',
560 help='Print delimited table of the type (for Word table importing)')
561 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t'
562 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)')
563
564
565 dbChoices = ('sqlite','postgres')
566 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
567 ,choices=dbChoices,type='choice'
568 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
569
570 addMsgOptions(parser)
571
572 (options,args) = parser.parse_args()
573 success=True
574
575 if options.doctest:
576 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
577 sys.argv= [sys.argv[0]]
578 if options.verbose: sys.argv.append('-v')
579 import doctest
580 numfail,numtests=doctest.testmod()
581 if numfail==0: print 'ok'
582 else:
583 print 'FAILED'
584 success=False
585
586 if not success: sys.exit('Something Failed')
587 del success
588
589 if options.unittest:
590 sys.argv = [sys.argv[0]]
591 if options.verbose: sys.argv.append('-v')
592 unittest.main()
593
594 outfile = sys.stdout
595 if None!=options.outputFileName:
596 outfile = file(options.outputFileName,'w')
597
598
599 if options.doEncode:
600
601 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
602 if None==options.UserIDField: parser.error("missing value for UserIDField")
603 if None==options.DestIDField: parser.error("missing value for DestIDField")
604 msgDict={
605 'MessageID': '10',
606 'RepeatIndicator': options.RepeatIndicatorField,
607 'UserID': options.UserIDField,
608 'Spare1': '0',
609 'DestID': options.DestIDField,
610 'Spare2': '0',
611 }
612
613 bits = encode(msgDict)
614 if 'binary'==options.ioType: print str(bits)
615 elif 'nmeapayload'==options.ioType:
616
617 print "bitLen",len(bits)
618 bitLen=len(bits)
619 if bitLen%6!=0:
620 bits = bits + BitVector(size=(6 - (bitLen%6)))
621 print "result:",binary.bitvectoais6(bits)[0]
622
623
624
625 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
626 else: sys.exit('ERROR: unknown ioType. Help!')
627
628
629 if options.sqlCreate:
630 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
631
632 if options.latexDefinitionTable:
633 latexDefinitionTable(outfile)
634
635
636 if options.textDefinitionTable:
637 textDefinitionTable(outfile,options.delimTextDefinitionTable)
638
639 if options.printCsvfieldList:
640
641 if None == options.fieldList: options.fieldList = fieldList
642 import StringIO
643 buf = StringIO.StringIO()
644 for field in options.fieldList:
645 buf.write(field+',')
646 result = buf.getvalue()
647 if result[-1] == ',': print result[:-1]
648 else: print result
649
650 if options.doDecode:
651 if len(args)==0: args = sys.stdin
652 for msg in args:
653 bv = None
654
655 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
656
657
658 bv = binary.ais6tobitvec(msg.split(',')[5])
659 else:
660
661 binaryMsg=True
662 for c in msg:
663 if c not in ('0','1'):
664 binaryMsg=False
665 break
666 if binaryMsg:
667 bv = BitVector(bitstring=msg)
668 else:
669 bv = binary.ais6tobitvec(msg)
670
671 printFields(decode(bv)
672 ,out=outfile
673 ,format=options.outputType
674 ,fieldList=options.fieldList
675 ,dbType=options.dbType
676 )
677