Package ais :: Module ais_msg_14
[hide private]
[frames] | no frames]

Source Code for Module ais.ais_msg_14

  1  #!/usr/bin/env python 
  2   
  3  __version__ = '$Revision: 4791 $'.split()[1] 
  4  __date__ = '$Date: 2007-03-18 $'.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  # FIX: check to see if these will be needed 
 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 #Would be nice to check the bit count here.. 123 #if validate: 124 # assert (len(bv)==FIX: SOME NUMBER) 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
132 -def decodeMessageID(bv, validate=False):
133 return 14
134
135 -def decodeRepeatIndicator(bv, validate=False):
136 return int(bv[6:8])
137
138 -def decodeUserID(bv, validate=False):
139 return int(bv[8:38])
140
141 -def decodeSpare2(bv, validate=False):
142 return 0
143 144
145 -def printHtml(params, out=sys.stdout):
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 # else: leave it empty 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 # Nothing to return
231 232 RepeatIndicatorEncodeLut = { 233 'default':'0', 234 'do not repeat any more':'3', 235 } #RepeatIndicatorEncodeLut 236 237 RepeatIndicatorDecodeLut = { 238 '0':'default', 239 '3':'do not repeat any more', 240 } # RepeatIndicatorEncodeLut 241 242 ###################################################################### 243 # SQL SUPPORT 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 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
264
265 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
266 ''' 267 Return the sqlhelp object to create the table. 268 269 @param fields: which fields to put in the create. Defaults to all. 270 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 271 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 272 @type addCoastGuardFields: bool 273 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 274 @return: An object that can be used to generate a return 275 @rtype: sqlhelp.create 276 ''' 277 if None == fields: fields = fieldList 278 import sqlhelp 279 c = sqlhelp.create('srbm',dbType=dbType) 280 c.addPrimaryKey() 281 if 'MessageID' in fields: c.addInt ('MessageID') 282 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator') 283 if 'UserID' in fields: c.addInt ('UserID') 284 if 'Spare2' in fields: c.addInt ('Spare2') 285 286 if addCoastGuardFields: 287 # c.addInt('cg_rssi') # Relative signal strength indicator 288 # c.addInt('cg_d') # dBm receive strength 289 # c.addInt('cg_T') # Receive timestamp from the AIS equipment 290 # c.addInt('cg_S') # Slot received in 291 # c.addVarChar('cg_x',10) # Idonno 292 c.addVarChar('cg_r',15) # Receiver station ID - should usually be an MMSI, but sometimes is a string 293 c.addInt('cg_sec') # UTC seconds since the epoch 294 295 c.addTimestamp('cg_timestamp') # UTC decoded cg_sec - not actually in the data stream 296 297 return c
298
299 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
300 ''' 301 Return the SQL INSERT command for this message type 302 @param params: dictionary of values keyed by field name 303 @param outfile: file like object to print to. 304 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields 305 @return: sql create string 306 @rtype: str 307 308 @see: sqlCreate 309 ''' 310 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
311 312
313 -def sqlInsert(params,extraParams=None,dbType='postgres'):
314 ''' 315 Give the SQL INSERT statement 316 @param params: dict keyed by field name of values 317 @param extraParams: any extra fields that you have created beyond the normal ais message fields 318 @rtype: sqlhelp.insert 319 @return: insert class instance 320 @todo: allow optional type checking of params? 321 @warning: this will take invalid keys happily and do what??? 322 ''' 323 import sqlhelp 324 i = sqlhelp.insert('srbm',dbType=dbType) 325 326 if dbType=='postgres': 327 finished = [] 328 for key in params: 329 if key in finished: 330 continue 331 332 if key not in toPgFields and key not in fromPgFields: 333 if type(params[key])==Decimal: i.add(key,float(params[key])) 334 else: i.add(key,params[key]) 335 else: 336 if key in fromPgFields: 337 val = params[key] 338 # Had better be a WKT type like POINT(-88.1 30.321) 339 i.addPostGIS(key,val) 340 finished.append(key) 341 else: 342 # Need to construct the type. 343 pgName = toPgFields[key] 344 #valStr='GeomFromText(\''+pgTypes[pgName]+'(' 345 valStr=pgTypes[pgName]+'(' 346 vals = [] 347 for nonPgKey in fromPgFields[pgName]: 348 vals.append(str(params[nonPgKey])) 349 finished.append(nonPgKey) 350 valStr+=' '.join(vals)+')' 351 i.addPostGIS(pgName,valStr) 352 else: 353 for key in params: 354 if type(params[key])==Decimal: i.add(key,float(params[key])) 355 else: i.add(key,params[key]) 356 357 if None != extraParams: 358 for key in extraParams: 359 i.add(key,extraParams[key]) 360 361 return i
362 363 364 ###################################################################### 365 # UNIT TESTING 366 ###################################################################### 367 import unittest
368 -def testParams():
369 '''Return a params file base on the testvalue tags. 370 @rtype: dict 371 @return: params based on testvalue tags 372 ''' 373 params = {} 374 params['MessageID'] = 14 375 params['RepeatIndicator'] = 1 376 params['UserID'] = 1193046 377 params['Spare2'] = 0 378 379 return params
380
381 -class Testsrbm(unittest.TestCase):
382 '''Use testvalue tag text from each type to build test case the srbm message'''
383 - def testEncodeDecode(self):
384 385 params = testParams() 386 bits = encode(params) 387 r = decode(bits) 388 389 # Check that each parameter came through ok. 390 self.failUnlessEqual(r['MessageID'],params['MessageID']) 391 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator']) 392 self.failUnlessEqual(r['UserID'],params['UserID']) 393 self.failUnlessEqual(r['Spare2'],params['Spare2'])
394
395 -def addMsgOptions(parser):
396 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 397 help='decode a "srbm" AIS message') 398 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 399 help='encode a "srbm" AIS message') 400 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int' 401 ,help='Field parameter value [default: %default]') 402 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int' 403 ,help='Field parameter value [default: %default]')
404 405 ############################################################ 406 if __name__=='__main__': 407 408 from optparse import OptionParser 409 parser = OptionParser(usage="%prog [options]", 410 version="%prog "+__version__) 411 412 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 413 help='run the documentation tests') 414 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 415 help='run the unit tests') 416 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 417 help='Make the test output verbose') 418 419 # FIX: remove nmea from binary messages. No way to build the whole packet? 420 # FIX: or build the surrounding msg 8 for a broadcast? 421 typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message? 422 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 423 ,default='nmeapayload' 424 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]') 425 426 427 outputChoices = ('std','html','csv','sql' ) 428 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 429 ,default='std' 430 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 431 432 parser.add_option('-o','--output',dest='outputFileName',default=None, 433 help='Name of the python file to write [default: stdout]') 434 435 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append', 436 choices=fieldList, 437 help='Which fields to include in the output. Currently only for csv output [default: all]') 438 439 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true', 440 help='Print the field name for csv') 441 442 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true', 443 help='Print out an sql create command for the table.') 444 445 dbChoices = ('sqlite','postgres') 446 parser.add_option('-D','--db-type',dest='dbType',default='postgres' 447 ,choices=dbChoices,type='choice' 448 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]') 449 450 addMsgOptions(parser) 451 452 (options,args) = parser.parse_args() 453 success=True 454 455 if options.doctest: 456 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 457 sys.argv= [sys.argv[0]] 458 if options.verbose: sys.argv.append('-v') 459 import doctest 460 numfail,numtests=doctest.testmod() 461 if numfail==0: print 'ok' 462 else: 463 print 'FAILED' 464 success=False 465 466 if not success: sys.exit('Something Failed') 467 del success # Hide success from epydoc 468 469 if options.unittest: 470 sys.argv = [sys.argv[0]] 471 if options.verbose: sys.argv.append('-v') 472 unittest.main() 473 474 outfile = sys.stdout 475 if None!=options.outputFileName: 476 outfile = file(options.outputFileName,'w') 477 478 479 if options.doEncode: 480 # First make sure all non required options are specified 481 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField") 482 if None==options.UserIDField: parser.error("missing value for UserIDField") 483 msgDict={ 484 'MessageID': '14', 485 'RepeatIndicator': options.RepeatIndicatorField, 486 'UserID': options.UserIDField, 487 'Spare2': '0', 488 } 489 490 bits = encode(msgDict) 491 if 'binary'==options.ioType: print str(bits) 492 elif 'nmeapayload'==options.ioType: 493 # FIX: figure out if this might be necessary at compile time 494 print "bitLen",len(bits) 495 bitLen=len(bits) 496 if bitLen%6!=0: 497 bits = bits + BitVector(size=(6 - (bitLen%6))) # Pad out to multiple of 6 498 print "result:",binary.bitvectoais6(bits)[0] 499 500 501 # FIX: Do not emit this option for the binary message payloads. Does not make sense. 502 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 503 else: sys.exit('ERROR: unknown ioType. Help!') 504 505 506 if options.sqlCreate: 507 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType) 508 509 if options.printCsvfieldList: 510 # Make a csv separated list of fields that will be displayed for csv 511 if None == options.fieldList: options.fieldList = fieldList 512 import StringIO 513 buf = StringIO.StringIO() 514 for field in options.fieldList: 515 buf.write(field+',') 516 result = buf.getvalue() 517 if result[-1] == ',': print result[:-1] 518 else: print result 519 520 if options.doDecode: 521 for msg in args: 522 bv = None 523 524 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'): 525 # Found nmea 526 # FIX: do checksum 527 bv = binary.ais6tobitvec(msg.split(',')[5]) 528 else: # either binary or nmeapayload... expect mostly nmeapayloads 529 # assumes that an all 0 and 1 string can not be a nmeapayload 530 binaryMsg=True 531 for c in msg: 532 if c not in ('0','1'): 533 binaryMsg=False 534 break 535 if binaryMsg: 536 bv = BitVector(bitstring=msg) 537 else: # nmeapayload 538 bv = binary.ais6tobitvec(msg) 539 540 printFields(decode(bv) 541 ,out=outfile 542 ,format=options.outputType 543 ,fieldList=options.fieldList 544 ,dbType=options.dbType 545 ) 546