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