1
2
3 __version__ = '$Revision: 2068 $'.split()[1]
4 __date__ = '$Date: 2006-05-02 08:17:59 -0400 (Tue, 02 May 2006) $'.split()[1]
5 __author__ = 'Kurt Schwehr'
6
7 __doc__='''
8 Handle encoding and decoding AIS strings.
9
10 @bug: need some more interesting doctests!
11 @bug: needs to throw an exception if the character is not in the LUT
12 @bug: what to do about string with trailing @@@ or " " (white space)
13
14 @var characterLUT: lookup table for decode to fetch characters faster
15 @type characterLUT: list
16
17 @var characterBits: lookup table for going from a single character to a 6 bit BitVector
18 @type characterBits: dict
19
20
21 @author: '''+__author__+'''
22 @version: ''' + __version__ +'''
23 @copyright: 2006
24
25 @var __date__: Date of last svn commit
26 @undocumented: __version__ __author__ __doc__ myparser
27 @undocumented: buildDict
28 '''
29
30
31
32
33 import sys
34
35
36 from BitVector import BitVector
37
38
39 import binary
40
41
42
43 characterLUT=[ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
44 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
45 'Z', '[', '\\', ']', '^', '-', ' ',
46 '!', '"', '#', '$', '%', '&', '`', '(', ')', '*', '+', ',', '-', '.', '/',
47 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
48 ':', ';', '<', '=', '>', '?'
49 ]
50
51 characterDict={
52 '@': 0, 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6,
53 'G': 7, 'H': 8, 'I': 9, 'J': 10, 'K': 11, 'L': 12, 'M': 13,
54 'N': 14, 'O': 15, 'P': 16, 'Q': 17, 'R': 18, 'S': 19, 'T': 20,
55 'U': 21, 'V': 22, 'W': 23, 'X': 24, 'Y': 25, 'Z': 26, '[': 27,
56 '\\': 28, ']': 29, '^': 30, '-': 31, ' ': 32, '!': 33, '"': 34,
57 '#': 35, '$': 36, '%': 37, '&': 38, '`': 39, '(': 40, ')': 41,
58 '*': 42, '+': 43, ',': 44, '-': 45, '.': 46, '/': 47, '0': 48,
59 '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55,
60 '8': 56, '9': 57, ':': 58, ';': 59, '<': 60, '=': 61, '>': 62,
61 '?': 63
62 }
63 '''Fast lookup for the AIS int code for a character '''
64
65
66
67 characterBits={}
68 characterBits['@']=binary.setBitVectorSize(BitVector(intVal=0),6)
69 characterBits['A']=binary.setBitVectorSize(BitVector(intVal=1),6)
70 characterBits['B']=binary.setBitVectorSize(BitVector(intVal=2),6)
71 characterBits['C']=binary.setBitVectorSize(BitVector(intVal=3),6)
72 characterBits['D']=binary.setBitVectorSize(BitVector(intVal=4),6)
73 characterBits['E']=binary.setBitVectorSize(BitVector(intVal=5),6)
74 characterBits['F']=binary.setBitVectorSize(BitVector(intVal=6),6)
75 characterBits['G']=binary.setBitVectorSize(BitVector(intVal=7),6)
76 characterBits['H']=binary.setBitVectorSize(BitVector(intVal=8),6)
77 characterBits['I']=binary.setBitVectorSize(BitVector(intVal=9),6)
78 characterBits['J']=binary.setBitVectorSize(BitVector(intVal=10),6)
79 characterBits['K']=binary.setBitVectorSize(BitVector(intVal=11),6)
80 characterBits['L']=binary.setBitVectorSize(BitVector(intVal=12),6)
81 characterBits['M']=binary.setBitVectorSize(BitVector(intVal=13),6)
82 characterBits['N']=binary.setBitVectorSize(BitVector(intVal=14),6)
83 characterBits['O']=binary.setBitVectorSize(BitVector(intVal=15),6)
84 characterBits['P']=binary.setBitVectorSize(BitVector(intVal=16),6)
85 characterBits['Q']=binary.setBitVectorSize(BitVector(intVal=17),6)
86 characterBits['R']=binary.setBitVectorSize(BitVector(intVal=18),6)
87 characterBits['S']=binary.setBitVectorSize(BitVector(intVal=19),6)
88 characterBits['T']=binary.setBitVectorSize(BitVector(intVal=20),6)
89 characterBits['U']=binary.setBitVectorSize(BitVector(intVal=21),6)
90 characterBits['V']=binary.setBitVectorSize(BitVector(intVal=22),6)
91 characterBits['W']=binary.setBitVectorSize(BitVector(intVal=23),6)
92 characterBits['X']=binary.setBitVectorSize(BitVector(intVal=24),6)
93 characterBits['Y']=binary.setBitVectorSize(BitVector(intVal=25),6)
94 characterBits['Z']=binary.setBitVectorSize(BitVector(intVal=26),6)
95 characterBits['[']=binary.setBitVectorSize(BitVector(intVal=27),6)
96 characterBits['\\']=binary.setBitVectorSize(BitVector(intVal=28),6)
97 characterBits[']']=binary.setBitVectorSize(BitVector(intVal=29),6)
98 characterBits['^']=binary.setBitVectorSize(BitVector(intVal=30),6)
99 characterBits['-']=binary.setBitVectorSize(BitVector(intVal=31),6)
100 characterBits[' ']=binary.setBitVectorSize(BitVector(intVal=32),6)
101 characterBits['!']=binary.setBitVectorSize(BitVector(intVal=33),6)
102 characterBits['"']=binary.setBitVectorSize(BitVector(intVal=34),6)
103 characterBits['#']=binary.setBitVectorSize(BitVector(intVal=35),6)
104 characterBits['$']=binary.setBitVectorSize(BitVector(intVal=36),6)
105 characterBits['%']=binary.setBitVectorSize(BitVector(intVal=37),6)
106 characterBits['&']=binary.setBitVectorSize(BitVector(intVal=38),6)
107 characterBits['`']=binary.setBitVectorSize(BitVector(intVal=39),6)
108 characterBits['(']=binary.setBitVectorSize(BitVector(intVal=40),6)
109 characterBits[')']=binary.setBitVectorSize(BitVector(intVal=41),6)
110 characterBits['*']=binary.setBitVectorSize(BitVector(intVal=42),6)
111 characterBits['+']=binary.setBitVectorSize(BitVector(intVal=43),6)
112 characterBits[',']=binary.setBitVectorSize(BitVector(intVal=44),6)
113 characterBits['-']=binary.setBitVectorSize(BitVector(intVal=45),6)
114 characterBits['.']=binary.setBitVectorSize(BitVector(intVal=46),6)
115 characterBits['/']=binary.setBitVectorSize(BitVector(intVal=47),6)
116 characterBits['0']=binary.setBitVectorSize(BitVector(intVal=48),6)
117 characterBits['1']=binary.setBitVectorSize(BitVector(intVal=49),6)
118 characterBits['2']=binary.setBitVectorSize(BitVector(intVal=50),6)
119 characterBits['3']=binary.setBitVectorSize(BitVector(intVal=51),6)
120 characterBits['4']=binary.setBitVectorSize(BitVector(intVal=52),6)
121 characterBits['5']=binary.setBitVectorSize(BitVector(intVal=53),6)
122 characterBits['6']=binary.setBitVectorSize(BitVector(intVal=54),6)
123 characterBits['7']=binary.setBitVectorSize(BitVector(intVal=55),6)
124 characterBits['8']=binary.setBitVectorSize(BitVector(intVal=56),6)
125 characterBits['9']=binary.setBitVectorSize(BitVector(intVal=57),6)
126 characterBits[':']=binary.setBitVectorSize(BitVector(intVal=58),6)
127 characterBits[';']=binary.setBitVectorSize(BitVector(intVal=59),6)
128 characterBits['<']=binary.setBitVectorSize(BitVector(intVal=60),6)
129 characterBits['=']=binary.setBitVectorSize(BitVector(intVal=61),6)
130 characterBits['>']=binary.setBitVectorSize(BitVector(intVal=62),6)
131 characterBits['?']=binary.setBitVectorSize(BitVector(intVal=63),6)
132
133
135 '''
136 Helper to build the build the carachterBits and Dict tables
137
138 @rtype: test to stdout
139 '''
140 count=0
141 print 'characterDict={'
142 for i in range(len(characterLUT)):
143 count += 1
144 c = characterLUT[i]
145 if c=='\\': c='\\\\'
146 print "'"+c+"': "+str(i)+",",
147 if count%6==0:
148 print
149 print '}'
150
151 print 'characterBits={}'
152 for i in range(len(characterLUT)):
153 c = characterLUT[i]
154 if c=='\\': c='\\\\'
155 print "characterBits['"+c+"']"+'=binary.setBitVectorSize(BitVector(intVal='+str(i)+'),6)'
156
157
158 -def decode(bits,dropAfterFirstAt=False):
159 '''
160 Decode bits as a string. Does not remove the end space or @@@@. Must be an multiple of 6 bits.
161
162 @param bits: n*6 bits that represent a string.
163 @type bits: BitVector
164 @return: string with pad spaces or @@@@
165 @rtype: str
166 '''
167
168 numchar=len(bits)/6
169 s = []
170 for i in range(numchar):
171 start = 6 * i
172 end = start+6
173 charbits=bits[start:end]
174 val = int(charbits)
175 if dropAfterFirstAt and val==0:
176 break
177 s.append(characterLUT[val])
178
179 return ''.join(s)
180
181
182 -def encode(string,bitSize=None):
183 '''
184 @param string: python ascii string to encode.
185 @type string: str
186 @param bitSize: how many bits should this take. must be a multiple of 6
187 @type bitSize: int
188 @return: enocded bits for the string
189 @rtype: BitVector
190 @bug: force to upper case
191 @bug: building this in reverse may be faster
192 @bug: check that bitSize is a multple of 6
193 @bug: pad with "@" to reach requested bitSize
194 '''
195 if bitSize:
196 assert(bitSize%6==0)
197 bv = BitVector(size=0)
198 for i in range(len(string)):
199 bv = bv+characterBits[string[i]]
200 if bitSize:
201 if bitSize < len(bv):
202 print 'ERROR: string longer than specified bit count: "'+string+'"', bitSize, len(bv)
203 assert False
204 extra = bitSize - len(bv)
205 bv = bv+BitVector(size=extra)
206 return bv
207
208 -def unpad(string,removeBlanks=True):
209 """
210 Remove AIS string padding
211
212 >>> unpad('@')
213 ''
214 >>> unpad('A@')
215 'A'
216 >>> unpad('ABCDEF1234@@@@@')
217 'ABCDEF1234'
218
219 FIX: is this the correct response?
220
221 >>> unpad('A@B')
222 'A@B'
223
224 This is non standard behavior, but some AIS systems space pad the right
225
226 >>> unpad(' ')
227 ''
228 >>> unpad('MY SHIP NAME ')
229 'MY SHIP NAME'
230
231 The standard implies this behavior with is less fun
232
233 >>> unpad('MY SHIP NAME ',removeBlanks=False)
234 'MY SHIP NAME '
235
236 @bug: use a faster algorithm for truncating the string
237 @param string: string to cleanup
238 @type string: str
239 @param removeBlanks: set to true to strip spaces on the right
240 @type removeBlanks: bool
241 @return: cleaned up string
242 @rtype: str
243 """
244 while len(string)>0 and string[-1]=='@':
245 string=string[:-1]
246 if removeBlanks:
247 while len(string)>0 and string[-1]==' ':
248 string=string[:-1]
249 return string
250
251 -def pad(string,length):
252 '''
253 pad a string out to the proper length with the @ character as required by the ais spec
254
255 >>> pad('',0)
256 ''
257 >>> pad('',1)
258 '@'
259 >>> pad('A',1)
260 'A'
261 >>> pad('A',2)
262 'A@'
263 >>> pad('MY SHIP NAME',20)
264 'MY SHIP NAME@@@@@@@@'
265
266 @param string: string to pad out
267 @type string: str
268 @param length: number of characters that the string must be
269 @type length: int
270 @return: str of len length
271 @rtype: str
272
273 @bug: Use a list and join to make the string building faster
274 '''
275 while len(string)<length: string += '@'
276 return string
277
278
279 if __name__ == '__main__':
280 from optparse import OptionParser
281 myparser = OptionParser(usage="%prog [options]",version="%prog "+__version__)
282 myparser.add_option('--test','--doc-test',dest='doctest',default=False,action='store_true',
283 help='run the documentation tests')
284
285 (options,args) = myparser.parse_args()
286
287 success=True
288
289 if options.doctest:
290 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
291 sys.argv= [sys.argv[0]]
292
293 import doctest
294 numfail,numtests=doctest.testmod()
295 if numfail==0: print 'ok'
296 else:
297 print 'FAILED'
298 success=False
299
300 if not success:
301 sys.exit('Something Failed')
302