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