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 bv: Bits to subtract one bit from the right side 199 @type bv: BitVector 200 @rtype: BitVector 201 ''' 202 bv = None 203 if None==bitSize: 204 bv = BitVector(intVal=abs(intVal)) 205 else: 206 bv = setBitVectorSize(BitVector(intVal=abs(intVal)),bitSize-1) 207 if (bitSize-1!=len(bv)): 208 print 'ERROR: bitsize not right' 209 print ' ',bitSize-1,len(bv) 210 assert(False) 211 if intVal>=0: 212 bv = BitVector(intVal=0) + bv 213 else: 214 bv = subone(bv) 215 bv = ~bv 216 bv = BitVector(intVal=1) + bv 217 return bv
218
219 -def signedIntFromBV(bv):
220 ''' 221 Interpret a bit vector as an signed integer. int(BitVector) 222 defaults to treating the bits as an unsigned int. Assumes twos 223 complement representation. 224 225 U{http://en.wikipedia.org/wiki/Twos_complement} 226 227 Positive values decode like so: 228 229 >>> signedIntFromBV(BitVector(bitstring='0000')) 230 0 231 >>> signedIntFromBV(BitVector(bitstring='0101')) 232 5 233 234 Here are some negative integer examples: 235 236 >>> signedIntFromBV(BitVector(bitstring='1111')) 237 -1 238 >>> signedIntFromBV(BitVector(bitstring='1110')) 239 -2 240 >>> signedIntFromBV(BitVector(bitstring='1010')) 241 -6 242 >>> signedIntFromBV(BitVector(bitstring='1001')) 243 -7 244 >>> signedIntFromBV(BitVector(bitstring='1000')) 245 -8 246 247 @param bv: Bits to treat as an signed int 248 @type bv: BitVector 249 @return: Signed integer 250 @rtype: int 251 252 @note: Does not know the difference between byte orders. 253 ''' 254 if 0==bv[0]: return int(bv) 255 # Nope, so it is negative 256 val = int(addone(~(bv[1:]))) 257 if 0 != val: return -val 258 return -(int(bv))
259 260 261
262 -def ais6chartobitvec(char6):
263 ''' 264 Create a 6 bit BitVector for a single character 265 266 >>> print int(ais6chartobitvec('0')) 267 0 268 >>> print int(ais6chartobitvec('1')) 269 1 270 >>> print int(ais6chartobitvec('9')) 271 9 272 >>> print int(ais6chartobitvec('<')) 273 12 274 >>> print int(ais6chartobitvec('=')) 275 13 276 >>> print int(ais6chartobitvec('@')) 277 16 278 >>> print int(ais6chartobitvec('A')) 279 17 280 281 >>> print int(ais6chartobitvec('O')) 282 31 283 >>> print int(ais6chartobitvec('P')) 284 32 285 >>> print int(ais6chartobitvec('Q')) 286 33 287 288 >>> print int(ais6chartobitvec('R')) 289 34 290 291 >>> print int(ais6chartobitvec('Z')) 292 34 293 >>> print int(ais6chartobitvec('a')) 294 41 295 >>> print int(ais6chartobitvec('w')) 296 63 297 >>> print ais6chartobitvec('w') 298 111111 299 300 x, y, and z will not appear. 301 302 @param char6: character of an AIS message where each character represents 6 bits 303 @type char6: str(1) 304 @return: Decoded bits for one character (does not know about padding) 305 @rtype: BitVector(6) 306 @bug: need to cut down the doctest here and copy all of the current one to tests/test_binary.py 307 ''' 308 c = ord(char6) 309 val = c - 48 310 if val>=40: val -= 8 311 if 0==val: return(BitVector(size=6)) 312 return setBitVectorSize(BitVector(intVal=val),6)
313 314 315
316 -def ais6tobitvecSLOW(str6):
317 """Convert an ITU AIS 6 bit string into a bit vector. Each character 318 represents 6 bits. 319 320 >>> print ais6tobitvecSLOW('6bF:Z') 321 000110101010010110001010100010 322 323 @note: If the original BitVector had ((len(bitvector) % 6 > 0), 324 then there will be pad bits in the str6. This function has no way 325 to know how many pad bits there are. 326 327 @bug: Need to add pad bit handling 328 329 @param str6: ASCII that as it appears in the NMEA string 330 @type str6: string 331 @return: decoded bits (not unstuffed... what do I mean by 332 unstuffed?). There may be pad bits at the tail to make this 6 bit 333 aligned. 334 @rtype: BitVector 335 """ 336 bvtotal = BitVector(size=0) 337 338 for c in str6: 339 c = ord(c) 340 val = c - 48 341 if val>=40: val -= 8 342 bv = None 343 #print 'slow: ',c,val 344 if 0==val: 345 bv = BitVector(size=6) 346 else: 347 bv = setBitVectorSize(BitVector(intVal=val),6) 348 #bv = BitVector(intVal=val,size=6) # FIX: I thought this would work, but it is more than 6 bits? 349 bvtotal += bv 350 return bvtotal
351 352 353 354
355 -def buildLookupTables():
356 ''' 357 @bug: rename the local encode/decode dictionaries so there is no shadowing 358 ''' 359 decode={} 360 encode={} 361 for i in range(127): 362 # for i in range(64): # FIX: is this the right range? 363 if i<48: continue 364 c = chr(i) 365 bv = ais6tobitvecSLOW(c) 366 val = int (bv) 367 if val>=64: continue 368 encode[val] = c 369 decode[c] = bv 370 #print i, val, bv, "'"+str(c)+"'" 371 return encode,decode
372 373 encode,decode=buildLookupTables() 374 375 #print 'encode:' 376 #for key in encode: 377 # print key,':',encode[key] 378 #print 'encode:' 379 #for key in decode: 380 # print key,':',decode[key]
381 -def ais6tobitvec(str6):
382 '''Convert an ITU AIS 6 bit string into a bit vector. Each character 383 represents 6 bits. This is the NMEA !AIVD[MO] message payload. 384 385 @note: If the original BitVector had ((len(bitvector) % 6 > 0), 386 then there will be pad bits in the str6. This function has no way 387 to know how many pad bits there are. 388 389 >>> print ais6tobitvec('6') 390 000110 391 392 >>> print ais6tobitvec('6b') 393 000110101010 394 395 >>> print ais6tobitvec('6bF:Z') 396 000110101010010110001010100010 397 398 @bug: Need to add pad bit handling 399 400 @param str6: ASCII that as it appears in the NMEA string 401 @type str6: string 402 @return: decoded bits (not unstuffed... what do I mean by 403 unstuffed?). There may be pad bits at the tail to make this 6 bit 404 aligned. 405 @rtype: BitVector 406 ''' 407 bvtotal = BitVector(size=6*len(str6)) 408 409 for pos in range(len(str6)): 410 bv = decode[str6[pos]] 411 start = pos*6 412 for i in range(6): 413 bvtotal[i+start] = bv[i] 414 return bvtotal
415
416 -def getPadding(bv):
417 ''' 418 Return the number of bits that need to be padded for a bit vector 419 420 >>> getPadding(BitVector(bitstring='0')) 421 5 422 >>> getPadding(BitVector(bitstring='01')) 423 4 424 >>> getPadding(BitVector(bitstring='010')) 425 3 426 >>> getPadding(BitVector(bitstring='0101')) 427 2 428 >>> getPadding(BitVector(bitstring='01010')) 429 1 430 >>> getPadding(BitVector(bitstring='010101')) 431 0 432 >>> getPadding(BitVector(bitstring='0101010')) 433 5 434 @rtype: int 435 @return: number of pad bits required for this bitvector to make it bit aligned to the ais nmea string 436 ''' 437 pad = 6-(len(bv)%6) 438 if 6==pad: pad = 0 439 return pad
440
441 -def bitvectoais6(bv,doPadding=True):
442 """Convert bit vector int an ITU AIS 6 bit string. Each character represents 6 bits 443 444 >>> print bitvectoais6(BitVector(bitstring='000110101010010110001010100010')) 445 ('6bF:Z', 0) 446 447 @param bv: message bits (must be already stuffed) 448 @type bv: BitVector 449 @return: str6 ASCII that as it appears in the NMEA string 450 @rtype: str, pad 451 452 @todo: make a test base for needing padding 453 @bug: handle case when padding needed 454 """ 455 #print "FIX: need to test" 456 pad = 6-(len(bv)%6) 457 if 6==pad: pad = 0 458 strLen = len(bv)/6 459 aisStrLst = [] 460 461 if pad!=0: 462 if doPadding: 463 #print 'FIX: check padding to make sure it works right' 464 bv = bv + BitVector(size=pad) 465 else: 466 print 'ERROR: What are you doing with a non-align entity? Let me pad it!' 467 assert False 468 469 #else: # No pad needed 470 for i in range(strLen): 471 start = i*6 472 end = (i+1)*6 473 val = int(bv[start:end]) 474 #assert(0 <= val) 475 #assert(64 >= val) 476 c = encode[val] 477 aisStrLst.append(c) 478 479 aisStr = ''.join(aisStrLst) 480 481 return aisStr, pad
482 483
484 -def stuffBits(bv):
485 """Apply bit stuffing - add extra bytes to long sequences 486 487 @param bv: bits that may need padding 488 @type bv: BitVector 489 @return: new bits, possibly longer 490 @rtype: BitVector 491 492 @see: unstuffBits 493 494 @todo: Add a nice description of how bit stuffing works 495 @todo: Actually write the code 496 """ 497 assert False
498
499 -def unstuffBits(bv):
500 """Undo bit stuffing - remove extra bytes to long sequences 501 502 @param bv: bits that may have padding 503 @type bv: BitVector 504 @return: new bits, possibly longer 505 @rtype: BitVector 506 507 @todo: Actually write the code 508 @see: stuffBits 509 """ 510 assert False
511 512 if __name__ == '__main__': 513 from optparse import OptionParser 514 parser = OptionParser(usage="%prog [options]",version="%prog "+__version__) 515 parser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true', 516 help='run the documentation tests') 517 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 518 help='Make the test output verbose') 519 (options,args) = parser.parse_args() 520 521 success=True 522 523 if options.doctest: 524 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 525 sys.argv= [sys.argv[0]] 526 if options.verbose: sys.argv.append('-v') 527 import doctest 528 numfail,numtests=doctest.testmod() 529 if numfail==0: print 'ok' 530 else: 531 print 'FAILED' 532 success=False 533 534 if not success: 535 sys.exit('Something Failed') 536