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-16 $'.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          '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 } #datumEncodeLut 486 487 datumDecodeLut = { 488 '0':'MLLW', 489 '1':'IGLD-85', 490 '2':'WaterDepth', 491 '3':'STND', 492 '4':'MHW', 493 '5':'MSL', 494 '6':'NGVD', 495 '7':'NAVD', 496 '8':'WGS-84', 497 '9':'LAT', 498 } # datumEncodeLut 499 500 sourceEncodeLut = { 501 'No data':'0', 502 'Realtime sensor data':'1', 503 'Raw realtime sensor data - no validation':'2', 504 'Predicted water level - tidal data generated by using harmonic analysis':'3', 505 'Forecast water level - tidal data generated by use of a hydrodynamic model':'4', 506 'Reserved':'5', 507 'Reserved':'6', 508 'Reserved':'7', 509 } #sourceEncodeLut 510 511 sourceDecodeLut = { 512 '0':'No data', 513 '1':'Realtime sensor data', 514 '2':'Raw realtime sensor data - no validation', 515 '3':'Predicted water level - tidal data generated by using harmonic analysis', 516 '4':'Forecast water level - tidal data generated by use of a hydrodynamic model', 517 '5':'Reserved', 518 '6':'Reserved', 519 '7':'Reserved', 520 } # sourceEncodeLut 521 522 ###################################################################### 523 # SQL SUPPORT 524 ###################################################################### 525 526 dbTableName='waterlevel' 527 'Database table name' 528
529 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None 530 ,addCoastGuardFields=True 531 ,dbType='postgres' 532 ):
533 ''' 534 Return the SQL CREATE command for this message type 535 @param outfile: file like object to print to. 536 @param fields: which fields to put in the create. Defaults to all. 537 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 538 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 539 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 540 @type addCoastGuardFields: bool 541 @return: sql create string 542 @rtype: str 543 544 @see: sqlCreate 545 ''' 546 # FIX: should this sqlCreate be the same as in LaTeX (createFuncName) rather than hard coded? 547 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
548
549 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
550 ''' 551 Return the sqlhelp object to create the table. 552 553 @param fields: which fields to put in the create. Defaults to all. 554 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 555 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 556 @type addCoastGuardFields: bool 557 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 558 @return: An object that can be used to generate a return 559 @rtype: sqlhelp.create 560 ''' 561 if None == fields: fields = fieldList 562 import sqlhelp 563 c = sqlhelp.create('waterlevel',dbType=dbType) 564 c.addPrimaryKey() 565 if 'MessageID' in fields: c.addInt ('MessageID') 566 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator') 567 if 'UserID' in fields: c.addInt ('UserID') 568 if 'Spare' in fields: c.addInt ('Spare') 569 if 'dac' in fields: c.addInt ('dac') 570 if 'fid' in fields: c.addInt ('fid') 571 if 'month' in fields: c.addInt ('month') 572 if 'day' in fields: c.addInt ('day') 573 if 'hour' in fields: c.addInt ('hour') 574 if 'min' in fields: c.addInt ('min') 575 if 'stationid' in fields: c.addVarChar('stationid',7) 576 if 'waterlevel' in fields: c.addInt ('waterlevel') 577 if 'datum' in fields: c.addInt ('datum') 578 if 'sigma' in fields: c.addInt ('sigma') 579 if 'source' in fields: c.addInt ('source') 580 581 if addCoastGuardFields: 582 # c.addInt('cg_rssi') # Relative signal strength indicator 583 # c.addInt('cg_d') # dBm receive strength 584 # c.addInt('cg_T') # Receive timestamp from the AIS equipment 585 # c.addInt('cg_S') # Slot received in 586 # c.addVarChar('cg_x',10) # Idonno 587 c.addVarChar('cg_r',15) # Receiver station ID - should usually be an MMSI, but sometimes is a string 588 c.addInt('cg_sec') # UTC seconds since the epoch 589 590 c.addTimestamp('cg_timestamp') # UTC decoded cg_sec - not actually in the data stream 591 592 return c
593
594 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
595 ''' 596 Return the SQL INSERT command for this message type 597 @param params: dictionary of values keyed by field name 598 @param outfile: file like object to print to. 599 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields 600 @return: sql create string 601 @rtype: str 602 603 @see: sqlCreate 604 ''' 605 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
606 607
608 -def sqlInsert(params,extraParams=None,dbType='postgres'):
609 ''' 610 Give the SQL INSERT statement 611 @param params: dict keyed by field name of values 612 @param extraParams: any extra fields that you have created beyond the normal ais message fields 613 @rtype: sqlhelp.insert 614 @return: insert class instance 615 @todo: allow optional type checking of params? 616 @warning: this will take invalid keys happily and do what??? 617 ''' 618 import sqlhelp 619 i = sqlhelp.insert('waterlevel',dbType=dbType) 620 621 if dbType=='postgres': 622 finished = [] 623 for key in params: 624 if key in finished: 625 continue 626 627 if key not in toPgFields and key not in fromPgFields: 628 if type(params[key])==Decimal: i.add(key,float(params[key])) 629 else: i.add(key,params[key]) 630 else: 631 if key in fromPgFields: 632 val = params[key] 633 # Had better be a WKT type like POINT(-88.1 30.321) 634 i.addPostGIS(key,val) 635 finished.append(key) 636 else: 637 # Need to construct the type. 638 pgName = toPgFields[key] 639 #valStr='GeomFromText(\''+pgTypes[pgName]+'(' 640 valStr=pgTypes[pgName]+'(' 641 vals = [] 642 for nonPgKey in fromPgFields[pgName]: 643 vals.append(str(params[nonPgKey])) 644 finished.append(nonPgKey) 645 valStr+=' '.join(vals)+')' 646 i.addPostGIS(pgName,valStr) 647 else: 648 for key in params: 649 if type(params[key])==Decimal: i.add(key,float(params[key])) 650 else: i.add(key,params[key]) 651 652 if None != extraParams: 653 for key in extraParams: 654 i.add(key,extraParams[key]) 655 656 return i
657 658 ###################################################################### 659 # LATEX SUPPORT 660 ###################################################################### 661
662 -def latexDefinitionTable(outfile=sys.stdout 663 ):
664 ''' 665 Return the LaTeX definition table for this message type 666 @param outfile: file like object to print to. 667 @type outfile: file obj 668 @return: LaTeX table string via the outfile 669 @rtype: str 670 671 ''' 672 o = outfile 673 674 o.write(''' 675 \\begin{table}%[htb] 676 \\centering 677 \\begin{tabular}{|l|c|l|} 678 \\hline 679 Parameter & Number of bits & Description 680 \\\\ \\hline\\hline 681 MessageID & 6 & AIS message number. Must be 8 \\\\ \hline 682 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline 683 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline 684 Spare & 2 & Reserved for definition by a regional authority. \\\\ \hline 685 dac & 10 & Designated Area Code \\\\ \hline 686 fid & 6 & Functional Identifier \\\\ \hline 687 month & 4 & Time the measurement represents month 1..12 \\\\ \hline 688 day & 5 & Time the measurement represents day of the month 1..31 \\\\ \hline 689 hour & 5 & Time the measurement represents UTC hours 0..23 \\\\ \hline 690 min & 6 & Time the measurement represents minutes \\\\ \hline 691 stationid & 42 & Character identifier of the station. Usually a number. \\\\ \hline 692 waterlevel & 16 & Water level in centimeters \\\\ \hline 693 datum & 5 & What reference datum applies to the value \\\\ \hline 694 sigma & 7 & Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? \\\\ \hline 695 source & 3 & How the water level was derived\\\\ \\hline \\hline 696 Total bits & 149 & Appears to take 1 slot with 19 pad bits to fill the last slot \\\\ \\hline 697 \\end{tabular} 698 \\caption{AIS message number 8: Water level report. This is } 699 \\label{tab:waterlevel} 700 \\end{table} 701 ''')
702 703 ###################################################################### 704 # Text Definition 705 ###################################################################### 706
707 -def textDefinitionTable(outfile=sys.stdout 708 ,delim='\t' 709 ):
710 ''' 711 Return the text definition table for this message type 712 @param outfile: file like object to print to. 713 @type outfile: file obj 714 @return: text table string via the outfile 715 @rtype: str 716 717 ''' 718 o = outfile 719 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description 720 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 8 721 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated 722 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI) 723 Spare'''+delim+'''2'''+delim+'''Reserved for definition by a regional authority. 724 dac'''+delim+'''10'''+delim+'''Designated Area Code 725 fid'''+delim+'''6'''+delim+'''Functional Identifier 726 month'''+delim+'''4'''+delim+'''Time the measurement represents month 1..12 727 day'''+delim+'''5'''+delim+'''Time the measurement represents day of the month 1..31 728 hour'''+delim+'''5'''+delim+'''Time the measurement represents UTC hours 0..23 729 min'''+delim+'''6'''+delim+'''Time the measurement represents minutes 730 stationid'''+delim+'''42'''+delim+'''Character identifier of the station. Usually a number. 731 waterlevel'''+delim+'''16'''+delim+'''Water level in centimeters 732 datum'''+delim+'''5'''+delim+'''What reference datum applies to the value 733 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? 734 source'''+delim+'''3'''+delim+'''How the water level was derived 735 Total bits'''+delim+'''149'''+delim+'''Appears to take 1 slot with 19 pad bits to fill the last slot''')
736 737 738 ###################################################################### 739 # UNIT TESTING 740 ###################################################################### 741 import unittest
742 -def testParams():
743 '''Return a params file base on the testvalue tags. 744 @rtype: dict 745 @return: params based on testvalue tags 746 ''' 747 params = {} 748 params['MessageID'] = 8 749 params['RepeatIndicator'] = 1 750 params['UserID'] = 1193046 751 params['Spare'] = 0 752 params['dac'] = 366 753 params['fid'] = 63 754 params['month'] = 2 755 params['day'] = 28 756 params['hour'] = 23 757 params['min'] = 45 758 params['stationid'] = 'A234567' 759 params['waterlevel'] = -97 760 params['datum'] = 0 761 params['sigma'] = 97 762 params['source'] = 2 763 764 return params
765
766 -class Testwaterlevel(unittest.TestCase):
767 '''Use testvalue tag text from each type to build test case the waterlevel message'''
768 - def testEncodeDecode(self):
769 770 params = testParams() 771 bits = encode(params) 772 r = decode(bits) 773 774 # Check that each parameter came through ok. 775 self.failUnlessEqual(r['MessageID'],params['MessageID']) 776 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator']) 777 self.failUnlessEqual(r['UserID'],params['UserID']) 778 self.failUnlessEqual(r['Spare'],params['Spare']) 779 self.failUnlessEqual(r['dac'],params['dac']) 780 self.failUnlessEqual(r['fid'],params['fid']) 781 self.failUnlessEqual(r['month'],params['month']) 782 self.failUnlessEqual(r['day'],params['day']) 783 self.failUnlessEqual(r['hour'],params['hour']) 784 self.failUnlessEqual(r['min'],params['min']) 785 self.failUnlessEqual(r['stationid'],params['stationid']) 786 self.failUnlessEqual(r['waterlevel'],params['waterlevel']) 787 self.failUnlessEqual(r['datum'],params['datum']) 788 self.failUnlessEqual(r['sigma'],params['sigma']) 789 self.failUnlessEqual(r['source'],params['source'])
790
791 -def addMsgOptions(parser):
792 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 793 help='decode a "waterlevel" AIS message') 794 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 795 help='encode a "waterlevel" AIS message') 796 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int' 797 ,help='Field parameter value [default: %default]') 798 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int' 799 ,help='Field parameter value [default: %default]') 800 parser.add_option('--month-field', dest='monthField',metavar='uint',type='int' 801 ,help='Field parameter value [default: %default]') 802 parser.add_option('--day-field', dest='dayField',metavar='uint',type='int' 803 ,help='Field parameter value [default: %default]') 804 parser.add_option('--hour-field', dest='hourField',metavar='uint',type='int' 805 ,help='Field parameter value [default: %default]') 806 parser.add_option('--min-field', dest='minField',metavar='uint',type='int' 807 ,help='Field parameter value [default: %default]') 808 parser.add_option('--stationid-field', dest='stationidField',default='@@@@@@@',metavar='aisstr6',type='string' 809 ,help='Field parameter value [default: %default]') 810 parser.add_option('--waterlevel-field', dest='waterlevelField',default=-32768,metavar='int',type='int' 811 ,help='Field parameter value [default: %default]') 812 parser.add_option('--datum-field', dest='datumField',default=31,metavar='uint',type='int' 813 ,help='Field parameter value [default: %default]') 814 parser.add_option('--sigma-field', dest='sigmaField',default=127,metavar='uint',type='int' 815 ,help='Field parameter value [default: %default]') 816 parser.add_option('--source-field', dest='sourceField',default=0,metavar='uint',type='int' 817 ,help='Field parameter value [default: %default]')
818 819 ############################################################ 820 if __name__=='__main__': 821 822 from optparse import OptionParser 823 parser = OptionParser(usage="%prog [options]", 824 version="%prog "+__version__) 825 826 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 827 help='run the documentation tests') 828 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 829 help='run the unit tests') 830 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 831 help='Make the test output verbose') 832 833 # FIX: remove nmea from binary messages. No way to build the whole packet? 834 # FIX: or build the surrounding msg 8 for a broadcast? 835 typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message? 836 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 837 ,default='nmeapayload' 838 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]') 839 840 841 outputChoices = ('std','html','csv','sql' ) 842 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 843 ,default='std' 844 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 845 846 parser.add_option('-o','--output',dest='outputFileName',default=None, 847 help='Name of the python file to write [default: stdout]') 848 849 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append', 850 choices=fieldList, 851 help='Which fields to include in the output. Currently only for csv output [default: all]') 852 853 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true', 854 help='Print the field name for csv') 855 856 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true', 857 help='Print out an sql create command for the table.') 858 859 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true', 860 help='Print a LaTeX table of the type') 861 862 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true', 863 help='Print delimited table of the type (for Word table importing)') 864 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t' 865 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)') 866 867 868 dbChoices = ('sqlite','postgres') 869 parser.add_option('-D','--db-type',dest='dbType',default='postgres' 870 ,choices=dbChoices,type='choice' 871 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]') 872 873 addMsgOptions(parser) 874 875 (options,args) = parser.parse_args() 876 success=True 877 878 if options.doctest: 879 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 880 sys.argv= [sys.argv[0]] 881 if options.verbose: sys.argv.append('-v') 882 import doctest 883 numfail,numtests=doctest.testmod() 884 if numfail==0: print 'ok' 885 else: 886 print 'FAILED' 887 success=False 888 889 if not success: sys.exit('Something Failed') 890 del success # Hide success from epydoc 891 892 if options.unittest: 893 sys.argv = [sys.argv[0]] 894 if options.verbose: sys.argv.append('-v') 895 unittest.main() 896 897 outfile = sys.stdout 898 if None!=options.outputFileName: 899 outfile = file(options.outputFileName,'w') 900 901 902 if options.doEncode: 903 # First make sure all non required options are specified 904 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField") 905 if None==options.UserIDField: parser.error("missing value for UserIDField") 906 if None==options.monthField: parser.error("missing value for monthField") 907 if None==options.dayField: parser.error("missing value for dayField") 908 if None==options.hourField: parser.error("missing value for hourField") 909 if None==options.minField: parser.error("missing value for minField") 910 if None==options.stationidField: parser.error("missing value for stationidField") 911 if None==options.waterlevelField: parser.error("missing value for waterlevelField") 912 if None==options.datumField: parser.error("missing value for datumField") 913 if None==options.sigmaField: parser.error("missing value for sigmaField") 914 if None==options.sourceField: parser.error("missing value for sourceField") 915 msgDict={ 916 'MessageID': '8', 917 'RepeatIndicator': options.RepeatIndicatorField, 918 'UserID': options.UserIDField, 919 'Spare': '0', 920 'dac': '366', 921 'fid': '63', 922 'month': options.monthField, 923 'day': options.dayField, 924 'hour': options.hourField, 925 'min': options.minField, 926 'stationid': options.stationidField, 927 'waterlevel': options.waterlevelField, 928 'datum': options.datumField, 929 'sigma': options.sigmaField, 930 'source': options.sourceField, 931 } 932 933 bits = encode(msgDict) 934 if 'binary'==options.ioType: print str(bits) 935 elif 'nmeapayload'==options.ioType: 936 # FIX: figure out if this might be necessary at compile time 937 print "bitLen",len(bits) 938 bitLen=len(bits) 939 if bitLen%6!=0: 940 bits = bits + BitVector(size=(6 - (bitLen%6))) # Pad out to multiple of 6 941 print "result:",binary.bitvectoais6(bits)[0] 942 943 944 # FIX: Do not emit this option for the binary message payloads. Does not make sense. 945 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 946 else: sys.exit('ERROR: unknown ioType. Help!') 947 948 949 if options.sqlCreate: 950 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType) 951 952 if options.latexDefinitionTable: 953 latexDefinitionTable(outfile) 954 955 # For conversion to word tables 956 if options.textDefinitionTable: 957 textDefinitionTable(outfile,options.delimTextDefinitionTable) 958 959 if options.printCsvfieldList: 960 # Make a csv separated list of fields that will be displayed for csv 961 if None == options.fieldList: options.fieldList = fieldList 962 import StringIO 963 buf = StringIO.StringIO() 964 for field in options.fieldList: 965 buf.write(field+',') 966 result = buf.getvalue() 967 if result[-1] == ',': print result[:-1] 968 else: print result 969 970 if options.doDecode: 971 if len(args)==0: args = sys.stdin 972 for msg in args: 973 bv = None 974 975 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'): 976 # Found nmea 977 # FIX: do checksum 978 bv = binary.ais6tobitvec(msg.split(',')[5]) 979 else: # either binary or nmeapayload... expect mostly nmeapayloads 980 # assumes that an all 0 and 1 string can not be a nmeapayload 981 binaryMsg=True 982 for c in msg: 983 if c not in ('0','1'): 984 binaryMsg=False 985 break 986 if binaryMsg: 987 bv = BitVector(bitstring=msg) 988 else: # nmeapayload 989 bv = binary.ais6tobitvec(msg) 990 991 printFields(decode(bv) 992 ,out=outfile 993 ,format=options.outputType 994 ,fieldList=options.fieldList 995 ,dbType=options.dbType 996 ) 997