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