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 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))
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
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
249 val = int(addone(~(bv[1:])))
250 if 0 != val: return -val
251 return -(int(bv))
252
253
254
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
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
337 if 0==val:
338 bv = BitVector(size=6)
339 else:
340 bv = setBitVectorSize(BitVector(intVal=val),6)
341
342 bvtotal += bv
343 return bvtotal
344
345
346
347
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
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
364 return encode,decode
365
366 encode,decode=buildLookupTables()
367
368
369
370
371
372
373
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
410 '''
411 Return the number of bits that need to be padded for a bit vector
412
413 >>> getPadding(BitVector(bitstring='0'))
414 5
415 >>> getPadding(BitVector(bitstring='01'))
416 4
417 >>> getPadding(BitVector(bitstring='010'))
418 3
419 >>> getPadding(BitVector(bitstring='0101'))
420 2
421 >>> getPadding(BitVector(bitstring='01010'))
422 1
423 >>> getPadding(BitVector(bitstring='010101'))
424 0
425 >>> getPadding(BitVector(bitstring='0101010'))
426 5
427 @rtype: int
428 @return: number of pad bits required for this bitvector to make it bit aligned to the ais nmea string
429 '''
430 pad = 6-(len(bv)%6)
431 if 6==pad: pad = 0
432 return pad
433
435 """Convert bit vector int an ITU AIS 6 bit string. Each character represents 6 bits
436
437 >>> print bitvectoais6(BitVector(bitstring='000110101010010110001010100010'))
438 ('6bF:Z', 0)
439
440 @param bv: message bits (must be already stuffed)
441 @type bv: BitVector
442 @return: str6 ASCII that as it appears in the NMEA string
443 @rtype: str, pad
444
445 @todo: make a test base for needing padding
446 @bug: handle case when padding needed
447 """
448
449 pad = 6-(len(bv)%6)
450 if 6==pad: pad = 0
451 strLen = len(bv)/6
452 aisStrLst = []
453
454 if pad!=0:
455 if doPadding:
456
457 bv = bv + BitVector(size=pad)
458 else:
459 print 'ERROR: What are you doing with a non-align entity? Let me pad it!'
460 assert False
461
462
463 for i in range(strLen):
464 start = i*6
465 end = (i+1)*6
466 val = int(bv[start:end])
467
468
469 c = encode[val]
470 aisStrLst.append(c)
471
472 aisStr = ''.join(aisStrLst)
473
474 return aisStr, pad
475
476
478 """Apply bit stuffing - add extra bytes to long sequences
479
480 @param bv: bits that may need padding
481 @type bv: BitVector
482 @return: new bits, possibly longer
483 @rtype: BitVector
484
485 @see: unstuffBits
486
487 @todo: Add a nice description of how bit stuffing works
488 @todo: Actually write the code
489 """
490 assert False
491
493 """Undo bit stuffing - remove extra bytes to long sequences
494
495 @param bv: bits that may have padding
496 @type bv: BitVector
497 @return: new bits, possibly longer
498 @rtype: BitVector
499
500 @todo: Actually write the code
501 @see: stuffBits
502 """
503 assert False
504
505 if __name__ == '__main__':
506 from optparse import OptionParser
507 parser = OptionParser(usage="%prog [options]",version="%prog "+__version__)
508 parser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true',
509 help='run the documentation tests')
510 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
511 help='Make the test output verbose')
512 (options,args) = parser.parse_args()
513
514 success=True
515
516 if options.doctest:
517 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
518 sys.argv= [sys.argv[0]]
519 if options.verbose: sys.argv.append('-v')
520 import doctest
521 numfail,numtests=doctest.testmod()
522 if numfail==0: print 'ok'
523 else:
524 print 'FAILED'
525 success=False
526
527 if not success:
528 sys.exit('Something Failed')
529