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 """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
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
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
438 for i in range(strLen):
439 start = i*6
440 end = (i+1)*6
441 val = int(bv[start:end])
442
443
444 c = encode[val]
445 aisStrLst.append(c)
446
447 aisStr = ''.join(aisStrLst)
448
449 return aisStr, pad
450
451
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
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