blob: e929d3f70204518918ca1f6abc812df273c9c320 [file] [log] [blame]
Holger Hans Peter Freyther3a96d282016-04-29 21:24:48 +02001#!/usr/bin/python2
Harald Welteeea18a62016-04-29 15:18:35 +02002
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
Vadim Yanitskiy2c717942017-01-13 02:23:01 +070026import sys, os, math, argparse
Vadim Yanitskiyf9c2c562016-11-01 22:19:28 +070027from functools import reduce
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +070028import conv_codes_gsm
Harald Welteeea18a62016-04-29 15:18:35 +020029
30class ConvolutionalCode(object):
31
Vadim Yanitskiye31cf802016-09-07 21:51:25 +070032 def __init__(self, block_len, polys, name,
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +070033 description = None, puncture = [], term_type = None,
34 vec_in = None, vec_out = None):
Harald Welteeea18a62016-04-29 15:18:35 +020035 # Save simple params
36 self.block_len = block_len
37 self.k = 1
38 self.puncture = puncture
39 self.rate_inv = len(polys)
Vadim Yanitskiya6b52162016-09-08 22:06:07 +070040 self.term_type = term_type
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +070041 self.vec_in = vec_in
42 self.vec_out = vec_out
Harald Welteeea18a62016-04-29 15:18:35 +020043
44 # Infos
45 self.name = name
46 self.description = description
47
Vadim Yanitskiy84fc2ce2016-09-08 20:30:36 +070048 # Handle polynomials (and check for recursion)
Harald Welteeea18a62016-04-29 15:18:35 +020049 self.polys = [(1, 1) if x[0] == x[1] else x for x in polys]
50
51 # Determine the polynomial degree
52 for (x, y) in polys:
53 self.k = max(self.k, int(math.floor(math.log(max(x, y), 2))))
54 self.k = self.k + 1
55
56 self.poly_divider = 1
57 rp = [x[1] for x in self.polys if x[1] != 1]
58 if rp:
59 if not all([x == rp[0] for x in rp]):
Vadim Yanitskiy84fc2ce2016-09-08 20:30:36 +070060 raise ValueError("Bad polynomials: "
61 "Can't have multiple different divider polynomials!")
Vadim Yanitskiye31cf802016-09-07 21:51:25 +070062
Harald Welteeea18a62016-04-29 15:18:35 +020063 if not all([x[0] == 1 for x in polys if x[1] == 1]):
Vadim Yanitskiy84fc2ce2016-09-08 20:30:36 +070064 raise ValueError("Bad polynomials: "
Vadim Yanitskiye31cf802016-09-07 21:51:25 +070065 "Can't have a '1' divider with a non '1' dividend "
66 "in a recursive code")
67
Harald Welteeea18a62016-04-29 15:18:35 +020068 self.poly_divider = rp[0]
69
70 @property
71 def recursive(self):
72 return self.poly_divider != 1
73
74 @property
75 def _state_mask(self):
76 return (1 << (self.k - 1)) - 1
77
78 def next_state(self, state, bit):
79 nb = combine(
80 (state << 1) | bit,
81 self.poly_divider,
82 self.k,
83 )
84 return ((state << 1) | nb) & self._state_mask
85
86 def next_term_state(self, state):
87 return (state << 1) & self._state_mask
88
89 def next_output(self, state, bit, ns = None):
90 # Next state bit
91 if ns is None:
92 ns = self.next_state(state, bit)
93
94 src = (ns & 1) | (state << 1)
95
Vadim Yanitskiy84fc2ce2016-09-08 20:30:36 +070096 # Scan polynomials
Harald Welteeea18a62016-04-29 15:18:35 +020097 rv = []
98 for p_n, p_d in self.polys:
99 if self.recursive and p_d == 1:
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700100 # No choice ... (systematic output in recursive case)
101 o = bit
Harald Welteeea18a62016-04-29 15:18:35 +0200102 else:
103 o = combine(src, p_n, self.k)
104 rv.append(o)
105
106 return rv
107
108 def next_term_output(self, state, ns = None):
109 # Next state bit
110 if ns is None:
111 ns = self.next_term_state(state)
112
113 src = (ns & 1) | (state << 1)
114
Vadim Yanitskiy84fc2ce2016-09-08 20:30:36 +0700115 # Scan polynomials
Harald Welteeea18a62016-04-29 15:18:35 +0200116 rv = []
117 for p_n, p_d in self.polys:
118 if self.recursive and p_d == 1:
119 # Systematic output are replaced when in 'termination' mode
120 o = combine(src, self.poly_divider, self.k)
121 else:
122 o = combine(src, p_n, self.k)
123 rv.append(o)
124
125 return rv
126
127 def next(self, state, bit):
128 ns = self.next_state(state, bit)
129 nb = self.next_output(state, bit, ns = ns)
130 return ns, nb
131
132 def next_term(self, state):
133 ns = self.next_term_state(state)
134 nb = self.next_term_output(state, ns = ns)
135 return ns, nb
136
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700137 def _print_term(self, fi, num_states, pack = False):
Vadim Yanitskiy6908fa72016-09-07 22:34:53 +0700138 items = []
139
Harald Welteeea18a62016-04-29 15:18:35 +0200140 for state in range(num_states):
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700141 if pack:
142 x = pack(self.next_term_output(state))
143 else:
144 x = self.next_term_state(state)
145
Vadim Yanitskiy6908fa72016-09-07 22:34:53 +0700146 items.append(x)
147
148 # Up to 12 numbers should be placed per line
149 print_formatted(items, "%3d, ", 12, fi)
Harald Welteeea18a62016-04-29 15:18:35 +0200150
151 def _print_x(self, fi, num_states, pack = False):
Vadim Yanitskiy6908fa72016-09-07 22:34:53 +0700152 items = []
153
Harald Welteeea18a62016-04-29 15:18:35 +0200154 for state in range(num_states):
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700155 if pack:
156 x0 = pack(self.next_output(state, 0))
157 x1 = pack(self.next_output(state, 1))
158 else:
159 x0 = self.next_state(state, 0)
160 x1 = self.next_state(state, 1)
161
Vadim Yanitskiy6908fa72016-09-07 22:34:53 +0700162 items.append((x0, x1))
163
164 # Up to 4 blocks should be placed per line
165 print_formatted(items, "{ %2d, %2d }, ", 4, fi)
166
167 def _print_puncture(self, fi):
168 # Up to 12 numbers should be placed per line
169 print_formatted(self.puncture, "%3d, ", 12, fi)
Harald Welteeea18a62016-04-29 15:18:35 +0200170
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700171 def print_state_and_output(self, fi):
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700172 pack = lambda n: \
173 sum([x << (self.rate_inv - i - 1) for i, x in enumerate(n)])
Harald Welteeea18a62016-04-29 15:18:35 +0200174 num_states = 1 << (self.k - 1)
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700175
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700176 fi.write("static const uint8_t %s_state[][2] = {\n" % self.name)
Harald Welteeea18a62016-04-29 15:18:35 +0200177 self._print_x(fi, num_states)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700178 fi.write("};\n\n")
179
180 fi.write("static const uint8_t %s_output[][2] = {\n" % self.name)
Harald Welteeea18a62016-04-29 15:18:35 +0200181 self._print_x(fi, num_states, pack)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700182 fi.write("};\n\n")
Harald Welteeea18a62016-04-29 15:18:35 +0200183
184 if self.recursive:
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700185 fi.write("static const uint8_t %s_term_state[] = {\n" % self.name)
Harald Welteeea18a62016-04-29 15:18:35 +0200186 self._print_term(fi, num_states)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700187 fi.write("};\n\n")
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700188
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700189 fi.write("static const uint8_t %s_term_output[] = {\n" % self.name)
Harald Welteeea18a62016-04-29 15:18:35 +0200190 self._print_term(fi, num_states, pack)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700191 fi.write("};\n\n")
Harald Welteeea18a62016-04-29 15:18:35 +0200192
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700193 def gen_tables(self, pref, fi, shared_tables = None):
194 # Do not print shared tables
195 if shared_tables is None:
196 self.print_state_and_output(fi)
197 table_pref = self.name
198 else:
199 table_pref = shared_tables
200
Harald Welteeea18a62016-04-29 15:18:35 +0200201 if len(self.puncture):
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700202 fi.write("static const int %s_puncture[] = {\n" % self.name)
Vadim Yanitskiy6908fa72016-09-07 22:34:53 +0700203 self._print_puncture(fi)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700204 fi.write("};\n\n")
Harald Welteeea18a62016-04-29 15:18:35 +0200205
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700206 # Write description as a multi-line comment
207 if self.description is not None:
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700208 fi.write("/**\n")
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700209 for line in self.description:
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700210 fi.write(" * %s\n" % line)
211 fi.write(" */\n")
Vadim Yanitskiye31cf802016-09-07 21:51:25 +0700212
213 # Print a final convolutional code definition
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700214 fi.write("const struct osmo_conv_code %s_%s = {\n" % (pref, self.name))
215 fi.write("\t.N = %d,\n" % self.rate_inv)
216 fi.write("\t.K = %d,\n" % self.k)
217 fi.write("\t.len = %d,\n" % self.block_len)
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700218 fi.write("\t.next_output = %s_output,\n" % table_pref)
219 fi.write("\t.next_state = %s_state,\n" % table_pref)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700220
Vadim Yanitskiya6b52162016-09-08 22:06:07 +0700221 if self.term_type is not None:
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700222 fi.write("\t.term = %s,\n" % self.term_type)
223
Harald Welteeea18a62016-04-29 15:18:35 +0200224 if self.recursive:
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700225 fi.write("\t.next_term_output = %s_term_output,\n" % table_pref)
226 fi.write("\t.next_term_state = %s_term_state,\n" % table_pref)
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700227
Harald Welteeea18a62016-04-29 15:18:35 +0200228 if len(self.puncture):
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700229 fi.write("\t.puncture = %s_puncture,\n" % self.name)
230 fi.write("};\n\n")
Harald Welteeea18a62016-04-29 15:18:35 +0200231
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +0700232 def calc_out_len(self):
233 out_len = self.block_len * self.rate_inv
234
235 # By default CONV_TERM_FLUSH
236 if self.term_type is None:
237 out_len += self.rate_inv * (self.k - 1)
238
239 if len(self.puncture):
240 out_len -= len(self.puncture) - 1
241
242 return out_len
243
244 def gen_test_vector(self, fi, prefix):
245 code_name = "%s_%s" % (prefix, self.name)
246
247 fi.write("\t{\n")
248 fi.write("\t\t.name = \"%s\",\n" % code_name)
249 fi.write("\t\t.code = &%s,\n" % code_name)
250
251 fi.write("\t\t.in_len = %d,\n" % self.block_len)
252 fi.write("\t\t.out_len = %d,\n" % self.calc_out_len())
253
254 # Print pre computed vectors if preset
255 if self.vec_in is not None and self.vec_out is not None:
256 fi.write("\t\t.has_vec = 1,\n")
257 fi.write("\t\t.vec_in = {\n")
258 print_formatted(self.vec_in, "0x%02x, ", 8, fi, indent = "\t\t\t")
259 fi.write("\t\t},\n")
260 fi.write("\t\t.vec_out = {\n")
261 print_formatted(self.vec_out, "0x%02x, ", 8, fi, indent = "\t\t\t")
262 fi.write("\t\t},\n")
263 else:
264 fi.write("\t\t.has_vec = 0,\n")
265 fi.write("\t\t.vec_in = { },\n")
266 fi.write("\t\t.vec_out = { },\n")
267
268 fi.write("\t},\n")
269
Harald Welteeea18a62016-04-29 15:18:35 +0200270poly = lambda *args: sum([(1 << x) for x in args])
271
272def combine(src, sel, nb):
273 x = src & sel
274 fn_xor = lambda x, y: x ^ y
275 return reduce(fn_xor, [(x >> n) & 1 for n in range(nb)])
276
Vadim Yanitskiy6908fa72016-09-07 22:34:53 +0700277def print_formatted(items, format, count, fi):
278 counter = 0
279
280 # Print initial indent
281 fi.write("\t")
282
283 for item in items:
284 if counter > 0 and counter % count == 0:
285 fi.write("\n\t")
286
287 fi.write(format % item)
288 counter += 1
289
290 fi.write("\n")
291
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700292def print_shared(fi, shared_polys):
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700293 for (name, polys) in shared_polys.items():
294 # HACK
295 code = ConvolutionalCode(0, polys, name = name)
296 code.print_state_and_output(fi)
297
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700298def generate_codes(codes, path, prefix, name):
Vadim Yanitskiyd2d97602016-09-07 22:18:10 +0700299 # Open a new file for writing
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700300 f = open(os.path.join(path, name), 'w')
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700301 f.write(mod_license + "\n")
302 f.write("#include <stdint.h>\n")
303 f.write("#include <osmocom/core/conv.h>\n\n")
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700304
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700305 sys.stderr.write("Generating convolutional codes...\n")
306
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700307 # Print shared tables first
308 if hasattr(codes, "shared_polys"):
309 print_shared(f, codes.shared_polys)
Harald Welteeea18a62016-04-29 15:18:35 +0200310
Vadim Yanitskiyd2d97602016-09-07 22:18:10 +0700311 # Generate the tables one by one
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700312 for code in codes.conv_codes:
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700313 sys.stderr.write("Generate '%s' definition\n" % code.name)
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700314
315 # Check whether shared polynomials are used
316 shared = None
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700317 if hasattr(codes, "shared_polys"):
318 for (name, polys) in codes.shared_polys.items():
319 if code.polys == polys:
320 shared = name
321 break
Vadim Yanitskiy6431add2016-10-29 00:00:57 +0700322
323 code.gen_tables(prefix, f, shared_tables = shared)
Vadim Yanitskiyd2d97602016-09-07 22:18:10 +0700324
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +0700325def generate_vectors(codes, path, prefix, name, inc = None):
326 # Open a new file for writing
327 f = open(os.path.join(path, name), 'w')
328 f.write(mod_license + "\n")
329
330 # Print includes
331 if inc is not None:
332 for item in inc:
333 f.write("%s\n" % item)
334 f.write("#include <osmocom/core/conv.h>\n")
335 f.write("#include \"conv.h\"\n\n")
336
337 sys.stderr.write("Generating test vectors...\n")
338
339 vec_count = len(codes.conv_codes)
340 f.write("const int %s_vectors_len = %d;\n\n"
341 % (prefix, vec_count))
342
343 f.write("const struct conv_test_vector %s_vectors[%d] = {\n"
344 % (prefix, vec_count))
345
346 # Generate the vectors one by one
347 for code in codes.conv_codes:
348 sys.stderr.write("Generate '%s' test vector\n" % code.name)
349 code.gen_test_vector(f, prefix)
350
351 f.write("};\n")
352
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700353def parse_argv():
354 parser = argparse.ArgumentParser()
355
356 # Positional arguments
357 parser.add_argument("action",
358 help = "what to generate",
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +0700359 choices = ["gen_codes", "gen_vectors"])
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700360 parser.add_argument("family",
361 help = "convolutional code family",
362 choices = ["gsm"])
363
364 # Optional arguments
365 parser.add_argument("-p", "--prefix",
366 help = "internal naming prefix")
367 parser.add_argument("-n", "--target-name",
368 help = "target name for generated file")
369 parser.add_argument("-P", "--target-path",
370 help = "target path for generated file")
371
372 return parser.parse_args()
373
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700374if __name__ == '__main__':
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700375 # Parse and verify arguments
376 argv = parse_argv()
377 path = argv.target_path or os.getcwd()
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +0700378 inc = None
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700379
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700380 # Determine convolutional code family
381 if argv.family == "gsm":
382 codes = conv_codes_gsm
383 prefix = argv.prefix or "gsm0503"
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +0700384 inc = [ "#include <osmocom/gsm/gsm0503.h>" ]
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700385
Vadim Yanitskiy2c717942017-01-13 02:23:01 +0700386 # What to generate?
387 if argv.action == "gen_codes":
388 name = argv.target_name or prefix + "_conv.c"
389 generate_codes(codes, path, prefix, name)
Vadim Yanitskiye9a90ee2017-01-13 03:43:58 +0700390 elif argv.action == "gen_vectors":
391 name = argv.target_name or prefix + "_test_vectors.c"
392 generate_vectors(codes, path, prefix, name, inc)
Vadim Yanitskiy15492bc2016-11-10 17:10:42 +0700393
Vadim Yanitskiy45ebc522016-10-27 02:19:37 +0700394 sys.stderr.write("Generation complete.\n")