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 @bug: May have bite order backwards
49 @type floatval: number
50 @param floatval: number to convert to bits
51 @rtype: BitVector
52 @return: 32 bits
53 @todo: Is there a faster way to do this?
54 @see: U{struct module<http://www.python.org/doc/current/lib/module-struct.html>}
55 '''
56 s = struct.pack('!f',floatval)
57 i = struct.unpack('!I',s)[0]
58
59
60
61
62 bvList = []
63 for i in range(4):
64 bvList.append(setBitVectorSize(BitVector(intVal=ord(s[i])),8))
65 return joinBV(bvList)
66
68 '''
69 Convert a 32 bit bitvector representing an IEEE float into a python float
70 @bug: May have bite order backwards
71 @type bv: BitVector
72 @param bv: 32 bits representing an IEEE float
73 @rtype: float
74 @return: the corresponing number
75 @see: U{struct module<http://www.python.org/doc/current/lib/module-struct.html>}
76 '''
77 return struct.unpack('!f',chr(bv[0:8]) + chr(bv[8:16]) + chr(bv[16:24]) + chr(bv[24:32]))[0]
78
79
81 '''
82 Combined a sequence of bit vectors into one large BitVector
83 @param bvSeq: sequence of bitvectors
84 @return: aggregated BitVector
85 @bug: replace with a faster algorithm!
86 '''
87 bvTotal=BitVector(size=0)
88 for bv in bvSeq:
89 bvTotal = bvTotal + bv
90
91 return bvTotal
92
94 """Pad a BitVector with 0's on the left until it is at least the size specified
95
96 @param bv: BitVector that needs to meet a minimim size
97 @type bv: BitVector
98 @param size: Minimum number of bits to make the new BitVector
99 @type size: int
100 @return: BitVector that is size bits or larger
101 @rtype: BitVector
102
103 @todo: What to do if the vector is larger than size?
104 """
105 pad=BitVector(bitlist=[0])
106 while len(bv)<size: bv = pad + bv
107 return bv
108
109
111 '''
112 Add one bit to a bit vector. Overflows are silently dropped.
113
114 >>> print addone(BitVector(bitstring='1100'))
115 1101
116
117 >>> print addone(BitVector(bitstring='1111'))
118 0000
119
120 @param bv: Add one to these bits
121 @type bv: BitVector
122 @return: Bits with one added
123 @rtype: BitVector
124 '''
125 new = bv
126 r = range(1,len(bv)+1)
127 for i in r:
128 index = len(bv)-i
129 if 0==bv[index]:
130 new[index]=1
131 break
132 new[index]=0
133 return new
134
136 '''
137 Subtract one bit from a bit vector
138
139 >>> print subone(BitVector(bitstring='1111'))
140 1110
141 >>> print subone(BitVector(bitstring='0010'))
142 0001
143 >>> print subone(BitVector(bitstring='0000'))
144 1111
145
146 @param bv: Bits to add one bit to the right side
147 @type bv: BitVector
148 @rtype: BitVector
149 '''
150 new = bv
151 r = range(1,len(bv)+1)
152 for i in r:
153 index = len(bv)-i
154 if 1==bv[index]:
155 new[index]=0
156 break
157 new[index]=1
158 return new
159
160
162 '''
163 Create a twos complement BitVector from a signed integer.
164
165 Positives must have a '0' in the left hand position.
166
167 >>> print bvFromSignedInt(0,bitSize=4)
168 0000
169 >>> print bvFromSignedInt(1,bitSize=4)
170 0001
171 >>> print bvFromSignedInt(7,bitSize=4)
172 0111
173
174 Negative numbers must have a '1' in the left hand position.
175
176 >>> print bvFromSignedInt(-1,bitSize=4)
177 1111
178 >>> print bvFromSignedInt(-2,bitSize=4)
179 1110
180 >>> print bvFromSignedInt(-7,bitSize=4)
181 1001
182
183 @param bv: Bits to subtract one bit from the right side
184 @type bv: BitVector
185 @rtype: BitVector
186 '''
187 bv = setBitVectorSize(BitVector(intVal=abs(intVal)),bitSize-1)
188 assert(bitSize-1==len(bv))
189 if intVal>=0:
190 bv = BitVector(intVal=0) + bv
191 else:
192 bv = subone(bv)
193 bv = ~bv
194 bv = BitVector(intVal=1) + bv
195 return bv
196
198 '''
199 Interpret a bit vector as an signed integer. int(BitVector)
200 defaults to treating the bits as an unsigned int. Assumes twos
201 complement representation.
202
203 U{http://en.wikipedia.org/wiki/Twos_complement}
204
205 Positive values decode like so:
206
207 >>> signedIntFromBV(BitVector(bitstring='0000'))
208 0
209 >>> signedIntFromBV(BitVector(bitstring='0101'))
210 5
211
212 Here are some negative integer examples:
213
214 >>> signedIntFromBV(BitVector(bitstring='1111'))
215 -1
216 >>> signedIntFromBV(BitVector(bitstring='1110'))
217 -2
218 >>> signedIntFromBV(BitVector(bitstring='1010'))
219 -6
220 >>> signedIntFromBV(BitVector(bitstring='1001'))
221 -7
222 >>> signedIntFromBV(BitVector(bitstring='1000'))
223 -8
224
225 @param bv: Bits to treat as an signed int
226 @type bv: BitVector
227 @return: Signed integer
228 @rtype: int
229
230 @note: Does not know the difference between byte orders.
231 '''
232 if 0==bv[0]: return int(bv)
233
234 val = int(addone(~(bv[1:])))
235 if 0 != val: return -val
236 return -(int(bv))
237
238
239
241 '''
242 Create a 6 bit BitVector for a single character
243
244 >>> print int(ais6chartobitvec('0'))
245 0
246 >>> print int(ais6chartobitvec('1'))
247 1
248 >>> print int(ais6chartobitvec('9'))
249 9
250 >>> print int(ais6chartobitvec('<'))
251 12
252 >>> print int(ais6chartobitvec('='))
253 13
254 >>> print int(ais6chartobitvec('@'))
255 16
256 >>> print int(ais6chartobitvec('A'))
257 17
258
259 >>> print int(ais6chartobitvec('O'))
260 31
261 >>> print int(ais6chartobitvec('P'))
262 32
263 >>> print int(ais6chartobitvec('Q'))
264 33
265
266 >>> print int(ais6chartobitvec('R'))
267 34
268
269 >>> print int(ais6chartobitvec('Z'))
270 34
271 >>> print int(ais6chartobitvec('a'))
272 41
273 >>> print int(ais6chartobitvec('w'))
274 63
275 >>> print ais6chartobitvec('w')
276 111111
277
278 x, y, and z will not appear.
279
280 @param char6: character of an AIS message where each character represents 6 bits
281 @type char6: str(1)
282 @return: Decoded bits for one character (does not know about padding)
283 @rtype: BitVector(6)
284 @bug: need to cut down the doctest here and copy all of the current one to tests/test_binary.py
285 '''
286 c = ord(char6)
287 val = c - 48
288 if val>=40: val -= 8
289 if 0==val: return(BitVector(size=6))
290 return setBitVectorSize(BitVector(intVal=val),6)
291
292
293
295 """Convert an ITU AIS 6 bit string into a bit vector. Each character
296 represents 6 bits.
297
298 >>> print ais6tobitvecSLOW('6bF:Z')
299 000110101010010110001010100010
300
301 @note: If the original BitVector had ((len(bitvector) % 6 > 0),
302 then there will be pad bits in the str6. This function has no way
303 to know how many pad bits there are.
304
305 @bug: Need to add pad bit handling
306
307 @param str6: ASCII that as it appears in the NMEA string
308 @type str6: string
309 @return: decoded bits (not unstuffed... what do I mean by
310 unstuffed?). There may be pad bits at the tail to make this 6 bit
311 aligned.
312 @rtype: BitVector
313 """
314 bvtotal = BitVector(size=0)
315
316 for c in str6:
317 c = ord(c)
318 val = c - 48
319 if val>=40: val -= 8
320 bv = None
321
322 if 0==val:
323 bv = BitVector(size=6)
324 else: bv = setBitVectorSize(BitVector(intVal=val),6)
325 bvtotal += bv
326 return bvtotal
327
328
329
330
332 '''
333 @bug: rename the local encode/decode dictionaries so there is no shadowing
334 '''
335 decode={}
336 encode={}
337 for i in range(127):
338 if i<48: continue
339 c = chr(i)
340 bv = ais6tobitvecSLOW(c)
341 val = int (bv)
342 if val>=64: continue
343 encode[val] = c
344 decode[c] = bv
345
346 return encode,decode
347
348 encode,decode=buildLookupTables()
349
351 '''Convert an ITU AIS 6 bit string into a bit vector. Each character
352 represents 6 bits. This is the NMEA !AIVD[MO] message payload.
353
354 @note: If the original BitVector had ((len(bitvector) % 6 > 0),
355 then there will be pad bits in the str6. This function has no way
356 to know how many pad bits there are.
357
358 >>> print ais6tobitvec('6')
359 000110
360
361 >>> print ais6tobitvec('6b')
362 000110101010
363
364 >>> print ais6tobitvec('6bF:Z')
365 000110101010010110001010100010
366
367 @bug: Need to add pad bit handling
368
369 @param str6: ASCII that as it appears in the NMEA string
370 @type str6: string
371 @return: decoded bits (not unstuffed... what do I mean by
372 unstuffed?). There may be pad bits at the tail to make this 6 bit
373 aligned.
374 @rtype: BitVector
375 '''
376 bvtotal = BitVector(size=6*len(str6))
377
378 for pos in range(len(str6)):
379 bv = decode[str6[pos]]
380 start = pos*6
381 for i in range(6):
382 bvtotal[i+start] = bv[i]
383 return bvtotal
384
386 """Convert bit vector int an ITU AIS 6 bit string. Each character represents 6 bits
387
388 >>> print bitvectoais6(BitVector(bitstring='000110101010010110001010100010'))
389 ('6bF:Z', 0)
390
391 @param bv: message bits (must be already stuffed)
392 @type bv: BitVector
393 @return: str6 ASCII that as it appears in the NMEA string
394 @rtype: str, pad
395
396 @todo: make a test base for needing padding
397 @bug: handle case when padding needed
398 """
399
400 pad = 6-(len(bv)%6)
401 strLen = len(bv)/6
402 aisStrLst = []
403
404 if pad!=0:
405 if doPadding:
406
407 bv = bv + BitVector(size=pad)
408 else:
409 print 'ERROR: What are you doing with a non-align entity? Let me pad it!'
410 assert False
411
412
413 for i in range(strLen):
414 start = i*6
415 end = (i+1)*6
416 val = int(bv[start:end])
417
418
419 c = encode[val]
420 aisStrLst.append(c)
421
422 aisStr = ''.join(aisStrLst)
423
424 return aisStr, pad
425
426
428 """Apply bit stuffing - add extra bytes to long sequences
429
430 @param bv: bits that may need padding
431 @type bv: BitVector
432 @return: new bits, possibly longer
433 @rtype: BitVector
434
435 @see: unstuffBits
436
437 @todo: Add a nice description of how bit stuffing works
438 @todo: Actually write the code
439 """
440 assert False
441
443 """Undo bit stuffing - remove extra bytes to long sequences
444
445 @param bv: bits that may have padding
446 @type bv: BitVector
447 @return: new bits, possibly longer
448 @rtype: BitVector
449
450 @todo: Actually write the code
451 @see: stuffBits
452 """
453 assert False
454
455 if __name__ == '__main__':
456 from optparse import OptionParser
457 myparser = OptionParser(usage="%prog [options]",version="%prog "+__version__)
458 myparser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true',
459 help='run the documentation tests')
460
461 (options,args) = myparser.parse_args()
462
463 success=True
464
465 if options.doctest:
466 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
467 sys.argv= [sys.argv[0]]
468
469 import doctest
470 numfail,numtests=doctest.testmod()
471 if numfail==0: print 'ok'
472 else:
473 print 'FAILED'
474 success=False
475
476 if not success:
477 sys.exit('Something Failed')
478