1   
  2   
  3  __version__ = '$Revision: 2075 $'.split()[1]  
  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   
 39  import sys 
 40   
 41   
 42  from BitVector import BitVector 
 43  import struct 
 44   
 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)   
 69      i = struct.unpack('!I',s)[0] 
 70       
 71       
 72   
 73       
 74       
 75      bvList = [] 
 76      for i in range(4): 
 77          bv1 = setBitVectorSize(BitVector(intVal=ord(s[i])),8) 
 78           
 79          bvList.append(bv1) 
 80      return joinBV(bvList) 
  81   
 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   
 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   
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   
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   
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   
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       
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       
259      val = int(addone(~(bv[1:]))) 
260      if 0 != val: return -val 
261      return -(int(bv)) 
 262   
263   
264   
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   
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           
347          if 0==val:  
348              bv = BitVector(size=6) 
349          else:  
350              bv = setBitVectorSize(BitVector(intVal=val),6) 
351               
352          bvtotal += bv 
353      return bvtotal 
 354   
355   
356   
357   
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   
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           
374      return encode,decode 
 375   
376  encode,decode=buildLookupTables() 
377   
378   
379   
380   
381   
382   
383   
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   
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   
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       
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               
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       
473      for i in range(strLen): 
474          start = i*6 
475          end = (i+1)*6 
476          val = int(bv[start:end]) 
477           
478           
479          c = encode[val] 
480          aisStrLst.append(c) 
481       
482      aisStr = ''.join(aisStrLst) 
483      
484      return aisStr, pad 
 485   
486   
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   
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