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 @bug: May have bite order backwards 49 @type floatval: number 50 @param floatval: number to convert to bits 51 @rtype: BitVector 52 @return: 32 bits 53 @todo: Is there a faster way to do this? 54 @see: U{struct module<http://www.python.org/doc/current/lib/module-struct.html>} 55 ''' 56 s = struct.pack('!f',floatval) # FIX: Is this the right bight order? Could easily be wrong!!!! 57 i = struct.unpack('!I',s)[0] 58 #print 'unpacked:',i 59 #return setBitVectorSize(BitVector(intVal=i),32) 60 61 # Old way... since BitVector can't encode large intVals (>2^31) 62 bvList = [] 63 for i in range(4): 64 bvList.append(setBitVectorSize(BitVector(intVal=ord(s[i])),8)) 65 return joinBV(bvList)
66
67 -def bitvec2float(bv):
68 ''' 69 Convert a 32 bit bitvector representing an IEEE float into a python float 70 @bug: May have bite order backwards 71 @type bv: BitVector 72 @param bv: 32 bits representing an IEEE float 73 @rtype: float 74 @return: the corresponing number 75 @see: U{struct module<http://www.python.org/doc/current/lib/module-struct.html>} 76 ''' 77 return struct.unpack('!f',chr(bv[0:8]) + chr(bv[8:16]) + chr(bv[16:24]) + chr(bv[24:32]))[0]
78 79
80 -def joinBV(bvSeq):
81 ''' 82 Combined a sequence of bit vectors into one large BitVector 83 @param bvSeq: sequence of bitvectors 84 @return: aggregated BitVector 85 @bug: replace with a faster algorithm! 86 ''' 87 bvTotal=BitVector(size=0) 88 for bv in bvSeq: 89 bvTotal = bvTotal + bv 90 91 return bvTotal
92
93 -def setBitVectorSize(bv,size=8):
94 """Pad a BitVector with 0's on the left until it is at least the size specified 95 96 @param bv: BitVector that needs to meet a minimim size 97 @type bv: BitVector 98 @param size: Minimum number of bits to make the new BitVector 99 @type size: int 100 @return: BitVector that is size bits or larger 101 @rtype: BitVector 102 103 @todo: What to do if the vector is larger than size? 104 """ 105 pad=BitVector(bitlist=[0]) 106 while len(bv)<size: bv = pad + bv 107 return bv
108 109
110 -def addone(bv):
111 ''' 112 Add one bit to a bit vector. Overflows are silently dropped. 113 114 >>> print addone(BitVector(bitstring='1100')) 115 1101 116 117 >>> print addone(BitVector(bitstring='1111')) 118 0000 119 120 @param bv: Add one to these bits 121 @type bv: BitVector 122 @return: Bits with one added 123 @rtype: BitVector 124 ''' 125 new = bv 126 r = range(1,len(bv)+1) 127 for i in r: 128 index = len(bv)-i 129 if 0==bv[index]: 130 new[index]=1 131 break 132 new[index]=0 133 return new
134
135 -def subone(bv):
136 ''' 137 Subtract one bit from a bit vector 138 139 >>> print subone(BitVector(bitstring='1111')) 140 1110 141 >>> print subone(BitVector(bitstring='0010')) 142 0001 143 >>> print subone(BitVector(bitstring='0000')) 144 1111 145 146 @param bv: Bits to add one bit to the right side 147 @type bv: BitVector 148 @rtype: BitVector 149 ''' 150 new = bv 151 r = range(1,len(bv)+1) 152 for i in r: 153 index = len(bv)-i 154 if 1==bv[index]: 155 new[index]=0 156 break 157 new[index]=1 158 return new
159 160
161 -def bvFromSignedInt(intVal,bitSize=None):
162 ''' 163 Create a twos complement BitVector from a signed integer. 164 165 Positives must have a '0' in the left hand position. 166 167 >>> print bvFromSignedInt(0,bitSize=4) 168 0000 169 >>> print bvFromSignedInt(1,bitSize=4) 170 0001 171 >>> print bvFromSignedInt(7,bitSize=4) 172 0111 173 174 Negative numbers must have a '1' in the left hand position. 175 176 >>> print bvFromSignedInt(-1,bitSize=4) 177 1111 178 >>> print bvFromSignedInt(-2,bitSize=4) 179 1110 180 >>> print bvFromSignedInt(-7,bitSize=4) 181 1001 182 183 @param bv: Bits to subtract one bit from the right side 184 @type bv: BitVector 185 @rtype: BitVector 186 ''' 187 bv = setBitVectorSize(BitVector(intVal=abs(intVal)),bitSize-1) 188 assert(bitSize-1==len(bv)) # FIX: what to do about overflow beyond bitSize? 189 if intVal>=0: 190 bv = BitVector(intVal=0) + bv 191 else: 192 bv = subone(bv) 193 bv = ~bv 194 bv = BitVector(intVal=1) + bv 195 return bv
196
197 -def signedIntFromBV(bv):
198 ''' 199 Interpret a bit vector as an signed integer. int(BitVector) 200 defaults to treating the bits as an unsigned int. Assumes twos 201 complement representation. 202 203 U{http://en.wikipedia.org/wiki/Twos_complement} 204 205 Positive values decode like so: 206 207 >>> signedIntFromBV(BitVector(bitstring='0000')) 208 0 209 >>> signedIntFromBV(BitVector(bitstring='0101')) 210 5 211 212 Here are some negative integer examples: 213 214 >>> signedIntFromBV(BitVector(bitstring='1111')) 215 -1 216 >>> signedIntFromBV(BitVector(bitstring='1110')) 217 -2 218 >>> signedIntFromBV(BitVector(bitstring='1010')) 219 -6 220 >>> signedIntFromBV(BitVector(bitstring='1001')) 221 -7 222 >>> signedIntFromBV(BitVector(bitstring='1000')) 223 -8 224 225 @param bv: Bits to treat as an signed int 226 @type bv: BitVector 227 @return: Signed integer 228 @rtype: int 229 230 @note: Does not know the difference between byte orders. 231 ''' 232 if 0==bv[0]: return int(bv) 233 # Nope, so it is negative 234 val = int(addone(~(bv[1:]))) 235 if 0 != val: return -val 236 return -(int(bv))
237 238 239
240 -def ais6chartobitvec(char6):
241 ''' 242 Create a 6 bit BitVector for a single character 243 244 >>> print int(ais6chartobitvec('0')) 245 0 246 >>> print int(ais6chartobitvec('1')) 247 1 248 >>> print int(ais6chartobitvec('9')) 249 9 250 >>> print int(ais6chartobitvec('<')) 251 12 252 >>> print int(ais6chartobitvec('=')) 253 13 254 >>> print int(ais6chartobitvec('@')) 255 16 256 >>> print int(ais6chartobitvec('A')) 257 17 258 259 >>> print int(ais6chartobitvec('O')) 260 31 261 >>> print int(ais6chartobitvec('P')) 262 32 263 >>> print int(ais6chartobitvec('Q')) 264 33 265 266 >>> print int(ais6chartobitvec('R')) 267 34 268 269 >>> print int(ais6chartobitvec('Z')) 270 34 271 >>> print int(ais6chartobitvec('a')) 272 41 273 >>> print int(ais6chartobitvec('w')) 274 63 275 >>> print ais6chartobitvec('w') 276 111111 277 278 x, y, and z will not appear. 279 280 @param char6: character of an AIS message where each character represents 6 bits 281 @type char6: str(1) 282 @return: Decoded bits for one character (does not know about padding) 283 @rtype: BitVector(6) 284 @bug: need to cut down the doctest here and copy all of the current one to tests/test_binary.py 285 ''' 286 c = ord(char6) 287 val = c - 48 288 if val>=40: val -= 8 289 if 0==val: return(BitVector(size=6)) 290 return setBitVectorSize(BitVector(intVal=val),6)
291 292 293
294 -def ais6tobitvecSLOW(str6):
295 """Convert an ITU AIS 6 bit string into a bit vector. Each character 296 represents 6 bits. 297 298 >>> print ais6tobitvecSLOW('6bF:Z') 299 000110101010010110001010100010 300 301 @note: If the original BitVector had ((len(bitvector) % 6 > 0), 302 then there will be pad bits in the str6. This function has no way 303 to know how many pad bits there are. 304 305 @bug: Need to add pad bit handling 306 307 @param str6: ASCII that as it appears in the NMEA string 308 @type str6: string 309 @return: decoded bits (not unstuffed... what do I mean by 310 unstuffed?). There may be pad bits at the tail to make this 6 bit 311 aligned. 312 @rtype: BitVector 313 """ 314 bvtotal = BitVector(size=0) 315 316 for c in str6: 317 c = ord(c) 318 val = c - 48 319 if val>=40: val -= 8 320 bv = None 321 #print 'slow: ',c,val 322 if 0==val: 323 bv = BitVector(size=6) 324 else: bv = setBitVectorSize(BitVector(intVal=val),6) 325 bvtotal += bv 326 return bvtotal
327 328 329 330
331 -def buildLookupTables():
332 ''' 333 @bug: rename the local encode/decode dictionaries so there is no shadowing 334 ''' 335 decode={} 336 encode={} 337 for i in range(127): 338 if i<48: continue 339 c = chr(i) 340 bv = ais6tobitvecSLOW(c) 341 val = int (bv) 342 if val>=64: continue 343 encode[val] = c 344 decode[c] = bv 345 #print i, val, bv, "'"+str(c)+"'" 346 return encode,decode
347 348 encode,decode=buildLookupTables() 349
350 -def ais6tobitvec(str6):
351 '''Convert an ITU AIS 6 bit string into a bit vector. Each character 352 represents 6 bits. This is the NMEA !AIVD[MO] message payload. 353 354 @note: If the original BitVector had ((len(bitvector) % 6 > 0), 355 then there will be pad bits in the str6. This function has no way 356 to know how many pad bits there are. 357 358 >>> print ais6tobitvec('6') 359 000110 360 361 >>> print ais6tobitvec('6b') 362 000110101010 363 364 >>> print ais6tobitvec('6bF:Z') 365 000110101010010110001010100010 366 367 @bug: Need to add pad bit handling 368 369 @param str6: ASCII that as it appears in the NMEA string 370 @type str6: string 371 @return: decoded bits (not unstuffed... what do I mean by 372 unstuffed?). There may be pad bits at the tail to make this 6 bit 373 aligned. 374 @rtype: BitVector 375 ''' 376 bvtotal = BitVector(size=6*len(str6)) 377 378 for pos in range(len(str6)): 379 bv = decode[str6[pos]] 380 start = pos*6 381 for i in range(6): 382 bvtotal[i+start] = bv[i] 383 return bvtotal
384
385 -def bitvectoais6(bv):
386 """Convert bit vector int an ITU AIS 6 bit string. Each character represents 6 bits 387 388 >>> print bitvectoais6(BitVector(bitstring='000110101010010110001010100010')) 389 ('6bF:Z', 0) 390 391 @param bv: message bits (must be already stuffed) 392 @type bv: BitVector 393 @return: str6 ASCII that as it appears in the NMEA string 394 @rtype: str, pad 395 396 @todo: make a test base for needing padding 397 @bug: handle case when padding needed 398 """ 399 #print "FIX: need to test" 400 pad = len(bv)%6 401 strLen = len(bv)/6 402 aisStrLst = [] 403 404 if pad!=0: 405 assert False 406 # FIX: how to pad on the right? 407 # only want to do this to the last character 408 else: # No pad needed 409 for i in range(strLen): 410 start = i*6 411 end = (i+1)*6 412 val = int(bv[start:end]) 413 assert(0 <= val) 414 assert(64 >= val) 415 c = encode[val] 416 aisStrLst.append(c) 417 418 aisStr = ''.join(aisStrLst) 419 420 return aisStr, pad
421 422
423 -def stuffBits(bv):
424 """Apply bit stuffing - add extra bytes to long sequences 425 426 @param bv: bits that may need padding 427 @type bv: BitVector 428 @return: new bits, possibly longer 429 @rtype: BitVector 430 431 @see: unstuffBits 432 433 @todo: Add a nice description of how bit stuffing works 434 @todo: Actually write the code 435 """ 436 assert False
437
438 -def unstuffBits(bv):
439 """Undo bit stuffing - remove extra bytes to long sequences 440 441 @param bv: bits that may have padding 442 @type bv: BitVector 443 @return: new bits, possibly longer 444 @rtype: BitVector 445 446 @todo: Actually write the code 447 @see: stuffBits 448 """ 449 assert False
450 451 if __name__ == '__main__': 452 from optparse import OptionParser 453 myparser = OptionParser(usage="%prog [options]",version="%prog "+__version__) 454 myparser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true', 455 help='run the documentation tests') 456 # verbosity.addVerbosityOptions(myparser) 457 (options,args) = myparser.parse_args() 458 459 success=True 460 461 if options.doctest: 462 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 463 sys.argv= [sys.argv[0]] 464 # if options.verbosity>=VERBOSE: sys.argv.append('-v') 465 import doctest 466 numfail,numtests=doctest.testmod() 467 if numfail==0: print 'ok' 468 else: 469 print 'FAILED' 470 success=False 471 472 if not success: 473 sys.exit('Something Failed') 474