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 = None
203 if None==bitSize:
204 bv = BitVector(intVal=abs(intVal))
205 else:
206 bv = setBitVectorSize(BitVector(intVal=abs(intVal)),bitSize-1)
207 if (bitSize-1!=len(bv)):
208 print 'ERROR: bitsize not right'
209 print ' ',bitSize-1,len(bv)
210 assert(False)
211 if intVal>=0:
212 bv = BitVector(intVal=0) + bv
213 else:
214 bv = subone(bv)
215 bv = ~bv
216 bv = BitVector(intVal=1) + bv
217 return bv
218
220 '''
221 Interpret a bit vector as an signed integer. int(BitVector)
222 defaults to treating the bits as an unsigned int. Assumes twos
223 complement representation.
224
225 U{http://en.wikipedia.org/wiki/Twos_complement}
226
227 Positive values decode like so:
228
229 >>> signedIntFromBV(BitVector(bitstring='0000'))
230 0
231 >>> signedIntFromBV(BitVector(bitstring='0101'))
232 5
233
234 Here are some negative integer examples:
235
236 >>> signedIntFromBV(BitVector(bitstring='1111'))
237 -1
238 >>> signedIntFromBV(BitVector(bitstring='1110'))
239 -2
240 >>> signedIntFromBV(BitVector(bitstring='1010'))
241 -6
242 >>> signedIntFromBV(BitVector(bitstring='1001'))
243 -7
244 >>> signedIntFromBV(BitVector(bitstring='1000'))
245 -8
246
247 @param bv: Bits to treat as an signed int
248 @type bv: BitVector
249 @return: Signed integer
250 @rtype: int
251
252 @note: Does not know the difference between byte orders.
253 '''
254 if 0==bv[0]: return int(bv)
255
256 val = int(addone(~(bv[1:])))
257 if 0 != val: return -val
258 return -(int(bv))
259
260
261
263 '''
264 Create a 6 bit BitVector for a single character
265
266 >>> print int(ais6chartobitvec('0'))
267 0
268 >>> print int(ais6chartobitvec('1'))
269 1
270 >>> print int(ais6chartobitvec('9'))
271 9
272 >>> print int(ais6chartobitvec('<'))
273 12
274 >>> print int(ais6chartobitvec('='))
275 13
276 >>> print int(ais6chartobitvec('@'))
277 16
278 >>> print int(ais6chartobitvec('A'))
279 17
280
281 >>> print int(ais6chartobitvec('O'))
282 31
283 >>> print int(ais6chartobitvec('P'))
284 32
285 >>> print int(ais6chartobitvec('Q'))
286 33
287
288 >>> print int(ais6chartobitvec('R'))
289 34
290
291 >>> print int(ais6chartobitvec('Z'))
292 34
293 >>> print int(ais6chartobitvec('a'))
294 41
295 >>> print int(ais6chartobitvec('w'))
296 63
297 >>> print ais6chartobitvec('w')
298 111111
299
300 x, y, and z will not appear.
301
302 @param char6: character of an AIS message where each character represents 6 bits
303 @type char6: str(1)
304 @return: Decoded bits for one character (does not know about padding)
305 @rtype: BitVector(6)
306 @bug: need to cut down the doctest here and copy all of the current one to tests/test_binary.py
307 '''
308 c = ord(char6)
309 val = c - 48
310 if val>=40: val -= 8
311 if 0==val: return(BitVector(size=6))
312 return setBitVectorSize(BitVector(intVal=val),6)
313
314
315
317 """Convert an ITU AIS 6 bit string into a bit vector. Each character
318 represents 6 bits.
319
320 >>> print ais6tobitvecSLOW('6bF:Z')
321 000110101010010110001010100010
322
323 @note: If the original BitVector had ((len(bitvector) % 6 > 0),
324 then there will be pad bits in the str6. This function has no way
325 to know how many pad bits there are.
326
327 @bug: Need to add pad bit handling
328
329 @param str6: ASCII that as it appears in the NMEA string
330 @type str6: string
331 @return: decoded bits (not unstuffed... what do I mean by
332 unstuffed?). There may be pad bits at the tail to make this 6 bit
333 aligned.
334 @rtype: BitVector
335 """
336 bvtotal = BitVector(size=0)
337
338 for c in str6:
339 c = ord(c)
340 val = c - 48
341 if val>=40: val -= 8
342 bv = None
343
344 if 0==val:
345 bv = BitVector(size=6)
346 else:
347 bv = setBitVectorSize(BitVector(intVal=val),6)
348
349 bvtotal += bv
350 return bvtotal
351
352
353
354
356 '''
357 @bug: rename the local encode/decode dictionaries so there is no shadowing
358 '''
359 decode={}
360 encode={}
361 for i in range(127):
362
363 if i<48: continue
364 c = chr(i)
365 bv = ais6tobitvecSLOW(c)
366 val = int (bv)
367 if val>=64: continue
368 encode[val] = c
369 decode[c] = bv
370
371 return encode,decode
372
373 encode,decode=buildLookupTables()
374
375
376
377
378
379
380
382 '''Convert an ITU AIS 6 bit string into a bit vector. Each character
383 represents 6 bits. This is the NMEA !AIVD[MO] message payload.
384
385 @note: If the original BitVector had ((len(bitvector) % 6 > 0),
386 then there will be pad bits in the str6. This function has no way
387 to know how many pad bits there are.
388
389 >>> print ais6tobitvec('6')
390 000110
391
392 >>> print ais6tobitvec('6b')
393 000110101010
394
395 >>> print ais6tobitvec('6bF:Z')
396 000110101010010110001010100010
397
398 @bug: Need to add pad bit handling
399
400 @param str6: ASCII that as it appears in the NMEA string
401 @type str6: string
402 @return: decoded bits (not unstuffed... what do I mean by
403 unstuffed?). There may be pad bits at the tail to make this 6 bit
404 aligned.
405 @rtype: BitVector
406 '''
407 bvtotal = BitVector(size=6*len(str6))
408
409 for pos in range(len(str6)):
410 bv = decode[str6[pos]]
411 start = pos*6
412 for i in range(6):
413 bvtotal[i+start] = bv[i]
414 return bvtotal
415
417 '''
418 Return the number of bits that need to be padded for a bit vector
419
420 >>> getPadding(BitVector(bitstring='0'))
421 5
422 >>> getPadding(BitVector(bitstring='01'))
423 4
424 >>> getPadding(BitVector(bitstring='010'))
425 3
426 >>> getPadding(BitVector(bitstring='0101'))
427 2
428 >>> getPadding(BitVector(bitstring='01010'))
429 1
430 >>> getPadding(BitVector(bitstring='010101'))
431 0
432 >>> getPadding(BitVector(bitstring='0101010'))
433 5
434 @rtype: int
435 @return: number of pad bits required for this bitvector to make it bit aligned to the ais nmea string
436 '''
437 pad = 6-(len(bv)%6)
438 if 6==pad: pad = 0
439 return pad
440
442 """Convert bit vector int an ITU AIS 6 bit string. Each character represents 6 bits
443
444 >>> print bitvectoais6(BitVector(bitstring='000110101010010110001010100010'))
445 ('6bF:Z', 0)
446
447 @param bv: message bits (must be already stuffed)
448 @type bv: BitVector
449 @return: str6 ASCII that as it appears in the NMEA string
450 @rtype: str, pad
451
452 @todo: make a test base for needing padding
453 @bug: handle case when padding needed
454 """
455
456 pad = 6-(len(bv)%6)
457 if 6==pad: pad = 0
458 strLen = len(bv)/6
459 aisStrLst = []
460
461 if pad!=0:
462 if doPadding:
463
464 bv = bv + BitVector(size=pad)
465 else:
466 print 'ERROR: What are you doing with a non-align entity? Let me pad it!'
467 assert False
468
469
470 for i in range(strLen):
471 start = i*6
472 end = (i+1)*6
473 val = int(bv[start:end])
474
475
476 c = encode[val]
477 aisStrLst.append(c)
478
479 aisStr = ''.join(aisStrLst)
480
481 return aisStr, pad
482
483
485 """Apply bit stuffing - add extra bytes to long sequences
486
487 @param bv: bits that may need padding
488 @type bv: BitVector
489 @return: new bits, possibly longer
490 @rtype: BitVector
491
492 @see: unstuffBits
493
494 @todo: Add a nice description of how bit stuffing works
495 @todo: Actually write the code
496 """
497 assert False
498
500 """Undo bit stuffing - remove extra bytes to long sequences
501
502 @param bv: bits that may have padding
503 @type bv: BitVector
504 @return: new bits, possibly longer
505 @rtype: BitVector
506
507 @todo: Actually write the code
508 @see: stuffBits
509 """
510 assert False
511
512 if __name__ == '__main__':
513 from optparse import OptionParser
514 parser = OptionParser(usage="%prog [options]",version="%prog "+__version__)
515 parser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true',
516 help='run the documentation tests')
517 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
518 help='Make the test output verbose')
519 (options,args) = parser.parse_args()
520
521 success=True
522
523 if options.doctest:
524 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
525 sys.argv= [sys.argv[0]]
526 if options.verbose: sys.argv.append('-v')
527 import doctest
528 numfail,numtests=doctest.testmod()
529 if numfail==0: print 'ok'
530 else:
531 print 'FAILED'
532 success=False
533
534 if not success:
535 sys.exit('Something Failed')
536