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

Source Code for Module ais.waterlevel2

   1  #!/usr/bin/env python 
   2   
   3  __version__ = '$Revision: 4791 $'.split()[1] 
   4  __date__ = '$Date: 2008-01-31 $'.split()[1] 
   5  __author__ = 'xmlbinmsg' 
   6   
   7  __doc__=''' 
   8   
   9  Autogenerated python functions to serialize/deserialize binary messages. 
  10   
  11  Generated by: ../scripts/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          'Spare', 
  54          'dac', 
  55          'fid', 
  56          'month', 
  57          'day', 
  58          'hour', 
  59          'min', 
  60          'stationid', 
  61          'waterlevel', 
  62          'datum', 
  63          'sigma', 
  64          'source', 
  65  ) 
  66   
  67  fieldListPostgres = ( 
  68          'MessageID', 
  69          'RepeatIndicator', 
  70          'UserID', 
  71          'Spare', 
  72          'dac', 
  73          'fid', 
  74          'month', 
  75          'day', 
  76          'hour', 
  77          'min', 
  78          'stationid', 
  79          'waterlevel', 
  80          'datum', 
  81          'sigma', 
  82          'source', 
  83  ) 
  84   
  85  toPgFields = { 
  86  } 
  87  ''' 
  88  Go to the Postgis field names from the straight field name 
  89  ''' 
  90   
  91  fromPgFields = { 
  92  } 
  93  ''' 
  94  Go from the Postgis field names to the straight field name 
  95  ''' 
  96   
  97  pgTypes = { 
  98  } 
  99  ''' 
 100  Lookup table for each postgis field name to get its type. 
 101  ''' 
 102   
103 -def encode(params, validate=False):
104 '''Create a waterlevel binary message payload to pack into an AIS Msg waterlevel. 105 106 Fields in params: 107 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8") 108 - RepeatIndicator(uint): Indicated how many times a message has been repeated 109 - UserID(uint): Unique ship identification number (MMSI) 110 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0") 111 - dac(uint): Designated Area Code (field automatically set to "366") 112 - fid(uint): Functional Identifier (field automatically set to "63") 113 - month(uint): Time the measurement represents month 1..12 114 - day(uint): Time the measurement represents day of the month 1..31 115 - hour(uint): Time the measurement represents UTC hours 0..23 116 - min(uint): Time the measurement represents minutes 117 - stationid(aisstr6): Character identifier of the station. Usually a number. 118 - waterlevel(int): Water level in centimeters 119 - datum(uint): What reference datum applies to the value 120 - sigma(uint): Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? 121 - source(uint): How the water level was derived 122 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing 123 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented. 124 @rtype: BitVector 125 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8 126 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits. 127 ''' 128 129 bvList = [] 130 bvList.append(binary.setBitVectorSize(BitVector(intVal=8),6)) 131 if 'RepeatIndicator' in params: 132 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2)) 133 else: 134 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2)) 135 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30)) 136 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2)) 137 bvList.append(binary.setBitVectorSize(BitVector(intVal=366),10)) 138 bvList.append(binary.setBitVectorSize(BitVector(intVal=63),6)) 139 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['month']),4)) 140 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['day']),5)) 141 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['hour']),5)) 142 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['min']),6)) 143 if 'stationid' in params: 144 bvList.append(aisstring.encode(params['stationid'],42)) 145 else: 146 bvList.append(aisstring.encode('@@@@@@@',42)) 147 if 'waterlevel' in params: 148 bvList.append(binary.bvFromSignedInt(params['waterlevel'],16)) 149 else: 150 bvList.append(binary.bvFromSignedInt(-32768,16)) 151 if 'datum' in params: 152 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['datum']),5)) 153 else: 154 bvList.append(binary.setBitVectorSize(BitVector(intVal=31),5)) 155 if 'sigma' in params: 156 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['sigma']),7)) 157 else: 158 bvList.append(binary.setBitVectorSize(BitVector(intVal=127),7)) 159 if 'source' in params: 160 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['source']),3)) 161 else: 162 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),3)) 163 164 return binary.joinBV(bvList)
165
166 -def decode(bv, validate=False):
167 '''Unpack a waterlevel message 168 169 Fields in params: 170 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8") 171 - RepeatIndicator(uint): Indicated how many times a message has been repeated 172 - UserID(uint): Unique ship identification number (MMSI) 173 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0") 174 - dac(uint): Designated Area Code (field automatically set to "366") 175 - fid(uint): Functional Identifier (field automatically set to "63") 176 - month(uint): Time the measurement represents month 1..12 177 - day(uint): Time the measurement represents day of the month 1..31 178 - hour(uint): Time the measurement represents UTC hours 0..23 179 - min(uint): Time the measurement represents minutes 180 - stationid(aisstr6): Character identifier of the station. Usually a number. 181 - waterlevel(int): Water level in centimeters 182 - datum(uint): What reference datum applies to the value 183 - sigma(uint): Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? 184 - source(uint): How the water level was derived 185 @type bv: BitVector 186 @param bv: Bits defining a message 187 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented. 188 @rtype: dict 189 @return: params 190 ''' 191 192 #Would be nice to check the bit count here.. 193 #if validate: 194 # assert (len(bv)==FIX: SOME NUMBER) 195 r = {} 196 r['MessageID']=8 197 r['RepeatIndicator']=int(bv[6:8]) 198 r['UserID']=int(bv[8:38]) 199 r['Spare']=0 200 r['dac']=366 201 r['fid']=63 202 r['month']=int(bv[56:60]) 203 r['day']=int(bv[60:65]) 204 r['hour']=int(bv[65:70]) 205 r['min']=int(bv[70:76]) 206 r['stationid']=aisstring.decode(bv[76:118]) 207 r['waterlevel']=binary.signedIntFromBV(bv[118:134]) 208 r['datum']=int(bv[134:139]) 209 r['sigma']=int(bv[139:146]) 210 r['source']=int(bv[146:149]) 211 return r
212
213 -def decodeMessageID(bv, validate=False):
214 return 8
215
216 -def decodeRepeatIndicator(bv, validate=False):
217 return int(bv[6:8])
218
219 -def decodeUserID(bv, validate=False):
220 return int(bv[8:38])
221
222 -def decodeSpare(bv, validate=False):
223 return 0
224
225 -def decodedac(bv, validate=False):
226 return 366
227
228 -def decodefid(bv, validate=False):
229 return 63
230
231 -def decodemonth(bv, validate=False):
232 return int(bv[56:60])
233
234 -def decodeday(bv, validate=False):
235 return int(bv[60:65])
236
237 -def decodehour(bv, validate=False):
238 return int(bv[65:70])
239
240 -def decodemin(bv, validate=False):
241 return int(bv[70:76])
242
243 -def decodestationid(bv, validate=False):
244 return aisstring.decode(bv[76:118])
245
246 -def decodewaterlevel(bv, validate=False):
247 return binary.signedIntFromBV(bv[118:134])
248
249 -def decodedatum(bv, validate=False):
250 return int(bv[134:139])
251
252 -def decodesigma(bv, validate=False):
253 return int(bv[139:146])
254
255 -def decodesource(bv, validate=False):
256 return int(bv[146:149])
257 258
259 -def printHtml(params, out=sys.stdout):
260 out.write("<h3>waterlevel</h3>\n") 261 out.write("<table border=\"1\">\n") 262 out.write("<tr bgcolor=\"orange\">\n") 263 out.write("<th align=\"left\">Field Name</th>\n") 264 out.write("<th align=\"left\">Type</th>\n") 265 out.write("<th align=\"left\">Value</th>\n") 266 out.write("<th align=\"left\">Value in Lookup Table</th>\n") 267 out.write("<th align=\"left\">Units</th>\n") 268 out.write("\n") 269 out.write("<tr>\n") 270 out.write("<td>MessageID</td>\n") 271 out.write("<td>uint</td>\n") 272 if 'MessageID' in params: 273 out.write(" <td>"+str(params['MessageID'])+"</td>\n") 274 out.write(" <td>"+str(params['MessageID'])+"</td>\n") 275 out.write("</tr>\n") 276 out.write("\n") 277 out.write("<tr>\n") 278 out.write("<td>RepeatIndicator</td>\n") 279 out.write("<td>uint</td>\n") 280 if 'RepeatIndicator' in params: 281 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n") 282 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut: 283 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>") 284 else: 285 out.write("<td><i>Missing LUT entry</i></td>") 286 out.write("</tr>\n") 287 out.write("\n") 288 out.write("<tr>\n") 289 out.write("<td>UserID</td>\n") 290 out.write("<td>uint</td>\n") 291 if 'UserID' in params: 292 out.write(" <td>"+str(params['UserID'])+"</td>\n") 293 out.write(" <td>"+str(params['UserID'])+"</td>\n") 294 out.write("</tr>\n") 295 out.write("\n") 296 out.write("<tr>\n") 297 out.write("<td>Spare</td>\n") 298 out.write("<td>uint</td>\n") 299 if 'Spare' in params: 300 out.write(" <td>"+str(params['Spare'])+"</td>\n") 301 out.write(" <td>"+str(params['Spare'])+"</td>\n") 302 out.write("</tr>\n") 303 out.write("\n") 304 out.write("<tr>\n") 305 out.write("<td>dac</td>\n") 306 out.write("<td>uint</td>\n") 307 if 'dac' in params: 308 out.write(" <td>"+str(params['dac'])+"</td>\n") 309 out.write(" <td>"+str(params['dac'])+"</td>\n") 310 out.write("</tr>\n") 311 out.write("\n") 312 out.write("<tr>\n") 313 out.write("<td>fid</td>\n") 314 out.write("<td>uint</td>\n") 315 if 'fid' in params: 316 out.write(" <td>"+str(params['fid'])+"</td>\n") 317 out.write(" <td>"+str(params['fid'])+"</td>\n") 318 out.write("</tr>\n") 319 out.write("\n") 320 out.write("<tr>\n") 321 out.write("<td>month</td>\n") 322 out.write("<td>uint</td>\n") 323 if 'month' in params: 324 out.write(" <td>"+str(params['month'])+"</td>\n") 325 out.write(" <td>"+str(params['month'])+"</td>\n") 326 out.write("</tr>\n") 327 out.write("\n") 328 out.write("<tr>\n") 329 out.write("<td>day</td>\n") 330 out.write("<td>uint</td>\n") 331 if 'day' in params: 332 out.write(" <td>"+str(params['day'])+"</td>\n") 333 out.write(" <td>"+str(params['day'])+"</td>\n") 334 out.write("</tr>\n") 335 out.write("\n") 336 out.write("<tr>\n") 337 out.write("<td>hour</td>\n") 338 out.write("<td>uint</td>\n") 339 if 'hour' in params: 340 out.write(" <td>"+str(params['hour'])+"</td>\n") 341 out.write(" <td>"+str(params['hour'])+"</td>\n") 342 out.write("</tr>\n") 343 out.write("\n") 344 out.write("<tr>\n") 345 out.write("<td>min</td>\n") 346 out.write("<td>uint</td>\n") 347 if 'min' in params: 348 out.write(" <td>"+str(params['min'])+"</td>\n") 349 out.write(" <td>"+str(params['min'])+"</td>\n") 350 out.write("</tr>\n") 351 out.write("\n") 352 out.write("<tr>\n") 353 out.write("<td>stationid</td>\n") 354 out.write("<td>aisstr6</td>\n") 355 if 'stationid' in params: 356 out.write(" <td>"+str(params['stationid'])+"</td>\n") 357 out.write(" <td>"+str(params['stationid'])+"</td>\n") 358 out.write("</tr>\n") 359 out.write("\n") 360 out.write("<tr>\n") 361 out.write("<td>waterlevel</td>\n") 362 out.write("<td>int</td>\n") 363 if 'waterlevel' in params: 364 out.write(" <td>"+str(params['waterlevel'])+"</td>\n") 365 out.write(" <td>"+str(params['waterlevel'])+"</td>\n") 366 out.write("<td>cm</td>\n") 367 out.write("</tr>\n") 368 out.write("\n") 369 out.write("<tr>\n") 370 out.write("<td>datum</td>\n") 371 out.write("<td>uint</td>\n") 372 if 'datum' in params: 373 out.write(" <td>"+str(params['datum'])+"</td>\n") 374 if str(params['datum']) in datumDecodeLut: 375 out.write("<td>"+datumDecodeLut[str(params['datum'])]+"</td>") 376 else: 377 out.write("<td><i>Missing LUT entry</i></td>") 378 out.write("</tr>\n") 379 out.write("\n") 380 out.write("<tr>\n") 381 out.write("<td>sigma</td>\n") 382 out.write("<td>uint</td>\n") 383 if 'sigma' in params: 384 out.write(" <td>"+str(params['sigma'])+"</td>\n") 385 out.write(" <td>"+str(params['sigma'])+"</td>\n") 386 out.write("<td>cm</td>\n") 387 out.write("</tr>\n") 388 out.write("\n") 389 out.write("<tr>\n") 390 out.write("<td>source</td>\n") 391 out.write("<td>uint</td>\n") 392 if 'source' in params: 393 out.write(" <td>"+str(params['source'])+"</td>\n") 394 if str(params['source']) in sourceDecodeLut: 395 out.write("<td>"+sourceDecodeLut[str(params['source'])]+"</td>") 396 else: 397 out.write("<td><i>Missing LUT entry</i></td>") 398 out.write("</tr>\n") 399 out.write("</table>\n")
400
401 -def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
402 '''Print a waterlevel message to stdout. 403 404 Fields in params: 405 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8") 406 - RepeatIndicator(uint): Indicated how many times a message has been repeated 407 - UserID(uint): Unique ship identification number (MMSI) 408 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0") 409 - dac(uint): Designated Area Code (field automatically set to "366") 410 - fid(uint): Functional Identifier (field automatically set to "63") 411 - month(uint): Time the measurement represents month 1..12 412 - day(uint): Time the measurement represents day of the month 1..31 413 - hour(uint): Time the measurement represents UTC hours 0..23 414 - min(uint): Time the measurement represents minutes 415 - stationid(aisstr6): Character identifier of the station. Usually a number. 416 - waterlevel(int): Water level in centimeters 417 - datum(uint): What reference datum applies to the value 418 - sigma(uint): Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? 419 - source(uint): How the water level was derived 420 @param params: Dictionary of field names/values. 421 @param out: File like object to write to 422 @rtype: stdout 423 @return: text to out 424 ''' 425 426 if 'std'==format: 427 out.write("waterlevel:\n") 428 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n") 429 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n") 430 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n") 431 if 'Spare' in params: out.write(" Spare: "+str(params['Spare'])+"\n") 432 if 'dac' in params: out.write(" dac: "+str(params['dac'])+"\n") 433 if 'fid' in params: out.write(" fid: "+str(params['fid'])+"\n") 434 if 'month' in params: out.write(" month: "+str(params['month'])+"\n") 435 if 'day' in params: out.write(" day: "+str(params['day'])+"\n") 436 if 'hour' in params: out.write(" hour: "+str(params['hour'])+"\n") 437 if 'min' in params: out.write(" min: "+str(params['min'])+"\n") 438 if 'stationid' in params: out.write(" stationid: "+str(params['stationid'])+"\n") 439 if 'waterlevel' in params: out.write(" waterlevel: "+str(params['waterlevel'])+"\n") 440 if 'datum' in params: out.write(" datum: "+str(params['datum'])+"\n") 441 if 'sigma' in params: out.write(" sigma: "+str(params['sigma'])+"\n") 442 if 'source' in params: out.write(" source: "+str(params['source'])+"\n") 443 elif 'csv'==format: 444 if None == options.fieldList: 445 options.fieldList = fieldList 446 needComma = False; 447 for field in fieldList: 448 if needComma: out.write(',') 449 needComma = True 450 if field in params: 451 out.write(str(params[field])) 452 # else: leave it empty 453 out.write("\n") 454 elif 'html'==format: 455 printHtml(params,out) 456 elif 'sql'==format: 457 sqlInsertStr(params,out,dbType=dbType) 458 else: 459 print "ERROR: unknown format:",format 460 assert False 461 462 return # Nothing to return
463 464 RepeatIndicatorEncodeLut = { 465 'default':'0', 466 'do not repeat any more':'3', 467 } #RepeatIndicatorEncodeLut 468 469 RepeatIndicatorDecodeLut = { 470 '0':'default', 471 '3':'do not repeat any more', 472 } # RepeatIndicatorEncodeLut 473 474 datumEncodeLut = { 475 'MLLW':'0', 476 'IGLD-85':'1', 477 'WaterDepth':'2', 478 'STND':'3', 479 'MHW':'4', 480 'MSL':'5', 481 'NGVD':'6', 482 'NAVD':'7', 483 'WGS-84':'8', 484 'LAT':'9', 485 'Pool':'10', 486 'Gauge':'11', 487 'Unknown/Unavailable':'31', 488 } #datumEncodeLut 489 490 datumDecodeLut = { 491 '0':'MLLW', 492 '1':'IGLD-85', 493 '2':'WaterDepth', 494 '3':'STND', 495 '4':'MHW', 496 '5':'MSL', 497 '6':'NGVD', 498 '7':'NAVD', 499 '8':'WGS-84', 500 '9':'LAT', 501 '10':'Pool', 502 '11':'Gauge', 503 '31':'Unknown/Unavailable', 504 } # datumEncodeLut 505 506 sourceEncodeLut = { 507 'No data':'0', 508 'Realtime sensor data':'1', 509 'Raw realtime sensor data - no validation':'2', 510 'Predicted water level - tidal data generated by using harmonic analysis':'3', 511 'Forecast water level - tidal data generated by use of a hydrodynamic model':'4', 512 'Reserved':'5', 513 'Reserved':'6', 514 'Reserved':'7', 515 } #sourceEncodeLut 516 517 sourceDecodeLut = { 518 '0':'No data', 519 '1':'Realtime sensor data', 520 '2':'Raw realtime sensor data - no validation', 521 '3':'Predicted water level - tidal data generated by using harmonic analysis', 522 '4':'Forecast water level - tidal data generated by use of a hydrodynamic model', 523 '5':'Reserved', 524 '6':'Reserved', 525 '7':'Reserved', 526 } # sourceEncodeLut 527 528 ###################################################################### 529 # SQL SUPPORT 530 ###################################################################### 531 532 dbTableName='waterlevel' 533 'Database table name' 534
535 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None 536 ,addCoastGuardFields=True 537 ,dbType='postgres' 538 ):
539 ''' 540 Return the SQL CREATE command for this message type 541 @param outfile: file like object to print to. 542 @param fields: which fields to put in the create. Defaults to all. 543 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 544 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 545 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 546 @type addCoastGuardFields: bool 547 @return: sql create string 548 @rtype: str 549 550 @see: sqlCreate 551 ''' 552 # FIX: should this sqlCreate be the same as in LaTeX (createFuncName) rather than hard coded? 553 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
554
555 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
556 ''' 557 Return the sqlhelp object to create the table. 558 559 @param fields: which fields to put in the create. Defaults to all. 560 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 561 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 562 @type addCoastGuardFields: bool 563 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 564 @return: An object that can be used to generate a return 565 @rtype: sqlhelp.create 566 ''' 567 if None == fields: fields = fieldList 568 import sqlhelp 569 c = sqlhelp.create('waterlevel',dbType=dbType) 570 c.addPrimaryKey() 571 if 'MessageID' in fields: c.addInt ('MessageID') 572 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator') 573 if 'UserID' in fields: c.addInt ('UserID') 574 if 'Spare' in fields: c.addInt ('Spare') 575 if 'dac' in fields: c.addInt ('dac') 576 if 'fid' in fields: c.addInt ('fid') 577 if 'month' in fields: c.addInt ('month') 578 if 'day' in fields: c.addInt ('day') 579 if 'hour' in fields: c.addInt ('hour') 580 if 'min' in fields: c.addInt ('min') 581 if 'stationid' in fields: c.addVarChar('stationid',7) 582 if 'waterlevel' in fields: c.addInt ('waterlevel') 583 if 'datum' in fields: c.addInt ('datum') 584 if 'sigma' in fields: c.addInt ('sigma') 585 if 'source' in fields: c.addInt ('source') 586 587 if addCoastGuardFields: 588 # c.addInt('cg_rssi') # Relative signal strength indicator 589 # c.addInt('cg_d') # dBm receive strength 590 # c.addInt('cg_T') # Receive timestamp from the AIS equipment 591 # c.addInt('cg_S') # Slot received in 592 # c.addVarChar('cg_x',10) # Idonno 593 c.addVarChar('cg_r',15) # Receiver station ID - should usually be an MMSI, but sometimes is a string 594 c.addInt('cg_sec') # UTC seconds since the epoch 595 596 c.addTimestamp('cg_timestamp') # UTC decoded cg_sec - not actually in the data stream 597 598 return c
599
600 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
601 ''' 602 Return the SQL INSERT command for this message type 603 @param params: dictionary of values keyed by field name 604 @param outfile: file like object to print to. 605 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields 606 @return: sql create string 607 @rtype: str 608 609 @see: sqlCreate 610 ''' 611 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
612 613
614 -def sqlInsert(params,extraParams=None,dbType='postgres'):
615 ''' 616 Give the SQL INSERT statement 617 @param params: dict keyed by field name of values 618 @param extraParams: any extra fields that you have created beyond the normal ais message fields 619 @rtype: sqlhelp.insert 620 @return: insert class instance 621 @todo: allow optional type checking of params? 622 @warning: this will take invalid keys happily and do what??? 623 ''' 624 import sqlhelp 625 i = sqlhelp.insert('waterlevel',dbType=dbType) 626 627 if dbType=='postgres': 628 finished = [] 629 for key in params: 630 if key in finished: 631 continue 632 633 if key not in toPgFields and key not in fromPgFields: 634 if type(params[key])==Decimal: i.add(key,float(params[key])) 635 else: i.add(key,params[key]) 636 else: 637 if key in fromPgFields: 638 val = params[key] 639 # Had better be a WKT type like POINT(-88.1 30.321) 640 i.addPostGIS(key,val) 641 finished.append(key) 642 else: 643 # Need to construct the type. 644 pgName = toPgFields[key] 645 #valStr='GeomFromText(\''+pgTypes[pgName]+'(' 646 valStr=pgTypes[pgName]+'(' 647 vals = [] 648 for nonPgKey in fromPgFields[pgName]: 649 vals.append(str(params[nonPgKey])) 650 finished.append(nonPgKey) 651 valStr+=' '.join(vals)+')' 652 i.addPostGIS(pgName,valStr) 653 else: 654 for key in params: 655 if type(params[key])==Decimal: i.add(key,float(params[key])) 656 else: i.add(key,params[key]) 657 658 if None != extraParams: 659 for key in extraParams: 660 i.add(key,extraParams[key]) 661 662 return i
663 664 ###################################################################### 665 # LATEX SUPPORT 666 ###################################################################### 667
668 -def latexDefinitionTable(outfile=sys.stdout 669 ):
670 ''' 671 Return the LaTeX definition table for this message type 672 @param outfile: file like object to print to. 673 @type outfile: file obj 674 @return: LaTeX table string via the outfile 675 @rtype: str 676 677 ''' 678 o = outfile 679 680 o.write(''' 681 \\begin{table}%[htb] 682 \\centering 683 \\begin{tabular}{|l|c|l|} 684 \\hline 685 Parameter & Number of bits & Description 686 \\\\ \\hline\\hline 687 MessageID & 6 & AIS message number. Must be 8 \\\\ \hline 688 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline 689 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline 690 Spare & 2 & Reserved for definition by a regional authority. \\\\ \hline 691 dac & 10 & Designated Area Code \\\\ \hline 692 fid & 6 & Functional Identifier \\\\ \hline 693 month & 4 & Time the measurement represents month 1..12 \\\\ \hline 694 day & 5 & Time the measurement represents day of the month 1..31 \\\\ \hline 695 hour & 5 & Time the measurement represents UTC hours 0..23 \\\\ \hline 696 min & 6 & Time the measurement represents minutes \\\\ \hline 697 stationid & 42 & Character identifier of the station. Usually a number. \\\\ \hline 698 waterlevel & 16 & Water level in centimeters \\\\ \hline 699 datum & 5 & What reference datum applies to the value \\\\ \hline 700 sigma & 7 & Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? \\\\ \hline 701 source & 3 & How the water level was derived\\\\ \\hline \\hline 702 Total bits & 149 & Appears to take 1 slot with 19 pad bits to fill the last slot \\\\ \\hline 703 \\end{tabular} 704 \\caption{AIS message number 8: Water level report. This is } 705 \\label{tab:waterlevel} 706 \\end{table} 707 ''')
708 709 ###################################################################### 710 # Text Definition 711 ###################################################################### 712
713 -def textDefinitionTable(outfile=sys.stdout 714 ,delim='\t' 715 ):
716 ''' 717 Return the text definition table for this message type 718 @param outfile: file like object to print to. 719 @type outfile: file obj 720 @return: text table string via the outfile 721 @rtype: str 722 723 ''' 724 o = outfile 725 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description 726 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 8 727 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated 728 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI) 729 Spare'''+delim+'''2'''+delim+'''Reserved for definition by a regional authority. 730 dac'''+delim+'''10'''+delim+'''Designated Area Code 731 fid'''+delim+'''6'''+delim+'''Functional Identifier 732 month'''+delim+'''4'''+delim+'''Time the measurement represents month 1..12 733 day'''+delim+'''5'''+delim+'''Time the measurement represents day of the month 1..31 734 hour'''+delim+'''5'''+delim+'''Time the measurement represents UTC hours 0..23 735 min'''+delim+'''6'''+delim+'''Time the measurement represents minutes 736 stationid'''+delim+'''42'''+delim+'''Character identifier of the station. Usually a number. 737 waterlevel'''+delim+'''16'''+delim+'''Water level in centimeters 738 datum'''+delim+'''5'''+delim+'''What reference datum applies to the value 739 sigma'''+delim+'''7'''+delim+'''Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? 740 source'''+delim+'''3'''+delim+'''How the water level was derived 741 Total bits'''+delim+'''149'''+delim+'''Appears to take 1 slot with 19 pad bits to fill the last slot''')
742 743 744 ###################################################################### 745 # UNIT TESTING 746 ###################################################################### 747 import unittest
748 -def testParams():
749 '''Return a params file base on the testvalue tags. 750 @rtype: dict 751 @return: params based on testvalue tags 752 ''' 753 params = {} 754 params['MessageID'] = 8 755 params['RepeatIndicator'] = 1 756 params['UserID'] = 1193046 757 params['Spare'] = 0 758 params['dac'] = 366 759 params['fid'] = 63 760 params['month'] = 2 761 params['day'] = 28 762 params['hour'] = 23 763 params['min'] = 45 764 params['stationid'] = 'A234567' 765 params['waterlevel'] = -97 766 params['datum'] = 0 767 params['sigma'] = 97 768 params['source'] = 2 769 770 return params
771
772 -class Testwaterlevel(unittest.TestCase):
773 '''Use testvalue tag text from each type to build test case the waterlevel message'''
774 - def testEncodeDecode(self):
775 776 params = testParams() 777 bits = encode(params) 778 r = decode(bits) 779 780 # Check that each parameter came through ok. 781 self.failUnlessEqual(r['MessageID'],params['MessageID']) 782 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator']) 783 self.failUnlessEqual(r['UserID'],params['UserID']) 784 self.failUnlessEqual(r['Spare'],params['Spare']) 785 self.failUnlessEqual(r['dac'],params['dac']) 786 self.failUnlessEqual(r['fid'],params['fid']) 787 self.failUnlessEqual(r['month'],params['month']) 788 self.failUnlessEqual(r['day'],params['day']) 789 self.failUnlessEqual(r['hour'],params['hour']) 790 self.failUnlessEqual(r['min'],params['min']) 791 self.failUnlessEqual(r['stationid'],params['stationid']) 792 self.failUnlessEqual(r['waterlevel'],params['waterlevel']) 793 self.failUnlessEqual(r['datum'],params['datum']) 794 self.failUnlessEqual(r['sigma'],params['sigma']) 795 self.failUnlessEqual(r['source'],params['source'])
796
797 -def addMsgOptions(parser):
798 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 799 help='decode a "waterlevel" AIS message') 800 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 801 help='encode a "waterlevel" AIS message') 802 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int' 803 ,help='Field parameter value [default: %default]') 804 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int' 805 ,help='Field parameter value [default: %default]') 806 parser.add_option('--month-field', dest='monthField',metavar='uint',type='int' 807 ,help='Field parameter value [default: %default]') 808 parser.add_option('--day-field', dest='dayField',metavar='uint',type='int' 809 ,help='Field parameter value [default: %default]') 810 parser.add_option('--hour-field', dest='hourField',metavar='uint',type='int' 811 ,help='Field parameter value [default: %default]') 812 parser.add_option('--min-field', dest='minField',metavar='uint',type='int' 813 ,help='Field parameter value [default: %default]') 814 parser.add_option('--stationid-field', dest='stationidField',default='@@@@@@@',metavar='aisstr6',type='string' 815 ,help='Field parameter value [default: %default]') 816 parser.add_option('--waterlevel-field', dest='waterlevelField',default=-32768,metavar='int',type='int' 817 ,help='Field parameter value [default: %default]') 818 parser.add_option('--datum-field', dest='datumField',default=31,metavar='uint',type='int' 819 ,help='Field parameter value [default: %default]') 820 parser.add_option('--sigma-field', dest='sigmaField',default=127,metavar='uint',type='int' 821 ,help='Field parameter value [default: %default]') 822 parser.add_option('--source-field', dest='sourceField',default=0,metavar='uint',type='int' 823 ,help='Field parameter value [default: %default]')
824 825 ############################################################ 826 if __name__=='__main__': 827 828 from optparse import OptionParser 829 parser = OptionParser(usage="%prog [options]", 830 version="%prog "+__version__) 831 832 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 833 help='run the documentation tests') 834 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 835 help='run the unit tests') 836 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 837 help='Make the test output verbose') 838 839 # FIX: remove nmea from binary messages. No way to build the whole packet? 840 # FIX: or build the surrounding msg 8 for a broadcast? 841 typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message? 842 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 843 ,default='nmeapayload' 844 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]') 845 846 847 outputChoices = ('std','html','csv','sql' ) 848 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 849 ,default='std' 850 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 851 852 parser.add_option('-o','--output',dest='outputFileName',default=None, 853 help='Name of the python file to write [default: stdout]') 854 855 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append', 856 choices=fieldList, 857 help='Which fields to include in the output. Currently only for csv output [default: all]') 858 859 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true', 860 help='Print the field name for csv') 861 862 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true', 863 help='Print out an sql create command for the table.') 864 865 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true', 866 help='Print a LaTeX table of the type') 867 868 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true', 869 help='Print delimited table of the type (for Word table importing)') 870 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t' 871 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)') 872 873 874 dbChoices = ('sqlite','postgres') 875 parser.add_option('-D','--db-type',dest='dbType',default='postgres' 876 ,choices=dbChoices,type='choice' 877 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]') 878 879 addMsgOptions(parser) 880 881 (options,args) = parser.parse_args() 882 success=True 883 884 if options.doctest: 885 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 886 sys.argv= [sys.argv[0]] 887 if options.verbose: sys.argv.append('-v') 888 import doctest 889 numfail,numtests=doctest.testmod() 890 if numfail==0: print 'ok' 891 else: 892 print 'FAILED' 893 success=False 894 895 if not success: sys.exit('Something Failed') 896 del success # Hide success from epydoc 897 898 if options.unittest: 899 sys.argv = [sys.argv[0]] 900 if options.verbose: sys.argv.append('-v') 901 unittest.main() 902 903 outfile = sys.stdout 904 if None!=options.outputFileName: 905 outfile = file(options.outputFileName,'w') 906 907 908 if options.doEncode: 909 # First make sure all non required options are specified 910 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField") 911 if None==options.UserIDField: parser.error("missing value for UserIDField") 912 if None==options.monthField: parser.error("missing value for monthField") 913 if None==options.dayField: parser.error("missing value for dayField") 914 if None==options.hourField: parser.error("missing value for hourField") 915 if None==options.minField: parser.error("missing value for minField") 916 if None==options.stationidField: parser.error("missing value for stationidField") 917 if None==options.waterlevelField: parser.error("missing value for waterlevelField") 918 if None==options.datumField: parser.error("missing value for datumField") 919 if None==options.sigmaField: parser.error("missing value for sigmaField") 920 if None==options.sourceField: parser.error("missing value for sourceField") 921 msgDict={ 922 'MessageID': '8', 923 'RepeatIndicator': options.RepeatIndicatorField, 924 'UserID': options.UserIDField, 925 'Spare': '0', 926 'dac': '366', 927 'fid': '63', 928 'month': options.monthField, 929 'day': options.dayField, 930 'hour': options.hourField, 931 'min': options.minField, 932 'stationid': options.stationidField, 933 'waterlevel': options.waterlevelField, 934 'datum': options.datumField, 935 'sigma': options.sigmaField, 936 'source': options.sourceField, 937 } 938 939 bits = encode(msgDict) 940 if 'binary'==options.ioType: print str(bits) 941 elif 'nmeapayload'==options.ioType: 942 # FIX: figure out if this might be necessary at compile time 943 print "bitLen",len(bits) 944 bitLen=len(bits) 945 if bitLen%6!=0: 946 bits = bits + BitVector(size=(6 - (bitLen%6))) # Pad out to multiple of 6 947 print "result:",binary.bitvectoais6(bits)[0] 948 949 950 # FIX: Do not emit this option for the binary message payloads. Does not make sense. 951 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 952 else: sys.exit('ERROR: unknown ioType. Help!') 953 954 955 if options.sqlCreate: 956 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType) 957 958 if options.latexDefinitionTable: 959 latexDefinitionTable(outfile) 960 961 # For conversion to word tables 962 if options.textDefinitionTable: 963 textDefinitionTable(outfile,options.delimTextDefinitionTable) 964 965 if options.printCsvfieldList: 966 # Make a csv separated list of fields that will be displayed for csv 967 if None == options.fieldList: options.fieldList = fieldList 968 import StringIO 969 buf = StringIO.StringIO() 970 for field in options.fieldList: 971 buf.write(field+',') 972 result = buf.getvalue() 973 if result[-1] == ',': print result[:-1] 974 else: print result 975 976 if options.doDecode: 977 if len(args)==0: args = sys.stdin 978 for msg in args: 979 bv = None 980 981 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'): 982 # Found nmea 983 # FIX: do checksum 984 bv = binary.ais6tobitvec(msg.split(',')[5]) 985 else: # either binary or nmeapayload... expect mostly nmeapayloads 986 # assumes that an all 0 and 1 string can not be a nmeapayload 987 binaryMsg=True 988 for c in msg: 989 if c not in ('0','1'): 990 binaryMsg=False 991 break 992 if binaryMsg: 993 bv = BitVector(bitstring=msg) 994 else: # nmeapayload 995 bv = binary.ais6tobitvec(msg) 996 997 printFields(decode(bv) 998 ,out=outfile 999 ,format=options.outputType 1000 ,fieldList=options.fieldList 1001 ,dbType=options.dbType 1002 ) 1003