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