1   
   2   
   3  __version__ = '$Revision: 4791 $'.split()[1] 
   4  __date__ = '$Date: 2008-01-09 $'.split()[1] 
   5  __author__ = 'xmlbinmsg' 
   6   
   7  __doc__=''' 
   8   
   9  Autogenerated python functions to serialize/deserialize binary messages. 
  10   
  11  Generated by: ./aisxmlbinmsg2py.py 
  12   
  13  Need to then wrap these functions with the outer AIS packet and then 
  14  convert the whole binary blob to a NMEA string.  Those functions are 
  15  not currently provided in this file. 
  16   
  17  serialize: python to ais binary 
  18  deserialize: ais binary to python 
  19   
  20  The generated code uses translators.py, binary.py, and aisstring.py 
  21  which should be packaged with the resulting files. 
  22   
  23   
  24  @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3 
  25  @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>} 
  26   
  27  @author: '''+__author__+''' 
  28  @version: ''' + __version__ +''' 
  29  @var __date__: Date of last svn commit 
  30  @undocumented: __version__ __author__ __doc__ parser 
  31  @status: under development 
  32  @license: Generated code has no license 
  33  @todo: FIX: put in a description of the message here with fields and types. 
  34  ''' 
  35   
  36  import sys 
  37  from decimal import Decimal 
  38  from BitVector import BitVector 
  39   
  40  import binary, aisstring 
  41   
  42   
  43  TrueBV  = BitVector(bitstring="1") 
  44  "Why always rebuild the True bit?  This should speed things up a bunch" 
  45  FalseBV = BitVector(bitstring="0") 
  46  "Why always rebuild the False bit?  This should speed things up a bunch" 
  47   
  48   
  49  fieldList = ( 
  50          'MessageID', 
  51          'RepeatIndicator', 
  52          'UserID', 
  53          'Spare', 
  54          'dac', 
  55          'fid', 
  56          'efid', 
  57          'month', 
  58          'day', 
  59          'hour', 
  60          'min', 
  61          'sec', 
  62          'stationid', 
  63          'longitude', 
  64          'latitude', 
  65          'timetoexpire', 
  66          'radius', 
  67  ) 
  68   
  69  fieldListPostgres = ( 
  70          'MessageID', 
  71          'RepeatIndicator', 
  72          'UserID', 
  73          'Spare', 
  74          'dac', 
  75          'fid', 
  76          'efid', 
  77          'month', 
  78          'day', 
  79          'hour', 
  80          'min', 
  81          'sec', 
  82          'stationid', 
  83          'whale',         
  84          'timetoexpire', 
  85          'radius', 
  86  ) 
  87   
  88  toPgFields = { 
  89          'longitude':'whale', 
  90          'latitude':'whale', 
  91  } 
  92  ''' 
  93  Go to the Postgis field names from the straight field name 
  94  ''' 
  95   
  96  fromPgFields = { 
  97          'whale':('longitude','latitude',), 
  98  } 
  99  ''' 
 100  Go from the Postgis field names to the straight field name 
 101  ''' 
 102   
 103  pgTypes = { 
 104          'whale':'POINT', 
 105  } 
 106  ''' 
 107  Lookup table for each postgis field name to get its type. 
 108  ''' 
 109   
 110 -def encode(params, validate=False): 
  111          '''Create a whalenotice binary message payload to pack into an AIS Msg whalenotice. 
 112   
 113          Fields in params: 
 114            - MessageID(uint): AIS message number.  Must be 8 (field automatically set to "8") 
 115            - RepeatIndicator(uint): Indicated how many times a message has been repeated 
 116            - UserID(uint): Unique ship identification number (MMSI) 
 117            - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0") 
 118            - dac(uint): Designated Area Code - 366 for the United States (field automatically set to "366") 
 119            - fid(uint): Functional IDentifier - 63 for the Whale Notice (field automatically set to "63") 
 120            - efid(uint): Extended Functional IDentifier.  1 for the Whale Notice (dac+fid+efid defines the exact message type) (field automatically set to "1") 
 121            - month(uint): Time of most recent whale detection.  UTC month 1..12 
 122            - day(uint): Time of most recent whale detection.  UTC day of the month 1..31 
 123            - hour(uint): Time of most recent whale detection.  UTC hours 0..23 
 124            - min(uint): Time of most recent whale detection.  UTC minutes 
 125            - sec(uint): Time of most recent whale detection.  UTC seconds 
 126            - stationid(aisstr6): Identifier of the station that detected the whale.  (e.g. which buoy) 
 127            - longitude(decimal): Center of the detection zone.  East West location 
 128            - latitude(decimal): Center of the detection zone.  North South location 
 129            - timetoexpire(uint): Seconds from the detection time until the notice expires 
 130            - radius(uint): Distance from center of detection zone (lat/lon above) 
 131          @param params: Dictionary of field names/values.  Throws a ValueError exception if required is missing 
 132          @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented. 
 133          @rtype: BitVector 
 134          @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8 
 135          @note: The returned bits may not be 6 bit aligned.  It is up to you to pad out the bits. 
 136          ''' 
 137   
 138          bvList = [] 
 139          bvList.append(binary.setBitVectorSize(BitVector(intVal=8),6)) 
 140          if 'RepeatIndicator' in params: 
 141                  bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2)) 
 142          else: 
 143                  bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2)) 
 144          bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30)) 
 145          bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2)) 
 146          bvList.append(binary.setBitVectorSize(BitVector(intVal=366),10)) 
 147          bvList.append(binary.setBitVectorSize(BitVector(intVal=63),6)) 
 148          bvList.append(binary.setBitVectorSize(BitVector(intVal=1),12)) 
 149          bvList.append(binary.setBitVectorSize(BitVector(intVal=params['month']),4)) 
 150          bvList.append(binary.setBitVectorSize(BitVector(intVal=params['day']),5)) 
 151          bvList.append(binary.setBitVectorSize(BitVector(intVal=params['hour']),5)) 
 152          bvList.append(binary.setBitVectorSize(BitVector(intVal=params['min']),6)) 
 153          bvList.append(binary.setBitVectorSize(BitVector(intVal=params['sec']),6)) 
 154          if 'stationid' in params: 
 155                  bvList.append(aisstring.encode(params['stationid'],42)) 
 156          else: 
 157                  bvList.append(aisstring.encode('@@@@@@@',42)) 
 158          if 'longitude' in params: 
 159                  bvList.append(binary.bvFromSignedInt(int(Decimal(params['longitude'])*Decimal('600000')),28)) 
 160          else: 
 161                  bvList.append(binary.bvFromSignedInt(108600000,28)) 
 162          if 'latitude' in params: 
 163                  bvList.append(binary.bvFromSignedInt(int(Decimal(params['latitude'])*Decimal('600000')),27)) 
 164          else: 
 165                  bvList.append(binary.bvFromSignedInt(54600000,27)) 
 166          if 'timetoexpire' in params: 
 167                  bvList.append(binary.setBitVectorSize(BitVector(intVal=params['timetoexpire']),16)) 
 168          else: 
 169                  bvList.append(binary.setBitVectorSize(BitVector(intVal=0),16)) 
 170          if 'radius' in params: 
 171                  bvList.append(binary.setBitVectorSize(BitVector(intVal=params['radius']),16)) 
 172          else: 
 173                  bvList.append(binary.setBitVectorSize(BitVector(intVal=65534),16)) 
 174   
 175          return binary.joinBV(bvList) 
  176   
 177 -def decode(bv, validate=False): 
  178          '''Unpack a whalenotice message  
 179   
 180          Fields in params: 
 181            - MessageID(uint): AIS message number.  Must be 8 (field automatically set to "8") 
 182            - RepeatIndicator(uint): Indicated how many times a message has been repeated 
 183            - UserID(uint): Unique ship identification number (MMSI) 
 184            - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0") 
 185            - dac(uint): Designated Area Code - 366 for the United States (field automatically set to "366") 
 186            - fid(uint): Functional IDentifier - 63 for the Whale Notice (field automatically set to "63") 
 187            - efid(uint): Extended Functional IDentifier.  1 for the Whale Notice (dac+fid+efid defines the exact message type) (field automatically set to "1") 
 188            - month(uint): Time of most recent whale detection.  UTC month 1..12 
 189            - day(uint): Time of most recent whale detection.  UTC day of the month 1..31 
 190            - hour(uint): Time of most recent whale detection.  UTC hours 0..23 
 191            - min(uint): Time of most recent whale detection.  UTC minutes 
 192            - sec(uint): Time of most recent whale detection.  UTC seconds 
 193            - stationid(aisstr6): Identifier of the station that detected the whale.  (e.g. which buoy) 
 194            - longitude(decimal): Center of the detection zone.  East West location 
 195            - latitude(decimal): Center of the detection zone.  North South location 
 196            - timetoexpire(uint): Seconds from the detection time until the notice expires 
 197            - radius(uint): Distance from center of detection zone (lat/lon above) 
 198          @type bv: BitVector 
 199          @param bv: Bits defining a message 
 200          @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented. 
 201          @rtype: dict 
 202          @return: params 
 203          ''' 
 204   
 205           
 206           
 207           
 208          r = {} 
 209          r['MessageID']=8 
 210          r['RepeatIndicator']=int(bv[6:8]) 
 211          r['UserID']=int(bv[8:38]) 
 212          r['Spare']=0 
 213          r['dac']=366 
 214          r['fid']=63 
 215          r['efid']=1 
 216          r['month']=int(bv[68:72]) 
 217          r['day']=int(bv[72:77]) 
 218          r['hour']=int(bv[77:82]) 
 219          r['min']=int(bv[82:88]) 
 220          r['sec']=int(bv[88:94]) 
 221          r['stationid']=aisstring.decode(bv[94:136]) 
 222          r['longitude']=Decimal(binary.signedIntFromBV(bv[136:164]))/Decimal('600000') 
 223          r['latitude']=Decimal(binary.signedIntFromBV(bv[164:191]))/Decimal('600000') 
 224          r['timetoexpire']=int(bv[191:207]) 
 225          r['radius']=int(bv[207:223]) 
 226          return r 
  227   
 230   
 233   
 236   
 239   
 242   
 245   
 248   
 250          return int(bv[68:72]) 
  251   
 253          return int(bv[72:77]) 
  254   
 256          return int(bv[77:82]) 
  257   
 259          return int(bv[82:88]) 
  260   
 262          return int(bv[88:94]) 
  263   
 266   
 269   
 272   
 274          return int(bv[191:207]) 
  275   
 277          return int(bv[207:223]) 
  278   
 279   
 281                  out.write("<h3>whalenotice</h3>\n") 
 282                  out.write("<table border=\"1\">\n") 
 283                  out.write("<tr bgcolor=\"orange\">\n") 
 284                  out.write("<th align=\"left\">Field Name</th>\n") 
 285                  out.write("<th align=\"left\">Type</th>\n") 
 286                  out.write("<th align=\"left\">Value</th>\n") 
 287                  out.write("<th align=\"left\">Value in Lookup Table</th>\n") 
 288                  out.write("<th align=\"left\">Units</th>\n") 
 289                  out.write("\n") 
 290                  out.write("<tr>\n") 
 291                  out.write("<td>MessageID</td>\n") 
 292                  out.write("<td>uint</td>\n") 
 293                  if 'MessageID' in params: 
 294                          out.write("     <td>"+str(params['MessageID'])+"</td>\n") 
 295                          out.write("     <td>"+str(params['MessageID'])+"</td>\n") 
 296                  out.write("</tr>\n") 
 297                  out.write("\n") 
 298                  out.write("<tr>\n") 
 299                  out.write("<td>RepeatIndicator</td>\n") 
 300                  out.write("<td>uint</td>\n") 
 301                  if 'RepeatIndicator' in params: 
 302                          out.write("     <td>"+str(params['RepeatIndicator'])+"</td>\n") 
 303                          if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut: 
 304                                  out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>") 
 305                          else: 
 306                                  out.write("<td><i>Missing LUT entry</i></td>") 
 307                  out.write("</tr>\n") 
 308                  out.write("\n") 
 309                  out.write("<tr>\n") 
 310                  out.write("<td>UserID</td>\n") 
 311                  out.write("<td>uint</td>\n") 
 312                  if 'UserID' in params: 
 313                          out.write("     <td>"+str(params['UserID'])+"</td>\n") 
 314                          out.write("     <td>"+str(params['UserID'])+"</td>\n") 
 315                  out.write("</tr>\n") 
 316                  out.write("\n") 
 317                  out.write("<tr>\n") 
 318                  out.write("<td>Spare</td>\n") 
 319                  out.write("<td>uint</td>\n") 
 320                  if 'Spare' in params: 
 321                          out.write("     <td>"+str(params['Spare'])+"</td>\n") 
 322                          out.write("     <td>"+str(params['Spare'])+"</td>\n") 
 323                  out.write("</tr>\n") 
 324                  out.write("\n") 
 325                  out.write("<tr>\n") 
 326                  out.write("<td>dac</td>\n") 
 327                  out.write("<td>uint</td>\n") 
 328                  if 'dac' in params: 
 329                          out.write("     <td>"+str(params['dac'])+"</td>\n") 
 330                          out.write("     <td>"+str(params['dac'])+"</td>\n") 
 331                  out.write("</tr>\n") 
 332                  out.write("\n") 
 333                  out.write("<tr>\n") 
 334                  out.write("<td>fid</td>\n") 
 335                  out.write("<td>uint</td>\n") 
 336                  if 'fid' in params: 
 337                          out.write("     <td>"+str(params['fid'])+"</td>\n") 
 338                          out.write("     <td>"+str(params['fid'])+"</td>\n") 
 339                  out.write("</tr>\n") 
 340                  out.write("\n") 
 341                  out.write("<tr>\n") 
 342                  out.write("<td>efid</td>\n") 
 343                  out.write("<td>uint</td>\n") 
 344                  if 'efid' in params: 
 345                          out.write("     <td>"+str(params['efid'])+"</td>\n") 
 346                          out.write("     <td>"+str(params['efid'])+"</td>\n") 
 347                  out.write("</tr>\n") 
 348                  out.write("\n") 
 349                  out.write("<tr>\n") 
 350                  out.write("<td>month</td>\n") 
 351                  out.write("<td>uint</td>\n") 
 352                  if 'month' in params: 
 353                          out.write("     <td>"+str(params['month'])+"</td>\n") 
 354                          out.write("     <td>"+str(params['month'])+"</td>\n") 
 355                  out.write("</tr>\n") 
 356                  out.write("\n") 
 357                  out.write("<tr>\n") 
 358                  out.write("<td>day</td>\n") 
 359                  out.write("<td>uint</td>\n") 
 360                  if 'day' in params: 
 361                          out.write("     <td>"+str(params['day'])+"</td>\n") 
 362                          out.write("     <td>"+str(params['day'])+"</td>\n") 
 363                  out.write("</tr>\n") 
 364                  out.write("\n") 
 365                  out.write("<tr>\n") 
 366                  out.write("<td>hour</td>\n") 
 367                  out.write("<td>uint</td>\n") 
 368                  if 'hour' in params: 
 369                          out.write("     <td>"+str(params['hour'])+"</td>\n") 
 370                          out.write("     <td>"+str(params['hour'])+"</td>\n") 
 371                  out.write("</tr>\n") 
 372                  out.write("\n") 
 373                  out.write("<tr>\n") 
 374                  out.write("<td>min</td>\n") 
 375                  out.write("<td>uint</td>\n") 
 376                  if 'min' in params: 
 377                          out.write("     <td>"+str(params['min'])+"</td>\n") 
 378                          out.write("     <td>"+str(params['min'])+"</td>\n") 
 379                  out.write("</tr>\n") 
 380                  out.write("\n") 
 381                  out.write("<tr>\n") 
 382                  out.write("<td>sec</td>\n") 
 383                  out.write("<td>uint</td>\n") 
 384                  if 'sec' in params: 
 385                          out.write("     <td>"+str(params['sec'])+"</td>\n") 
 386                          out.write("     <td>"+str(params['sec'])+"</td>\n") 
 387                  out.write("</tr>\n") 
 388                  out.write("\n") 
 389                  out.write("<tr>\n") 
 390                  out.write("<td>stationid</td>\n") 
 391                  out.write("<td>aisstr6</td>\n") 
 392                  if 'stationid' in params: 
 393                          out.write("     <td>"+str(params['stationid'])+"</td>\n") 
 394                          out.write("     <td>"+str(params['stationid'])+"</td>\n") 
 395                  out.write("</tr>\n") 
 396                  out.write("\n") 
 397                  out.write("<tr>\n") 
 398                  out.write("<td>longitude</td>\n") 
 399                  out.write("<td>decimal</td>\n") 
 400                  if 'longitude' in params: 
 401                          out.write("     <td>"+str(params['longitude'])+"</td>\n") 
 402                          out.write("     <td>"+str(params['longitude'])+"</td>\n") 
 403                  out.write("<td>degrees</td>\n") 
 404                  out.write("</tr>\n") 
 405                  out.write("\n") 
 406                  out.write("<tr>\n") 
 407                  out.write("<td>latitude</td>\n") 
 408                  out.write("<td>decimal</td>\n") 
 409                  if 'latitude' in params: 
 410                          out.write("     <td>"+str(params['latitude'])+"</td>\n") 
 411                          out.write("     <td>"+str(params['latitude'])+"</td>\n") 
 412                  out.write("<td>degrees</td>\n") 
 413                  out.write("</tr>\n") 
 414                  out.write("\n") 
 415                  out.write("<tr>\n") 
 416                  out.write("<td>timetoexpire</td>\n") 
 417                  out.write("<td>uint</td>\n") 
 418                  if 'timetoexpire' in params: 
 419                          out.write("     <td>"+str(params['timetoexpire'])+"</td>\n") 
 420                          out.write("     <td>"+str(params['timetoexpire'])+"</td>\n") 
 421                  out.write("<td>seconds</td>\n") 
 422                  out.write("</tr>\n") 
 423                  out.write("\n") 
 424                  out.write("<tr>\n") 
 425                  out.write("<td>radius</td>\n") 
 426                  out.write("<td>uint</td>\n") 
 427                  if 'radius' in params: 
 428                          out.write("     <td>"+str(params['radius'])+"</td>\n") 
 429                          out.write("     <td>"+str(params['radius'])+"</td>\n") 
 430                  out.write("<td>m</td>\n") 
 431                  out.write("</tr>\n") 
 432                  out.write("</table>\n") 
  433   
 434   
 436          '''KML (Keyhole Markup Language) for Google Earth, but without the header/footer''' 
 437          out.write("\    <Placemark>\n") 
 438          out.write("\t   <name>"+str(params['stationsid'])+"</name>\n") 
 439          out.write("\t\t<description>\n") 
 440          import StringIO 
 441          buf = StringIO.StringIO() 
 442          printHtml(params,buf) 
 443          import cgi 
 444          out.write(cgi.escape(buf.getvalue())) 
 445          out.write("\t\t</description>\n") 
 446          out.write("\t\t<styleUrl>#m_ylw-pushpin_copy0</styleUrl>\n") 
 447          out.write("\t\t<Point>\n") 
 448          out.write("\t\t\t<coordinates>") 
 449          out.write(str(params['longitude'])) 
 450          out.write(',') 
 451          out.write(str(params['latitude'])) 
 452          out.write(",0</coordinates>\n") 
 453          out.write("\t\t</Point>\n") 
 454          out.write("\t</Placemark>\n") 
  455   
 456 -def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'): 
  457          '''Print a whalenotice message to stdout. 
 458   
 459          Fields in params: 
 460            - MessageID(uint): AIS message number.  Must be 8 (field automatically set to "8") 
 461            - RepeatIndicator(uint): Indicated how many times a message has been repeated 
 462            - UserID(uint): Unique ship identification number (MMSI) 
 463            - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0") 
 464            - dac(uint): Designated Area Code - 366 for the United States (field automatically set to "366") 
 465            - fid(uint): Functional IDentifier - 63 for the Whale Notice (field automatically set to "63") 
 466            - efid(uint): Extended Functional IDentifier.  1 for the Whale Notice (dac+fid+efid defines the exact message type) (field automatically set to "1") 
 467            - month(uint): Time of most recent whale detection.  UTC month 1..12 
 468            - day(uint): Time of most recent whale detection.  UTC day of the month 1..31 
 469            - hour(uint): Time of most recent whale detection.  UTC hours 0..23 
 470            - min(uint): Time of most recent whale detection.  UTC minutes 
 471            - sec(uint): Time of most recent whale detection.  UTC seconds 
 472            - stationid(aisstr6): Identifier of the station that detected the whale.  (e.g. which buoy) 
 473            - longitude(decimal): Center of the detection zone.  East West location 
 474            - latitude(decimal): Center of the detection zone.  North South location 
 475            - timetoexpire(uint): Seconds from the detection time until the notice expires 
 476            - radius(uint): Distance from center of detection zone (lat/lon above) 
 477          @param params: Dictionary of field names/values.   
 478          @param out: File like object to write to 
 479          @rtype: stdout 
 480          @return: text to out 
 481          ''' 
 482   
 483          if 'std'==format: 
 484                  out.write("whalenotice:\n") 
 485                  if 'MessageID' in params: out.write("   MessageID:        "+str(params['MessageID'])+"\n") 
 486                  if 'RepeatIndicator' in params: out.write("     RepeatIndicator:  "+str(params['RepeatIndicator'])+"\n") 
 487                  if 'UserID' in params: out.write("      UserID:           "+str(params['UserID'])+"\n") 
 488                  if 'Spare' in params: out.write("       Spare:            "+str(params['Spare'])+"\n") 
 489                  if 'dac' in params: out.write(" dac:              "+str(params['dac'])+"\n") 
 490                  if 'fid' in params: out.write(" fid:              "+str(params['fid'])+"\n") 
 491                  if 'efid' in params: out.write("        efid:             "+str(params['efid'])+"\n") 
 492                  if 'month' in params: out.write("       month:            "+str(params['month'])+"\n") 
 493                  if 'day' in params: out.write(" day:              "+str(params['day'])+"\n") 
 494                  if 'hour' in params: out.write("        hour:             "+str(params['hour'])+"\n") 
 495                  if 'min' in params: out.write(" min:              "+str(params['min'])+"\n") 
 496                  if 'sec' in params: out.write(" sec:              "+str(params['sec'])+"\n") 
 497                  if 'stationid' in params: out.write("   stationid:        "+str(params['stationid'])+"\n") 
 498                  if 'longitude' in params: out.write("   longitude:        "+str(params['longitude'])+"\n") 
 499                  if 'latitude' in params: out.write("    latitude:         "+str(params['latitude'])+"\n") 
 500                  if 'timetoexpire' in params: out.write("        timetoexpire:     "+str(params['timetoexpire'])+"\n") 
 501                  if 'radius' in params: out.write("      radius:           "+str(params['radius'])+"\n") 
 502          elif 'csv'==format: 
 503                  if None == options.fieldList: 
 504                          options.fieldList = fieldList 
 505                  needComma = False; 
 506                  for field in fieldList: 
 507                          if needComma: out.write(',') 
 508                          needComma = True 
 509                          if field in params: 
 510                                  out.write(str(params[field])) 
 511                           
 512                  out.write("\n") 
 513          elif 'html'==format: 
 514                  printHtml(params,out) 
 515          elif 'sql'==format: 
 516                  sqlInsertStr(params,out,dbType=dbType) 
 517          elif 'kml'==format: 
 518                  printKml(params,out) 
 519          elif 'kml-full'==format: 
 520                  out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") 
 521                  out.write("<kml xmlns=\"http://earth.google.com/kml/2.1\">\n") 
 522                  out.write("<Document>\n") 
 523                  out.write("     <name>whalenotice</name>\n") 
 524                  printKml(params,out) 
 525                  out.write("</Document>\n") 
 526                  out.write("</kml>\n") 
 527          else:  
 528                  print "ERROR: unknown format:",format 
 529                  assert False 
 530   
 531          return  
  532   
 533  RepeatIndicatorEncodeLut = { 
 534          'default':'0', 
 535          'do not repeat any more':'3', 
 536          }  
 537   
 538  RepeatIndicatorDecodeLut = { 
 539          '0':'default', 
 540          '3':'do not repeat any more', 
 541          }  
 542   
 543   
 544   
 545   
 546   
 547  dbTableName='whalenotice' 
 548  'Database table name' 
 549   
 550 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None 
 551                  ,addCoastGuardFields=True 
 552                  ,dbType='postgres' 
 553                  ): 
  554          ''' 
 555          Return the SQL CREATE command for this message type 
 556          @param outfile: file like object to print to. 
 557          @param fields: which fields to put in the create.  Defaults to all. 
 558          @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 
 559          @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 
 560          @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 
 561          @type addCoastGuardFields: bool 
 562          @return: sql create string 
 563          @rtype: str 
 564   
 565          @see: sqlCreate 
 566          ''' 
 567           
 568          outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType))) 
  569   
 570 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'): 
  571          ''' 
 572          Return the sqlhelp object to create the table. 
 573   
 574          @param fields: which fields to put in the create.  Defaults to all. 
 575          @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 
 576          @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 
 577          @type addCoastGuardFields: bool 
 578          @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 
 579          @return: An object that can be used to generate a return 
 580          @rtype: sqlhelp.create 
 581          ''' 
 582          if None == fields: fields = fieldList 
 583          import sqlhelp 
 584          c = sqlhelp.create('whalenotice',dbType=dbType) 
 585          c.addPrimaryKey() 
 586          if 'MessageID' in fields: c.addInt ('MessageID') 
 587          if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator') 
 588          if 'UserID' in fields: c.addInt ('UserID') 
 589          if 'Spare' in fields: c.addInt ('Spare') 
 590          if 'dac' in fields: c.addInt ('dac') 
 591          if 'fid' in fields: c.addInt ('fid') 
 592          if 'efid' in fields: c.addInt ('efid') 
 593          if 'month' in fields: c.addInt ('month') 
 594          if 'day' in fields: c.addInt ('day') 
 595          if 'hour' in fields: c.addInt ('hour') 
 596          if 'min' in fields: c.addInt ('min') 
 597          if 'sec' in fields: c.addInt ('sec') 
 598          if 'stationid' in fields: c.addVarChar('stationid',7) 
 599          if dbType != 'postgres': 
 600                  if 'longitude' in fields: c.addDecimal('longitude',8,5) 
 601          if dbType != 'postgres': 
 602                  if 'latitude' in fields: c.addDecimal('latitude',8,5) 
 603          if 'timetoexpire' in fields: c.addInt ('timetoexpire') 
 604          if 'radius' in fields: c.addInt ('radius') 
 605   
 606          if addCoastGuardFields: 
 607                   
 608                   
 609                   
 610                   
 611                   
 612                  c.addVarChar('cg_r',15)    
 613                  c.addInt('cg_sec')         
 614   
 615                  c.addTimestamp('cg_timestamp')  
 616   
 617          if dbType == 'postgres': 
 618                   
 619                   
 620                  c.addPostGIS('whale','POINT',2,SRID=4326); 
 621   
 622          return c 
  623   
 624 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'): 
  625          ''' 
 626          Return the SQL INSERT command for this message type 
 627          @param params: dictionary of values keyed by field name 
 628          @param outfile: file like object to print to. 
 629          @param extraParams: A sequence of tuples containing (name,sql type) for additional fields 
 630          @return: sql create string 
 631          @rtype: str 
 632   
 633          @see: sqlCreate 
 634          ''' 
 635          outfile.write(str(sqlInsert(params,extraParams,dbType=dbType))) 
  636   
 637   
 638 -def sqlInsert(params,extraParams=None,dbType='postgres'): 
  639          ''' 
 640          Give the SQL INSERT statement 
 641          @param params: dict keyed by field name of values 
 642          @param extraParams: any extra fields that you have created beyond the normal ais message fields 
 643          @rtype: sqlhelp.insert 
 644          @return: insert class instance 
 645          @todo: allow optional type checking of params? 
 646          @warning: this will take invalid keys happily and do what??? 
 647          ''' 
 648          import sqlhelp 
 649          i = sqlhelp.insert('whalenotice',dbType=dbType) 
 650   
 651          if dbType=='postgres': 
 652                  finished = [] 
 653                  for key in params: 
 654                          if key in finished:  
 655                                  continue 
 656   
 657                          if key not in toPgFields and key not in fromPgFields: 
 658                                  if type(params[key])==Decimal: i.add(key,float(params[key])) 
 659                                  else: i.add(key,params[key]) 
 660                          else: 
 661                                  if key in fromPgFields: 
 662                                          val = params[key] 
 663                                           
 664                                          i.addPostGIS(key,val) 
 665                                          finished.append(key) 
 666                                  else: 
 667                                           
 668                                          pgName = toPgFields[key] 
 669                                           
 670                                          valStr=pgTypes[pgName]+'(' 
 671                                          vals = [] 
 672                                          for nonPgKey in fromPgFields[pgName]: 
 673                                                  vals.append(str(params[nonPgKey])) 
 674                                                  finished.append(nonPgKey) 
 675                                          valStr+=' '.join(vals)+')' 
 676                                          i.addPostGIS(pgName,valStr) 
 677          else: 
 678                  for key in params:  
 679                          if type(params[key])==Decimal: i.add(key,float(params[key])) 
 680                          else: i.add(key,params[key]) 
 681   
 682          if None != extraParams: 
 683                  for key in extraParams:  
 684                          i.add(key,extraParams[key]) 
 685   
 686          return i 
  687   
 688   
 689   
 690   
 691   
 694          ''' 
 695          Return the LaTeX definition table for this message type 
 696          @param outfile: file like object to print to. 
 697          @type outfile: file obj 
 698          @return: LaTeX table string via the outfile 
 699          @rtype: str 
 700   
 701          ''' 
 702          o = outfile 
 703   
 704          o.write(''' 
 705  \\begin{table}%[htb] 
 706  \\centering 
 707  \\begin{tabular}{|l|c|l|} 
 708  \\hline 
 709  Parameter & Number of bits & Description  
 710  \\\\  \\hline\\hline 
 711  MessageID & 6 & AIS message number.  Must be 8 \\\\ \hline  
 712  RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline  
 713  UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline  
 714  Spare & 2 & Reserved for definition by a regional authority. \\\\ \hline  
 715  dac & 10 & Designated Area Code - 366 for the United States \\\\ \hline  
 716  fid & 6 & Functional IDentifier - 63 for the Whale Notice \\\\ \hline  
 717  efid & 12 & Extended Functional IDentifier.  1 for the Whale Notice (dac+fid+efid defines the exact message type) \\\\ \hline  
 718  month & 4 & Time of most recent whale detection.  UTC month 1..12 \\\\ \hline  
 719  day & 5 & Time of most recent whale detection.  UTC day of the month 1..31 \\\\ \hline  
 720  hour & 5 & Time of most recent whale detection.  UTC hours 0..23 \\\\ \hline  
 721  min & 6 & Time of most recent whale detection.  UTC minutes \\\\ \hline  
 722  sec & 6 & Time of most recent whale detection.  UTC seconds \\\\ \hline  
 723  stationid & 42 & Identifier of the station that detected the whale.  (e.g. which buoy) \\\\ \hline  
 724  longitude & 28 & Center of the detection zone.  East West location \\\\ \hline  
 725  latitude & 27 & Center of the detection zone.  North South location \\\\ \hline  
 726  timetoexpire & 16 & Seconds from the detection time until the notice expires \\\\ \hline  
 727  radius & 16 & Distance from center of detection zone (lat/lon above)\\\\ \\hline \\hline 
 728  Total bits & 223 & Appears to take 2 slots with 201 pad bits to fill the last slot \\\\ \\hline 
 729  \\end{tabular} 
 730  \\caption{AIS message number 8: Endangered whale notification binary message} 
 731  \\label{tab:whalenotice} 
 732  \\end{table} 
 733  ''') 
  734   
 735   
 736   
 737   
 738   
 739 -def textDefinitionTable(outfile=sys.stdout 
 740                  ,delim='\t' 
 741                  ): 
  742          ''' 
 743          Return the text definition table for this message type 
 744          @param outfile: file like object to print to. 
 745          @type outfile: file obj 
 746          @return: text table string via the outfile 
 747          @rtype: str 
 748   
 749          ''' 
 750          o = outfile 
 751          o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description  
 752  MessageID'''+delim+'''6'''+delim+'''AIS message number.  Must be 8 
 753  RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated 
 754  UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI) 
 755  Spare'''+delim+'''2'''+delim+'''Reserved for definition by a regional authority. 
 756  dac'''+delim+'''10'''+delim+'''Designated Area Code - 366 for the United States 
 757  fid'''+delim+'''6'''+delim+'''Functional IDentifier - 63 for the Whale Notice 
 758  efid'''+delim+'''12'''+delim+'''Extended Functional IDentifier.  1 for the Whale Notice (dac+fid+efid defines the exact message type) 
 759  month'''+delim+'''4'''+delim+'''Time of most recent whale detection.  UTC month 1..12 
 760  day'''+delim+'''5'''+delim+'''Time of most recent whale detection.  UTC day of the month 1..31 
 761  hour'''+delim+'''5'''+delim+'''Time of most recent whale detection.  UTC hours 0..23 
 762  min'''+delim+'''6'''+delim+'''Time of most recent whale detection.  UTC minutes 
 763  sec'''+delim+'''6'''+delim+'''Time of most recent whale detection.  UTC seconds 
 764  stationid'''+delim+'''42'''+delim+'''Identifier of the station that detected the whale.  (e.g. which buoy) 
 765  longitude'''+delim+'''28'''+delim+'''Center of the detection zone.  East West location 
 766  latitude'''+delim+'''27'''+delim+'''Center of the detection zone.  North South location 
 767  timetoexpire'''+delim+'''16'''+delim+'''Seconds from the detection time until the notice expires 
 768  radius'''+delim+'''16'''+delim+'''Distance from center of detection zone (lat/lon above) 
 769  Total bits'''+delim+'''223'''+delim+'''Appears to take 2 slots with 201 pad bits to fill the last slot''') 
  770   
 771   
 772   
 773   
 774   
 775  import unittest 
 777          '''Return a params file base on the testvalue tags. 
 778          @rtype: dict 
 779          @return: params based on testvalue tags 
 780          ''' 
 781          params = {} 
 782          params['MessageID'] = 8 
 783          params['RepeatIndicator'] = 1 
 784          params['UserID'] = 1193046 
 785          params['Spare'] = 0 
 786          params['dac'] = 366 
 787          params['fid'] = 63 
 788          params['efid'] = 1 
 789          params['month'] = 2 
 790          params['day'] = 28 
 791          params['hour'] = 23 
 792          params['min'] = 45 
 793          params['sec'] = 58 
 794          params['stationid'] = 'A234567' 
 795          params['longitude'] = Decimal('-122.16328055555556') 
 796          params['latitude'] = Decimal('37.424458333333334') 
 797          params['timetoexpire'] = 1 
 798          params['radius'] = 5000 
 799   
 800          return params 
  801   
 803          '''Use testvalue tag text from each type to build test case the whalenotice message''' 
 805   
 806                  params = testParams() 
 807                  bits   = encode(params) 
 808                  r      = decode(bits) 
 809   
 810                   
 811                  self.failUnlessEqual(r['MessageID'],params['MessageID']) 
 812                  self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator']) 
 813                  self.failUnlessEqual(r['UserID'],params['UserID']) 
 814                  self.failUnlessEqual(r['Spare'],params['Spare']) 
 815                  self.failUnlessEqual(r['dac'],params['dac']) 
 816                  self.failUnlessEqual(r['fid'],params['fid']) 
 817                  self.failUnlessEqual(r['efid'],params['efid']) 
 818                  self.failUnlessEqual(r['month'],params['month']) 
 819                  self.failUnlessEqual(r['day'],params['day']) 
 820                  self.failUnlessEqual(r['hour'],params['hour']) 
 821                  self.failUnlessEqual(r['min'],params['min']) 
 822                  self.failUnlessEqual(r['sec'],params['sec']) 
 823                  self.failUnlessEqual(r['stationid'],params['stationid']) 
 824                  self.failUnlessAlmostEqual(r['longitude'],params['longitude'],5) 
 825                  self.failUnlessAlmostEqual(r['latitude'],params['latitude'],5) 
 826                  self.failUnlessEqual(r['timetoexpire'],params['timetoexpire']) 
 827                  self.failUnlessEqual(r['radius'],params['radius']) 
   828   
 830          parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 
 831                  help='decode a "whalenotice" AIS message') 
 832          parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 
 833                  help='encode a "whalenotice" AIS message') 
 834          parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int' 
 835                  ,help='Field parameter value [default: %default]') 
 836          parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int' 
 837                  ,help='Field parameter value [default: %default]') 
 838          parser.add_option('--month-field', dest='monthField',metavar='uint',type='int' 
 839                  ,help='Field parameter value [default: %default]') 
 840          parser.add_option('--day-field', dest='dayField',metavar='uint',type='int' 
 841                  ,help='Field parameter value [default: %default]') 
 842          parser.add_option('--hour-field', dest='hourField',metavar='uint',type='int' 
 843                  ,help='Field parameter value [default: %default]') 
 844          parser.add_option('--min-field', dest='minField',metavar='uint',type='int' 
 845                  ,help='Field parameter value [default: %default]') 
 846          parser.add_option('--sec-field', dest='secField',metavar='uint',type='int' 
 847                  ,help='Field parameter value [default: %default]') 
 848          parser.add_option('--stationid-field', dest='stationidField',default='@@@@@@@',metavar='aisstr6',type='string' 
 849                  ,help='Field parameter value [default: %default]') 
 850          parser.add_option('--longitude-field', dest='longitudeField',default=Decimal('181'),metavar='decimal',type='string' 
 851                  ,help='Field parameter value [default: %default]') 
 852          parser.add_option('--latitude-field', dest='latitudeField',default=Decimal('91'),metavar='decimal',type='string' 
 853                  ,help='Field parameter value [default: %default]') 
 854          parser.add_option('--timetoexpire-field', dest='timetoexpireField',default=0,metavar='uint',type='int' 
 855                  ,help='Field parameter value [default: %default]') 
 856          parser.add_option('--radius-field', dest='radiusField',default=65534,metavar='uint',type='int' 
 857                  ,help='Field parameter value [default: %default]') 
  858   
 859   
 860  if __name__=='__main__': 
 861   
 862          from optparse import OptionParser 
 863          parser = OptionParser(usage="%prog [options]", 
 864                  version="%prog "+__version__) 
 865   
 866          parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 
 867                  help='run the documentation tests') 
 868          parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 
 869                  help='run the unit tests') 
 870          parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 
 871                  help='Make the test output verbose') 
 872   
 873           
 874           
 875          typeChoices = ('binary','nmeapayload','nmea')  
 876          parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 
 877                  ,default='nmeapayload' 
 878                  ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]') 
 879   
 880   
 881          outputChoices = ('std','html','csv','sql' , 'kml','kml-full') 
 882          parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 
 883                  ,default='std' 
 884                  ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 
 885   
 886          parser.add_option('-o','--output',dest='outputFileName',default=None, 
 887                            help='Name of the python file to write [default: stdout]') 
 888   
 889          parser.add_option('-f','--fields',dest='fieldList',default=None, action='append', 
 890                            choices=fieldList, 
 891                            help='Which fields to include in the output.  Currently only for csv output [default: all]') 
 892   
 893          parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true', 
 894                            help='Print the field name for csv') 
 895   
 896          parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true', 
 897                            help='Print out an sql create command for the table.') 
 898   
 899          parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true', 
 900                            help='Print a LaTeX table of the type') 
 901   
 902          parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true', 
 903                            help='Print delimited table of the type (for Word table importing)') 
 904          parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t' 
 905                            ,help='Delimiter for text table [default: \'%default\'](for Word table importing)') 
 906   
 907   
 908          dbChoices = ('sqlite','postgres') 
 909          parser.add_option('-D','--db-type',dest='dbType',default='postgres' 
 910                            ,choices=dbChoices,type='choice' 
 911                            ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]') 
 912   
 913          addMsgOptions(parser) 
 914   
 915          (options,args) = parser.parse_args() 
 916          success=True 
 917   
 918          if options.doctest: 
 919                  import os; print os.path.basename(sys.argv[0]), 'doctests ...', 
 920                  sys.argv= [sys.argv[0]] 
 921                  if options.verbose: sys.argv.append('-v') 
 922                  import doctest 
 923                  numfail,numtests=doctest.testmod() 
 924                  if numfail==0: print 'ok' 
 925                  else:  
 926                          print 'FAILED' 
 927                          success=False 
 928   
 929          if not success: sys.exit('Something Failed') 
 930          del success  
 931   
 932          if options.unittest: 
 933                  sys.argv = [sys.argv[0]] 
 934                  if options.verbose: sys.argv.append('-v') 
 935                  unittest.main() 
 936   
 937          outfile = sys.stdout 
 938          if None!=options.outputFileName: 
 939                  outfile = file(options.outputFileName,'w') 
 940   
 941   
 942          if options.doEncode: 
 943                   
 944                  if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField") 
 945                  if None==options.UserIDField: parser.error("missing value for UserIDField") 
 946                  if None==options.monthField: parser.error("missing value for monthField") 
 947                  if None==options.dayField: parser.error("missing value for dayField") 
 948                  if None==options.hourField: parser.error("missing value for hourField") 
 949                  if None==options.minField: parser.error("missing value for minField") 
 950                  if None==options.secField: parser.error("missing value for secField") 
 951                  if None==options.stationidField: parser.error("missing value for stationidField") 
 952                  if None==options.longitudeField: parser.error("missing value for longitudeField") 
 953                  if None==options.latitudeField: parser.error("missing value for latitudeField") 
 954                  if None==options.timetoexpireField: parser.error("missing value for timetoexpireField") 
 955                  if None==options.radiusField: parser.error("missing value for radiusField") 
 956                  msgDict={ 
 957                          'MessageID': '8', 
 958                          'RepeatIndicator': options.RepeatIndicatorField, 
 959                          'UserID': options.UserIDField, 
 960                          'Spare': '0', 
 961                          'dac': '366', 
 962                          'fid': '63', 
 963                          'efid': '1', 
 964                          'month': options.monthField, 
 965                          'day': options.dayField, 
 966                          'hour': options.hourField, 
 967                          'min': options.minField, 
 968                          'sec': options.secField, 
 969                          'stationid': options.stationidField, 
 970                          'longitude': options.longitudeField, 
 971                          'latitude': options.latitudeField, 
 972                          'timetoexpire': options.timetoexpireField, 
 973                          'radius': options.radiusField, 
 974                  } 
 975   
 976                  bits = encode(msgDict) 
 977                  if 'binary'==options.ioType: print str(bits) 
 978                  elif 'nmeapayload'==options.ioType: 
 979                           
 980                          print "bitLen",len(bits) 
 981                          bitLen=len(bits) 
 982                          if bitLen%6!=0: 
 983                              bits = bits + BitVector(size=(6 - (bitLen%6)))   
 984                          print "result:",binary.bitvectoais6(bits)[0] 
 985   
 986   
 987                   
 988                  elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 
 989                  else: sys.exit('ERROR: unknown ioType.  Help!') 
 990   
 991   
 992          if options.sqlCreate: 
 993                  sqlCreateStr(outfile,options.fieldList,dbType=options.dbType) 
 994   
 995          if options.latexDefinitionTable: 
 996                  latexDefinitionTable(outfile) 
 997   
 998           
 999          if options.textDefinitionTable: 
1000                  textDefinitionTable(outfile,options.delimTextDefinitionTable) 
1001   
1002          if options.printCsvfieldList: 
1003                   
1004                  if None == options.fieldList: options.fieldList = fieldList 
1005                  import StringIO 
1006                  buf = StringIO.StringIO() 
1007                  for field in options.fieldList: 
1008                          buf.write(field+',') 
1009                  result = buf.getvalue() 
1010                  if result[-1] == ',': print result[:-1] 
1011                  else: print result 
1012   
1013          if options.doDecode: 
1014                  if len(args)==0: args = sys.stdin 
1015                  for msg in args: 
1016                          bv = None 
1017   
1018                          if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'): 
1019                                   
1020                                   
1021                                  bv = binary.ais6tobitvec(msg.split(',')[5]) 
1022                          else:  
1023                                   
1024                                  binaryMsg=True 
1025                                  for c in msg: 
1026                                          if c not in ('0','1'): 
1027                                                  binaryMsg=False 
1028                                                  break 
1029                                  if binaryMsg: 
1030                                          bv = BitVector(bitstring=msg) 
1031                                  else:  
1032                                          bv = binary.ais6tobitvec(msg) 
1033   
1034                          printFields(decode(bv) 
1035                                      ,out=outfile 
1036                                      ,format=options.outputType 
1037                                      ,fieldList=options.fieldList 
1038                                      ,dbType=options.dbType 
1039                                      ) 
1040