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

Source Code for Module ais.ais_msg_4

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