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

Source Code for Module ais.binary

  1  #!/usr/bin/env python 
  2   
  3  __version__ = '$Revision: 2075 $'.split()[1] # See man ident 
  4  __date__ = '$Date: 2006-05-03 04:18:20 -0400 (Wed, 03 May 2006) $'.split()[1] 
  5  __author__ = 'Kurt Schwehr' 
  6   
  7  __doc__=''' 
  8  AIS binary helper functions. 
  9   
 10  Code to convert AIS messages between binary BitVectors and strings. 
 11  They are usually encoded an ASCII 6-bit packing within NMEA 
 12  !AIVDM/!AIVDO messages. 
 13   
 14  @see: NMEA strings at U{http://gpsd.berlios.de/NMEA.txt} 
 15  @see: Wikipedia at U{http://en.wikipedia.org/wiki/Automatic_Identification_System} 
 16   
 17  @author: '''+__author__+''' 
 18  @version: ''' + __version__ +''' 
 19  @copyright: 2006 
 20   
 21   
 22  @todo: Flush out stuffBits and unstuffBits 
 23  @todo: bitvectorais6 
 24  @todo: test cases for ais6tobitvec 
 25   
 26  @var decode: cache of character to BitVector lookup 
 27  @var encode: cache of ais int value to charcter 
 28  @var __date__: Date of last svn commit 
 29  @undocumented: __version__ __author__ __doc__ myparser 
 30  @undocumented: buildLookupTables ais6tobitvecSLOW 
 31   
 32  @bug: code up stuffBits and unstuffBits 
 33  @bug: find an example needing bitstuffing 
 34  @undocumented: stuffBits unstuffBits 
 35  ''' 
 36   
 37   
 38  # Python standard library 
 39  import sys 
 40   
 41  # Outside modules 
 42  from BitVector import BitVector 
 43  import struct 
 44   
45 -def float2bitvec(floatval):
46 ''' 47 Get the IEEE floating point bits for a python float 48 49 >>> print float2bitvec(1.) 50 00111111100000000000000000000000 51 52 >>> print float2bitvec (-1.) 53 10111111100000000000000000000000 54 55 >>> print float2bitvec (-999999.) 56 11001001011101000010001111110000 57 58 59 60 @bug: May have bite order backwards 61 @type floatval: number 62 @param floatval: number to convert to bits 63 @rtype: BitVector 64 @return: 32 bits 65 @todo: Is there a faster way to do this? 66 @see: U{struct module<http://www.python.org/doc/current/lib/module-struct.html>} 67 ''' 68 s = struct.pack('!f',floatval) # FIX: Is this the right bight order? Could easily be wrong!!!! 69 i = struct.unpack('!I',s)[0] 70 #print 'unpacked:',i 71 #return setBitVectorSize(BitVector(intVal=i),32) 72 73 # Old way... since BitVector can't encode large intVals (>2^31) 74 # FIX: make this go in one step now that bitvector 1.3 is out. 75 bvList = [] 76 for i in range(4): 77 bv1 = setBitVectorSize(BitVector(intVal=ord(s[i])),8) 78 #bv2 = BitVector(intVal=ord(s[i]),size=8) 79 bvList.append(bv1) 80 return joinBV(bvList)
81
82 -def bitvec2float(bv):
83 ''' 84 Convert a 32 bit bitvector representing an IEEE float into a python float 85 @bug: May have bite order backwards 86 @type bv: BitVector 87 @param bv: 32 bits representing an IEEE float 88 @rtype: float 89 @return: the corresponing number 90 @see: U{struct module<http://www.python.org/doc/current/lib/module-struct.html>} 91 ''' 92 return struct.unpack('!f',chr(bv[0:8]) + chr(bv[8:16]) + chr(bv[16:24]) + chr(bv[24:32]))[0]
93 94
95 -def joinBV(bvSeq):
96 ''' 97 Combined a sequence of bit vectors into one large BitVector 98 @param bvSeq: sequence of bitvectors 99 @return: aggregated BitVector 100 @bug: replace with a faster algorithm! 101 ''' 102 bvTotal=BitVector(size=0) 103 for bv in bvSeq: 104 bvTotal = bvTotal + bv 105 106 return bvTotal
107
108 -def setBitVectorSize(bv,size=8):
109 """Pad a BitVector with 0's on the left until it is at least the size specified 110 111 @param bv: BitVector that needs to meet a minimim size 112 @type bv: BitVector 113 @param size: Minimum number of bits to make the new BitVector 114 @type size: int 115 @return: BitVector that is size bits or larger 116 @rtype: BitVector 117 118 @todo: What to do if the vector is larger than size? 119 """ 120 pad=BitVector(bitlist=[0]) 121 while len(bv)<size: bv = pad + bv 122 return bv
123 124
125 -def addone(bv):
126 ''' 127 Add one bit to a bit vector. Overflows are silently dropped. 128 129 >>> print addone(BitVector(bitstring='1100')) 130 1101 131 132 >>> print addone(BitVector(bitstring='1111')) 133 0000 134 135 @param bv: Add one to these bits 136 @type bv: BitVector 137 @return: Bits with one added 138 @rtype: BitVector 139 ''' 140 new = bv 141 r = range(1,len(bv)+1) 142 for i in r: 143 index = len(bv)-i 144 if 0==bv[index]: 145 new[index]=1 146 break 147 new[index]=0 148 return new
149
150 -def subone(bv):
151 ''' 152 Subtract one bit from a bit vector 153 154 >>> print subone(BitVector(bitstring='1111')) 155 1110 156 >>> print subone(BitVector(bitstring='0010')) 157 0001 158 >>> print subone(BitVector(bitstring='0000')) 159 1111 160 161 @param bv: Bits to add one bit to the right side 162 @type bv: BitVector 163 @rtype: BitVector 164 ''' 165 new = bv 166 r = range(1,len(bv)+1) 167 for i in r: 168 index = len(bv)-i 169 if 1==bv[index]: 170 new[index]=0 171 break 172 new[index]=1 173 return new
174 175
176 -def bvFromSignedInt(intVal,bitSize=None):
177 ''' 178 Create a twos complement BitVector from a signed integer. 179 180 Positives must have a '0' in the left hand position. 181 182 >>> print bvFromSignedInt(0,bitSize=4) 183 0000 184 >>> print bvFromSignedInt(1,bitSize=4) 185 0001 186 >>> print bvFromSignedInt(7,bitSize=4) 187 0111 188 189 Negative numbers must have a '1' in the left hand position. 190 191 >>> print bvFromSignedInt(-1,bitSize=4) 192 1111 193 >>> print bvFromSignedInt(-2,bitSize=4) 194 1110 195 >>> print bvFromSignedInt(-7,bitSize=4) 196 1001 197 198 @param intVal: integer value to turn into a bit vector 199 @type intVal: int 200 @param bitSize: optional size to flush out the number of bits 201 @type bitSize: int 202 @return: A Bit Vector flushed out to the right size 203 @rtype: BitVector 204 ''' 205 bv = None 206 if None==bitSize: 207 bv = BitVector(intVal=abs(intVal)) 208 else: 209 bv = setBitVectorSize(BitVector(intVal=abs(intVal)),bitSize-1) 210 if (bitSize-1!=len(bv)): 211 print 'ERROR: bitsize not right' 212 print ' ',bitSize-1,len(bv) 213 assert(False) 214 if intVal>=0: 215 bv = BitVector(intVal=0) + bv 216 else: 217 bv = subone(bv) 218 bv = ~bv 219 bv = BitVector(intVal=1) + bv 220 return bv
221
222 -def signedIntFromBV(bv):
223 ''' 224 Interpret a bit vector as an signed integer. int(BitVector) 225 defaults to treating the bits as an unsigned int. Assumes twos 226 complement representation. 227 228 U{http://en.wikipedia.org/wiki/Twos_complement} 229 230 Positive values decode like so: 231 232 >>> signedIntFromBV(BitVector(bitstring='0000')) 233 0 234 >>> signedIntFromBV(BitVector(bitstring='0101')) 235 5 236 237 Here are some negative integer examples: 238 239 >>> signedIntFromBV(BitVector(bitstring='1111')) 240 -1 241 >>> signedIntFromBV(BitVector(bitstring='1110')) 242 -2 243 >>> signedIntFromBV(BitVector(bitstring='1010')) 244 -6 245 >>> signedIntFromBV(BitVector(bitstring='1001')) 246 -7 247 >>> signedIntFromBV(BitVector(bitstring='1000')) 248 -8 249 250 @param bv: Bits to treat as an signed int 251 @type bv: BitVector 252 @return: Signed integer 253 @rtype: int 254 255 @note: Does not know the difference between byte orders. 256 ''' 257 if 0==bv[0]: return int(bv) 258 # Nope, so it is negative 259 val = int(addone(~(bv[1:]))) 260 if 0 != val: return -val 261 return -(int(bv))
262 263 264
265 -def ais6chartobitvec(char6):
266 ''' 267 Create a 6 bit BitVector for a single character 268 269 >>> print int(ais6chartobitvec('0')) 270 0 271 >>> print int(ais6chartobitvec('1')) 272 1 273 >>> print int(ais6chartobitvec('9')) 274 9 275 >>> print int(ais6chartobitvec('<')) 276 12 277 >>> print int(ais6chartobitvec('=')) 278 13 279 >>> print int(ais6chartobitvec('@')) 280 16 281 >>> print int(ais6chartobitvec('A')) 282 17 283 284 >>> print int(ais6chartobitvec('O')) 285 31 286 >>> print int(ais6chartobitvec('P')) 287 32 288 >>> print int(ais6chartobitvec('Q')) 289 33 290 291 >>> print int(ais6chartobitvec('R')) 292 34 293 294 >>> print int(ais6chartobitvec('Z')) 295 34 296 >>> print int(ais6chartobitvec('a')) 297 41 298 >>> print int(ais6chartobitvec('w')) 299 63 300 >>> print ais6chartobitvec('w') 301 111111 302 303 x, y, and z will not appear. 304 305 @param char6: character of an AIS message where each character represents 6 bits 306 @type char6: str(1) 307 @return: Decoded bits for one character (does not know about padding) 308 @rtype: BitVector(6) 309 @bug: need to cut down the doctest here and copy all of the current one to tests/test_binary.py 310 ''' 311 c = ord(char6) 312 val = c - 48 313 if val>=40: val -= 8 314 if 0==val: return(BitVector(size=6)) 315 return setBitVectorSize(BitVector(intVal=val),6)
316 317 318
319 -def ais6tobitvecSLOW(str6):
320 """Convert an ITU AIS 6 bit string into a bit vector. Each character 321 represents 6 bits. 322 323 >>> print ais6tobitvecSLOW('6bF:Z') 324 000110101010010110001010100010 325 326 @note: If the original BitVector had ((len(bitvector) % 6 > 0), 327 then there will be pad bits in the str6. This function has no way 328 to know how many pad bits there are. 329 330 @bug: Need to add pad bit handling 331 332 @param str6: ASCII that as it appears in the NMEA string 333 @type str6: string 334 @return: decoded bits (not unstuffed... what do I mean by 335 unstuffed?). There may be pad bits at the tail to make this 6 bit 336 aligned. 337 @rtype: BitVector 338 """ 339 bvtotal = BitVector(size=0) 340 341 for c in str6: 342 c = ord(c) 343 val = c - 48 344 if val>=40: val -= 8 345 bv = None 346 #print 'slow: ',c,val 347 if 0==val: 348 bv = BitVector(size=6) 349 else: 350 bv = setBitVectorSize(BitVector(intVal=val),6) 351 #bv = BitVector(intVal=val,size=6) # FIX: I thought this would work, but it is more than 6 bits? 352 bvtotal += bv 353 return bvtotal
354 355 356 357
358 -def buildLookupTables():
359 ''' 360 @bug: rename the local encode/decode dictionaries so there is no shadowing 361 ''' 362 decode={} 363 encode={} 364 for i in range(127): 365 # for i in range(64): # FIX: is this the right range? 366 if i<48: continue 367 c = chr(i) 368 bv = ais6tobitvecSLOW(c) 369 val = int (bv) 370 if val>=64: continue 371 encode[val] = c 372 decode[c] = bv 373 #print i, val, bv, "'"+str(c)+"'" 374 return encode,decode
375 376 encode,decode=buildLookupTables() 377 378 #print 'encode:' 379 #for key in encode: 380 # print key,':',encode[key] 381 #print 'encode:' 382 #for key in decode: 383 # print key,':',decode[key]
384 -def ais6tobitvec(str6):
385 '''Convert an ITU AIS 6 bit string into a bit vector. Each character 386 represents 6 bits. This is the NMEA !AIVD[MO] message payload. 387 388 @note: If the original BitVector had ((len(bitvector) % 6 > 0), 389 then there will be pad bits in the str6. This function has no way 390 to know how many pad bits there are. 391 392 >>> print ais6tobitvec('6') 393 000110 394 395 >>> print ais6tobitvec('6b') 396 000110101010 397 398 >>> print ais6tobitvec('6bF:Z') 399 000110101010010110001010100010 400 401 @bug: Need to add pad bit handling 402 403 @param str6: ASCII that as it appears in the NMEA string 404 @type str6: string 405 @return: decoded bits (not unstuffed... what do I mean by 406 unstuffed?). There may be pad bits at the tail to make this 6 bit 407 aligned. 408 @rtype: BitVector 409 ''' 410 bvtotal = BitVector(size=6*len(str6)) 411 412 for pos in range(len(str6)): 413 bv = decode[str6[pos]] 414 start = pos*6 415 for i in range(6): 416 bvtotal[i+start] = bv[i] 417 return bvtotal
418
419 -def getPadding(bv):
420 ''' 421 Return the number of bits that need to be padded for a bit vector 422 423 >>> getPadding(BitVector(bitstring='0')) 424 5 425 >>> getPadding(BitVector(bitstring='01')) 426 4 427 >>> getPadding(BitVector(bitstring='010')) 428 3 429 >>> getPadding(BitVector(bitstring='0101')) 430 2 431 >>> getPadding(BitVector(bitstring='01010')) 432 1 433 >>> getPadding(BitVector(bitstring='010101')) 434 0 435 >>> getPadding(BitVector(bitstring='0101010')) 436 5 437 @rtype: int 438 @return: number of pad bits required for this bitvector to make it bit aligned to the ais nmea string 439 ''' 440 pad = 6-(len(bv)%6) 441 if 6==pad: pad = 0 442 return pad
443
444 -def bitvectoais6(bv,doPadding=True):
445 """Convert bit vector int an ITU AIS 6 bit string. Each character represents 6 bits 446 447 >>> print bitvectoais6(BitVector(bitstring='000110101010010110001010100010')) 448 ('6bF:Z', 0) 449 450 @param bv: message bits (must be already stuffed) 451 @type bv: BitVector 452 @return: str6 ASCII that as it appears in the NMEA string 453 @rtype: str, pad 454 455 @todo: make a test base for needing padding 456 @bug: handle case when padding needed 457 """ 458 #print "FIX: need to test" 459 pad = 6-(len(bv)%6) 460 if 6==pad: pad = 0 461 strLen = len(bv)/6 462 aisStrLst = [] 463 464 if pad!=0: 465 if doPadding: 466 #print 'FIX: check padding to make sure it works right' 467 bv = bv + BitVector(size=pad) 468 else: 469 print 'ERROR: What are you doing with a non-align entity? Let me pad it!' 470 assert False 471 472 #else: # No pad needed 473 for i in range(strLen): 474 start = i*6 475 end = (i+1)*6 476 val = int(bv[start:end]) 477 #assert(0 <= val) 478 #assert(64 >= val) 479 c = encode[val] 480 aisStrLst.append(c) 481 482 aisStr = ''.join(aisStrLst) 483 484 return aisStr, pad
485 486
487 -def stuffBits(bv):
488 """Apply bit stuffing - add extra bytes to long sequences 489 490 @param bv: bits that may need padding 491 @type bv: BitVector 492 @return: new bits, possibly longer 493 @rtype: BitVector 494 495 @see: unstuffBits 496 497 @todo: Add a nice description of how bit stuffing works 498 @todo: Actually write the code 499 """ 500 assert False
501
502 -def unstuffBits(bv):
503 """Undo bit stuffing - remove extra bytes to long sequences 504 505 @param bv: bits that may have padding 506 @type bv: BitVector 507 @return: new bits, possibly longer 508 @rtype: BitVector 509 510 @todo: Actually write the code 511 @see: stuffBits 512 """ 513 assert False
514 515 if __name__ == '__main__': 516 from optparse import OptionParser 517 parser = OptionParser(usage="%prog [options]",version="%prog "+__version__) 518 parser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true', 519 help='run the documentation tests') 520 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 521 help='Make the test output verbose') 522 (options,args) = parser.parse_args() 523 524 success=True 525 526 if options.doctest: 527 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 528 sys.argv= [sys.argv[0]] 529 if options.verbose: sys.argv.append('-v') 530 import doctest 531 numfail,numtests=doctest.testmod() 532 if numfail==0: print 'ok' 533 else: 534 print 'FAILED' 535 success=False 536 537 if not success: 538 sys.exit('Something Failed') 539