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

Source Code for Module ais.whalenotice3

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