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 '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 import unittest
404 '''Return a params file base on the testvalue tags.
405 @rtype: dict
406 @return: params based on testvalue tags
407 '''
408 params = {}
409 params['MessageID'] = 14
410 params['RepeatIndicator'] = 1
411 params['UserID'] = 1193046
412 params['Spare2'] = 0
413
414 return params
415
417 '''Use testvalue tag text from each type to build test case the srbm message'''
419
420 params = testParams()
421 bits = encode(params)
422 r = decode(bits)
423
424
425 self.failUnlessEqual(r['MessageID'],params['MessageID'])
426 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
427 self.failUnlessEqual(r['UserID'],params['UserID'])
428 self.failUnlessEqual(r['Spare2'],params['Spare2'])
429
431 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
432 help='decode a "srbm" AIS message')
433 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
434 help='encode a "srbm" AIS message')
435 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
436 ,help='Field parameter value [default: %default]')
437 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
438 ,help='Field parameter value [default: %default]')
439
440
441 if __name__=='__main__':
442
443 from optparse import OptionParser
444 parser = OptionParser(usage="%prog [options]",
445 version="%prog "+__version__)
446
447 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
448 help='run the documentation tests')
449 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
450 help='run the unit tests')
451 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
452 help='Make the test output verbose')
453
454
455
456 typeChoices = ('binary','nmeapayload','nmea')
457 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
458 ,default='nmeapayload'
459 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
460
461
462 outputChoices = ('std','html','csv','sql' )
463 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
464 ,default='std'
465 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
466
467 parser.add_option('-o','--output',dest='outputFileName',default=None,
468 help='Name of the python file to write [default: stdout]')
469
470 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
471 choices=fieldList,
472 help='Which fields to include in the output. Currently only for csv output [default: all]')
473
474 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
475 help='Print the field name for csv')
476
477 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
478 help='Print out an sql create command for the table.')
479
480 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true',
481 help='Print a LaTeX table of the type')
482
483 dbChoices = ('sqlite','postgres')
484 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
485 ,choices=dbChoices,type='choice'
486 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
487
488 addMsgOptions(parser)
489
490 (options,args) = parser.parse_args()
491 success=True
492
493 if options.doctest:
494 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
495 sys.argv= [sys.argv[0]]
496 if options.verbose: sys.argv.append('-v')
497 import doctest
498 numfail,numtests=doctest.testmod()
499 if numfail==0: print 'ok'
500 else:
501 print 'FAILED'
502 success=False
503
504 if not success: sys.exit('Something Failed')
505 del success
506
507 if options.unittest:
508 sys.argv = [sys.argv[0]]
509 if options.verbose: sys.argv.append('-v')
510 unittest.main()
511
512 outfile = sys.stdout
513 if None!=options.outputFileName:
514 outfile = file(options.outputFileName,'w')
515
516
517 if options.doEncode:
518
519 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
520 if None==options.UserIDField: parser.error("missing value for UserIDField")
521 msgDict={
522 'MessageID': '14',
523 'RepeatIndicator': options.RepeatIndicatorField,
524 'UserID': options.UserIDField,
525 'Spare2': '0',
526 }
527
528 bits = encode(msgDict)
529 if 'binary'==options.ioType: print str(bits)
530 elif 'nmeapayload'==options.ioType:
531
532 print "bitLen",len(bits)
533 bitLen=len(bits)
534 if bitLen%6!=0:
535 bits = bits + BitVector(size=(6 - (bitLen%6)))
536 print "result:",binary.bitvectoais6(bits)[0]
537
538
539
540 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
541 else: sys.exit('ERROR: unknown ioType. Help!')
542
543
544 if options.sqlCreate:
545 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
546
547 if options.latexDefinitionTable:
548 latexDefinitionTable(outfile)
549
550 if options.printCsvfieldList:
551
552 if None == options.fieldList: options.fieldList = fieldList
553 import StringIO
554 buf = StringIO.StringIO()
555 for field in options.fieldList:
556 buf.write(field+',')
557 result = buf.getvalue()
558 if result[-1] == ',': print result[:-1]
559 else: print result
560
561 if options.doDecode:
562 for msg in args:
563 bv = None
564
565 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
566
567
568 bv = binary.ais6tobitvec(msg.split(',')[5])
569 else:
570
571 binaryMsg=True
572 for c in msg:
573 if c not in ('0','1'):
574 binaryMsg=False
575 break
576 if binaryMsg:
577 bv = BitVector(bitstring=msg)
578 else:
579 bv = binary.ais6tobitvec(msg)
580
581 printFields(decode(bv)
582 ,out=outfile
583 ,format=options.outputType
584 ,fieldList=options.fieldList
585 ,dbType=options.dbType
586 )
587