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