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