blob: 1377fcae7b903163699516b6bd322ec5f597a9f8 [file] [log] [blame]
Harald Welteeea18a62016-04-29 15:18:35 +02001#!/usr/bin/python
2
3mod_license = """
4/*
5 * Copyright (C) 2011-2016 Sylvain Munaut <tnt@246tNt.com>
6 * Copyright (C) 2016 sysmocom s.f.m.c. GmbH
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24"""
25
26import sys, os, math
27
28class ConvolutionalCode(object):
29
30 def __init__(self, block_len, polys, name = "call-me", description = "LOL", puncture = []):
31 # Save simple params
32 self.block_len = block_len
33 self.k = 1
34 self.puncture = puncture
35 self.rate_inv = len(polys)
36
37 # Infos
38 self.name = name
39 self.description = description
40
41 # Handle polynoms (and check for recursion)
42 self.polys = [(1, 1) if x[0] == x[1] else x for x in polys]
43
44 # Determine the polynomial degree
45 for (x, y) in polys:
46 self.k = max(self.k, int(math.floor(math.log(max(x, y), 2))))
47 self.k = self.k + 1
48
49 self.poly_divider = 1
50 rp = [x[1] for x in self.polys if x[1] != 1]
51 if rp:
52 if not all([x == rp[0] for x in rp]):
53 raise ValueError("Bad polynoms: Can't have multiple different divider polynoms !")
54 if not all([x[0] == 1 for x in polys if x[1] == 1]):
55 raise ValueError("Bad polynoms: Can't have a '1' divider with a non '1' dividend in a recursive code")
56 self.poly_divider = rp[0]
57
58 @property
59 def recursive(self):
60 return self.poly_divider != 1
61
62 @property
63 def _state_mask(self):
64 return (1 << (self.k - 1)) - 1
65
66 def next_state(self, state, bit):
67 nb = combine(
68 (state << 1) | bit,
69 self.poly_divider,
70 self.k,
71 )
72 return ((state << 1) | nb) & self._state_mask
73
74 def next_term_state(self, state):
75 return (state << 1) & self._state_mask
76
77 def next_output(self, state, bit, ns = None):
78 # Next state bit
79 if ns is None:
80 ns = self.next_state(state, bit)
81
82 src = (ns & 1) | (state << 1)
83
84 # Scan polynoms
85 rv = []
86 for p_n, p_d in self.polys:
87 if self.recursive and p_d == 1:
88 o = bit # No choice ... (systematic output in recursive case)
89 else:
90 o = combine(src, p_n, self.k)
91 rv.append(o)
92
93 return rv
94
95 def next_term_output(self, state, ns = None):
96 # Next state bit
97 if ns is None:
98 ns = self.next_term_state(state)
99
100 src = (ns & 1) | (state << 1)
101
102 # Scan polynoms
103 rv = []
104 for p_n, p_d in self.polys:
105 if self.recursive and p_d == 1:
106 # Systematic output are replaced when in 'termination' mode
107 o = combine(src, self.poly_divider, self.k)
108 else:
109 o = combine(src, p_n, self.k)
110 rv.append(o)
111
112 return rv
113
114 def next(self, state, bit):
115 ns = self.next_state(state, bit)
116 nb = self.next_output(state, bit, ns = ns)
117 return ns, nb
118
119 def next_term(self, state):
120 ns = self.next_term_state(state)
121 nb = self.next_term_output(state, ns = ns)
122 return ns, nb
123
124 def _print_term(self, fi, num_states, pack = False):
125 d = []
126 for state in range(num_states):
127 x = pack(self.next_term_output(state)) if pack else self.next_term_state(state)
128 d.append("%d, " % x)
129 print >>fi, "\t%s" % ''.join(d)
130
131 def _print_x(self, fi, num_states, pack = False):
132 for state in range(num_states):
133 x0 = pack(self.next_output(state, 0)) if pack else self.next_state(state, 0)
134 x1 = pack(self.next_output(state, 1)) if pack else self.next_state(state, 1)
135 print >>fi, "\t{ %2d, %2d }," % (x0, x1)
136
137 def gen_tables(self, pref, fi):
138 pack = lambda n: sum([x << (self.rate_inv - i - 1) for i, x in enumerate(n)])
139 num_states = 1 << (self.k - 1)
140 print >>fi, "\nstatic const uint8_t %s_state[][2] = {" % self.name
141 self._print_x(fi, num_states)
142 print >>fi, "};\n\nstatic const uint8_t %s_output[][2] = {" % self.name
143 self._print_x(fi, num_states, pack)
144 print >>fi, "};"
145
146 if self.recursive:
147 print >>fi, "\nstatic const uint8_t %s_term_state[] = {" % self.name
148 self._print_term(fi, num_states)
149 print >>fi, "};\n\nstatic const uint8_t %s_term_output[] = {" % self.name
150 self._print_term(fi, num_states, pack)
151 print >>fi, "};"
152
153 if len(self.puncture):
154 print >>fi, "\nstatic const int %s_puncture[] = {" % self.name
155 for p in self.puncture:
156 print >>fi, "\t%d," % p
157 print >>fi, "};"
158
159 print >>fi, "\n/* %s */" % self.description
160 print >>fi, "const struct osmo_conv_code %s_%s = {" % (pref, self.name)
161 print >>fi, "\t.N = %d," % self.rate_inv
162 print >>fi, "\t.K = %d," % self.k
163 print >>fi, "\t.len = %d," % self.block_len
164 print >>fi, "\t.next_output = %s_output," % self.name
165 print >>fi, "\t.next_state = %s_state," % self.name
166 if self.recursive:
167 print >>fi, "\t.next_term_output = %s_term_output," % self.name
168 print >>fi, "\t.next_term_state = %s_term_state," % self.name
169 if len(self.puncture):
170 print >>fi, "\t.puncture = %s_puncture," % self.name
171 print >>fi, "};"
172
173poly = lambda *args: sum([(1 << x) for x in args])
174
175def combine(src, sel, nb):
176 x = src & sel
177 fn_xor = lambda x, y: x ^ y
178 return reduce(fn_xor, [(x >> n) & 1 for n in range(nb)])
179
180# Polynomials according to 3GPP TS 05.03 Annex B
181G0 = poly(0, 3, 4)
182G1 = poly(0, 1, 3, 4)
183G2 = poly(0, 2, 4)
184G3 = poly(0, 1, 2, 3, 4)
185G4 = poly(0, 2, 3, 5, 6)
186G5 = poly(0, 1, 4, 6)
187G6 = poly(0, 1, 2, 3, 4, 6)
188G7 = poly(0, 1, 2, 3, 6)
189
190CCH_poly = [
191 ( G0, 1 ),
192 ( G1, 1 )
193]
194
195xCCH = ConvolutionalCode(
196 224,
197 CCH_poly,
198 name = "xcch",
199 description =""" *CCH convolutional code:
200 228 bits blocks, rate 1/2, k = 5
201 G0 = 1 + D3 + D4
202 G1 = 1 + D + D3 + D4
203"""
204)
205
206CS2 = ConvolutionalCode(
207 290,
208 CCH_poly,
209 puncture = [
210 15, 19, 23, 27, 31, 35, 43, 47, 51, 55, 59, 63, 67, 71,
211 75, 79, 83, 91, 95, 99, 103, 107, 111, 115, 119, 123, 127, 131,
212 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 187, 191, 195,
213 199, 203, 207, 211, 215, 219, 223, 227, 235, 239, 243, 247, 251, 255,
214 259, 263, 267, 271, 275, 283, 287, 291, 295, 299, 303, 307, 311, 315,
215 319, 323, 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 371, 379,
216 383, 387, 391, 395, 399, 403, 407, 411, 415, 419, 427, 431, 435, 439,
217 443, 447, 451, 455, 459, 463, 467, 475, 479, 483, 487, 491, 495, 499,
218 503, 507, 511, 515, 523, 527, 531, 535, 539, 543, 547, 551, 555, 559,
219 563, 571, 575, 579, 583, 587, -1
220 ],
221 name = "cs2",
222 description =""" CS2 convolutional code:
223 G0 = 1 + D3 + D4
224 G1 = 1 + D + D3 + D4
225"""
226)
227
228CS3 = ConvolutionalCode(
229 334,
230 CCH_poly,
231 puncture = [
232 15, 17, 21, 23, 27, 29, 33, 35, 39, 41, 45, 47, 51, 53,
233 57, 59, 63, 65, 69, 71, 75, 77, 81, 83, 87, 89, 93, 95,
234 99, 101, 105, 107, 111, 113, 117, 119, 123, 125, 129, 131, 135, 137,
235 141, 143, 147, 149, 153, 155, 159, 161, 165, 167, 171, 173, 177, 179,
236 183, 185, 189, 191, 195, 197, 201, 203, 207, 209, 213, 215, 219, 221,
237 225, 227, 231, 233, 237, 239, 243, 245, 249, 251, 255, 257, 261, 263,
238 267, 269, 273, 275, 279, 281, 285, 287, 291, 293, 297, 299, 303, 305,
239 309, 311, 315, 317, 321, 323, 327, 329, 333, 335, 339, 341, 345, 347,
240 351, 353, 357, 359, 363, 365, 369, 371, 375, 377, 381, 383, 387, 389,
241 393, 395, 399, 401, 405, 407, 411, 413, 417, 419, 423, 425, 429, 431,
242 435, 437, 441, 443, 447, 449, 453, 455, 459, 461, 465, 467, 471, 473,
243 477, 479, 483, 485, 489, 491, 495, 497, 501, 503, 507, 509, 513, 515,
244 519, 521, 525, 527, 531, 533, 537, 539, 543, 545, 549, 551, 555, 557,
245 561, 563, 567, 569, 573, 575, 579, 581, 585, 587, 591, 593, 597, 599,
246 603, 605, 609, 611, 615, 617, 621, 623, 627, 629, 633, 635, 639, 641,
247 645, 647, 651, 653, 657, 659, 663, 665, 669, 671, -1
248 ],
249 name = "cs3",
250 description =""" CS3 convolutional code:
251 G0 = 1 + D3 + D4
252 G1 = 1 + D + D3 + D4
253"""
254)
255
256TCH_AFS_12_2 = ConvolutionalCode(
257 250,
258 [
259 ( 1, 1 ),
260 ( G1, G0 ),
261 ],
262 puncture = [
263 321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363,
264 365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401,
265 405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441,
266 443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477,
267 481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507,
268 -1
269 ],
270 name = 'tch_afs_12_2',
271 description = """TCH/AFS 12.2 convolutional code:
272 250 bits block, rate 1/2, punctured
273 G0/G0 = 1
274 G1/G0 = 1 + D + D3 + D4 / 1 + D3 + D4
275"""
276)
277
278TCH_AFS_10_2 = ConvolutionalCode(
279 210,
280 [
281 ( G1, G3 ),
282 ( G2, G3 ),
283 ( 1, 1 ),
284 ],
285 puncture = [
286 1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43,
287 46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91,
288 94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139,
289 142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187,
290 190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235,
291 238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283,
292 286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328,
293 331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364,
294 367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400,
295 403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436,
296 439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472,
297 475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508,
298 511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544,
299 547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580,
300 583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613,
301 616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637,
302 639, 640, -1
303 ],
304 name = 'tch_afs_10_2',
305 description = """TCH/AFS 10.2 kbits convolutional code:
306 G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
307 G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
308 G3/G3 = 1
309"""
310)
311
312TCH_AFS_7_95 = ConvolutionalCode(
313 165,
314 [
315 ( 1, 1 ),
316 ( G5, G4 ),
317 ( G6, G4 ),
318 ],
319 puncture = [
320 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
321 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
322 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
323 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
324 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
325 506, 508, 509, 511, 512, -1
326 ],
327 name = 'tch_afs_7_95',
328 description = """TCH/AFS 7.95 kbits convolutional code:
329 G4/G4 = 1
330 G5/G4 = 1 + D + D4 + D6 / 1 + D2 + D3 + D5 + D6
331 G6/G4 = 1 + D + D2 + D3 + D4 + D6 / 1 + D2 + D3 + D5 + D6
332"""
333)
334
335TCH_AFS_7_4 = ConvolutionalCode(
336 154,
337 [
338 ( G1, G3 ),
339 ( G2, G3 ),
340 ( 1, 1 ),
341 ],
342 puncture = [
343 0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415,
344 421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469,
345 471, 472, -1
346 ],
347 name = 'tch_afs_7_4',
348 description = """TCH/AFS 7.4 kbits convolutional code:
349 G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
350 G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
351 G3/G3 = 1
352"""
353)
354
355TCH_AFS_6_7 = ConvolutionalCode(
356 140,
357 [
358 ( G1, G3 ),
359 ( G2, G3 ),
360 ( 1, 1 ),
361 ( 1, 1 ),
362 ],
363 puncture = [
364 1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107,
365 119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267,
366 279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327,
367 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371,
368 375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403,
369 407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435,
370 439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467,
371 471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499,
372 503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531,
373 535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559,
374 561, 563, 565, 567, 569, 571, 573, 575, -1
375 ],
376 name = 'tch_afs_6_7',
377 description = """TCH/AFS 6.7 kbits convolutional code:
378 G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
379 G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
380 G3/G3 = 1
381 G3/G3 = 1
382"""
383)
384
385TCH_AFS_5_9 = ConvolutionalCode(
386 124,
387 [
388 ( G4, G6 ),
389 ( G5, G6 ),
390 ( 1, 1),
391 ( 1, 1),
392 ],
393 puncture = [
394 0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95,
395 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287,
396 303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375,
397 379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439,
398 443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491,
399 495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519,
400 -1
401 ],
402 name = 'tch_afs_5_9',
403 description = """TCH/AFS 5.9 kbits convolutional code:
404 124 bits
405 G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
406 G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6
407 G6/G6 = 1
408 G6/G6 = 1
409"""
410)
411
412TCH_AFS_5_15 = ConvolutionalCode(
413 109,
414 [
415 ( G1, G3 ),
416 ( G1, G3 ),
417 ( G2, G3 ),
418 ( 1, 1 ),
419 ( 1, 1 ),
420 ],
421 puncture = [
422 0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40,
423 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
424 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280,
425 290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345,
426 350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385,
427 390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425,
428 430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465,
429 470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505,
430 510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540,
431 544, 545, 549, 550, 554, 555, 559, 560, 564, -1
432 ],
433 name = 'tch_afs_5_15',
434 description = """TCH/AFS 5.15 kbits convolutional code:
435 G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
436 G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
437 G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
438 G3/G3 = 1
439 G3/G3 = 1
440"""
441)
442
443TCH_AFS_4_75 = ConvolutionalCode(
444 101,
445 [
446 ( G4, G6 ),
447 ( G4, G6 ),
448 ( G5, G6 ),
449 ( 1, 1 ),
450 ( 1, 1 ),
451 ],
452 puncture = [
453 0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55,
454 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175,
455 185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295,
456 305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405,
457 410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460,
458 465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509,
459 510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530,
460 531, 532, 534, -1
461 ],
462 name = 'tch_afs_4_75',
463 description = """TCH/AFS 4.75 kbits convolutional code:
464 G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
465 G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
466 G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6
467 G6/G6 = 1
468 G6/G6 = 1
469"""
470)
471
472def gen_c(dest, pref, code):
473 f = open(os.path.join(dest, 'conv_' + code.name + '_gen.c'), 'w')
474 print >>f, mod_license
475 print >>f, "#include <stdint.h>"
476 print >>f, "#include <osmocom/core/conv.h>"
477 code.gen_tables(pref, f)
478
479if __name__ == '__main__':
480 print >>sys.stderr, "Generating convolutional codes..."
481 prefix = "gsm0503"
482 path = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
483 gen_c(path, prefix, xCCH)
484 gen_c(path, prefix, CS2)
485 gen_c(path, prefix, CS3)
486 gen_c(path, prefix, TCH_AFS_12_2)
487 gen_c(path, prefix, TCH_AFS_10_2)
488 gen_c(path, prefix, TCH_AFS_7_95)
489 gen_c(path, prefix, TCH_AFS_7_4)
490 gen_c(path, prefix, TCH_AFS_6_7)
491 gen_c(path, prefix, TCH_AFS_5_9)
492 gen_c(path, prefix, TCH_AFS_5_15)
493 gen_c(path, prefix, TCH_AFS_4_75)
494 print >>sys.stderr, "\tdone."