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

Source Code for Module ais.ais_msg_3

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