blob: 06c4299b96ac85cf84bb2575440fedf1d6133ef6 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file conv.c
2 * Generic convolutional encoding / decoding. */
Sylvain Munaut19dc5c92011-04-23 16:09:19 +02003/*
Sylvain Munaut19dc5c92011-04-23 16:09:19 +02004 * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
5 *
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
9 *
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020010 * 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 2 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
Harald Welteba6988b2011-08-17 12:46:48 +020025/*! \addtogroup conv
26 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020027 * Osmocom convolutional encoder and decoder.
28 *
29 * \file conv.c */
Harald Welteba6988b2011-08-17 12:46:48 +020030
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010031#include "config.h"
32#ifdef HAVE_ALLOCA_H
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020033#include <alloca.h>
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010034#endif
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020035#include <stdint.h>
36#include <stdlib.h>
37#include <string.h>
38
Vadim Yanitskiya8809f02020-02-09 04:12:53 +070039#include <osmocom/core/utils.h>
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020040#include <osmocom/core/bits.h>
41#include <osmocom/core/conv.h>
42
43
44/* ------------------------------------------------------------------------ */
Sylvain Munautae8dbb42011-11-24 17:47:32 +010045/* Common */
46/* ------------------------------------------------------------------------ */
47
48int
49osmo_conv_get_input_length(const struct osmo_conv_code *code, int len)
50{
51 return len <= 0 ? code->len : len;
52}
53
54int
55osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
56{
57 int pbits, in_len, out_len;
58
59 /* Input length */
60 in_len = osmo_conv_get_input_length(code, len);
61
62 /* Output length */
63 out_len = in_len * code->N;
64
65 if (code->term == CONV_TERM_FLUSH)
66 out_len += code->N * (code->K - 1);
67
68 /* Count punctured bits */
69 if (code->puncture) {
70 for (pbits=0; code->puncture[pbits] >= 0; pbits++);
71 out_len -= pbits;
72 }
73
74 return out_len;
75}
76
77
78/* ------------------------------------------------------------------------ */
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020079/* Encoding */
80/* ------------------------------------------------------------------------ */
81
Neels Hofmeyr87e45502017-06-20 00:17:59 +020082/*! Initialize a convolutional encoder
Harald Welteba6988b2011-08-17 12:46:48 +020083 * \param[in,out] encoder Encoder state to initialize
84 * \param[in] code Description of convolutional code
85 */
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020086void
87osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
88 const struct osmo_conv_code *code)
89{
90 memset(encoder, 0x00, sizeof(struct osmo_conv_encoder));
Vadim Yanitskiya8809f02020-02-09 04:12:53 +070091 OSMO_ASSERT(code != NULL);
Sylvain Munaut19dc5c92011-04-23 16:09:19 +020092 encoder->code = code;
93}
94
Sylvain Munaut297d13f2011-11-24 17:46:58 +010095void
96osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
97 const ubit_t *input)
98{
99 int i;
100 uint8_t state = 0;
101
102 for (i=0; i<(encoder->code->K-1); i++)
103 state = (state << 1) | input[i];
104
105 encoder->state = state;
106}
107
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200108static inline int
109_conv_encode_do_output(struct osmo_conv_encoder *encoder,
110 uint8_t out, ubit_t *output)
111{
112 const struct osmo_conv_code *code = encoder->code;
113 int o_idx = 0;
114 int j;
115
116 if (code->puncture) {
117 for (j=0; j<code->N; j++)
118 {
119 int bit_no = code->N - j - 1;
120 int r_idx = encoder->i_idx * code->N + j;
121
122 if (code->puncture[encoder->p_idx] == r_idx)
123 encoder->p_idx++;
124 else
125 output[o_idx++] = (out >> bit_no) & 1;
126 }
127 } else {
128 for (j=0; j<code->N; j++)
129 {
130 int bit_no = code->N - j - 1;
131 output[o_idx++] = (out >> bit_no) & 1;
132 }
133 }
134
135 return o_idx;
136}
137
138int
139osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
140 const ubit_t *input, ubit_t *output, int n)
141{
142 const struct osmo_conv_code *code = encoder->code;
143 uint8_t state;
144 int i;
145 int o_idx;
146
147 o_idx = 0;
148 state = encoder->state;
149
150 for (i=0; i<n; i++) {
151 int bit = input[i];
152 uint8_t out;
153
154 out = code->next_output[state][bit];
155 state = code->next_state[state][bit];
156
157 o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
158
159 encoder->i_idx++;
160 }
161
162 encoder->state = state;
163
164 return o_idx;
165}
166
167int
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100168osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
169 ubit_t *output)
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200170{
171 const struct osmo_conv_code *code = encoder->code;
172 uint8_t state;
173 int n;
174 int i;
175 int o_idx;
176
177 n = code->K - 1;
178
179 o_idx = 0;
180 state = encoder->state;
181
182 for (i=0; i<n; i++) {
183 uint8_t out;
184
185 if (code->next_term_output) {
186 out = code->next_term_output[state];
187 state = code->next_term_state[state];
188 } else {
189 out = code->next_output[state][0];
190 state = code->next_state[state][0];
191 }
192
193 o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
194
195 encoder->i_idx++;
196 }
197
198 encoder->state = state;
199
200 return o_idx;
201}
202
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200203/*! All-in-one convolutional encoding function
Harald Welteba6988b2011-08-17 12:46:48 +0200204 * \param[in] code description of convolutional code to be used
205 * \param[in] input array of unpacked bits (uncoded)
206 * \param[out] output array of unpacked bits (encoded)
Sylvain Munaut03d2c892011-11-24 11:53:49 +0100207 * \return Number of produced output bits
Harald Welteba6988b2011-08-17 12:46:48 +0200208 *
209 * This is an all-in-one function, taking care of
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100210 * \ref osmo_conv_init, \ref osmo_conv_encode_load_state,
211 * \ref osmo_conv_encode_raw and \ref osmo_conv_encode_flush as needed.
Harald Welteba6988b2011-08-17 12:46:48 +0200212 */
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200213int
214osmo_conv_encode(const struct osmo_conv_code *code,
215 const ubit_t *input, ubit_t *output)
216{
217 struct osmo_conv_encoder encoder;
218 int l;
219
220 osmo_conv_encode_init(&encoder, code);
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100221
222 if (code->term == CONV_TERM_TAIL_BITING) {
223 int eidx = code->len - code->K + 1;
224 osmo_conv_encode_load_state(&encoder, &input[eidx]);
225 }
226
227 l = osmo_conv_encode_raw(&encoder, input, output, code->len);
228
229 if (code->term == CONV_TERM_FLUSH)
230 l += osmo_conv_encode_flush(&encoder, &output[l]);
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200231
232 return l;
233}
234
235
236/* ------------------------------------------------------------------------ */
237/* Decoding (viterbi) */
238/* ------------------------------------------------------------------------ */
239
240#define MAX_AE 0x00ffffff
241
Tom Tsou35536802016-11-24 19:24:32 +0700242/* Forward declaration for accerlated decoding with certain codes */
243int
244osmo_conv_decode_acc(const struct osmo_conv_code *code,
245 const sbit_t *input, ubit_t *output);
246
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200247void
248osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100249 const struct osmo_conv_code *code, int len, int start_state)
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200250{
251 int n_states;
252
253 /* Init */
254 if (len <= 0)
255 len = code->len;
256
257 n_states = 1 << (code->K - 1);
258
259 memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
260
261 decoder->code = code;
262 decoder->n_states = n_states;
263 decoder->len = len;
264
265 /* Allocate arrays */
266 decoder->ae = malloc(sizeof(unsigned int) * n_states);
267 decoder->ae_next = malloc(sizeof(unsigned int) * n_states);
268
269 decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1));
270
271 /* Classic reset */
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100272 osmo_conv_decode_reset(decoder, start_state);
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200273}
274
275void
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100276osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state)
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200277{
278 int i;
279
280 /* Reset indexes */
281 decoder->o_idx = 0;
282 decoder->p_idx = 0;
283
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100284 /* Initial error */
285 if (start_state < 0) {
286 /* All states possible */
287 memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states);
288 } else {
289 /* Fixed start state */
290 for (i=0; i<decoder->n_states; i++) {
291 decoder->ae[i] = (i == start_state) ? 0 : MAX_AE;
292 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200293 }
294}
295
296void
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100297osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder)
298{
299 int i;
300 unsigned int min_ae = MAX_AE;
301
302 /* Reset indexes */
303 decoder->o_idx = 0;
304 decoder->p_idx = 0;
305
306 /* Initial error normalize (remove constant) */
307 for (i=0; i<decoder->n_states; i++) {
308 if (decoder->ae[i] < min_ae)
309 min_ae = decoder->ae[i];
310 }
311
312 for (i=0; i<decoder->n_states; i++)
313 decoder->ae[i] -= min_ae;
314}
315
316void
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200317osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder)
318{
319 free(decoder->ae);
320 free(decoder->ae_next);
321 free(decoder->state_history);
322
323 memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
324}
325
326int
327osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
328 const sbit_t *input, int n)
329{
330 const struct osmo_conv_code *code = decoder->code;
331
332 int i, s, b, j;
333
334 int n_states;
335 unsigned int *ae;
336 unsigned int *ae_next;
337 uint8_t *state_history;
338 sbit_t *in_sym;
339
340 int i_idx, p_idx;
341
342 /* Prepare */
343 n_states = decoder->n_states;
344
345 ae = decoder->ae;
346 ae_next = decoder->ae_next;
347 state_history = &decoder->state_history[n_states * decoder->o_idx];
348
349 in_sym = alloca(sizeof(sbit_t) * code->N);
350
351 i_idx = 0;
352 p_idx = decoder->p_idx;
353
354 /* Scan the treillis */
355 for (i=0; i<n; i++)
356 {
357 /* Reset next accumulated error */
358 for (s=0; s<n_states; s++) {
359 ae_next[s] = MAX_AE;
360 }
361
362 /* Get input */
363 if (code->puncture) {
364 /* Hard way ... */
365 for (j=0; j<code->N; j++) {
366 int idx = ((decoder->o_idx + i) * code->N) + j;
367 if (idx == code->puncture[p_idx]) {
368 in_sym[j] = 0; /* Undefined */
369 p_idx++;
370 } else {
371 in_sym[j] = input[i_idx];
372 i_idx++;
373 }
374 }
375 } else {
376 /* Easy, just copy N bits */
377 memcpy(in_sym, &input[i_idx], code->N);
378 i_idx += code->N;
379 }
380
381 /* Scan all state */
382 for (s=0; s<n_states; s++)
383 {
384 /* Scan possible input bits */
385 for (b=0; b<2; b++)
386 {
387 int nae, ov, e;
388 uint8_t m;
389
390 /* Next output and state */
391 uint8_t out = code->next_output[s][b];
392 uint8_t state = code->next_state[s][b];
393
394 /* New error for this path */
395 nae = ae[s]; /* start from last error */
396 m = 1 << (code->N - 1); /* mask for 'out' bit selection */
397
398 for (j=0; j<code->N; j++) {
Sylvain Munautd4440d42011-11-24 16:04:58 +0100399 int is = (int)in_sym[j];
400 if (is) {
401 ov = (out & m) ? -127 : 127; /* sbit_t value for it */
402 e = is - ov; /* raw error for this bit */
403 nae += (e * e) >> 9; /* acc the squared/scaled value */
404 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200405 m >>= 1; /* next mask bit */
406 }
407
408 /* Is it survivor ? */
409 if (ae_next[state] > nae) {
410 ae_next[state] = nae;
411 state_history[(n_states * i) + state] = s;
412 }
413 }
414 }
415
416 /* Copy accumulated error */
417 memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
418 }
419
420 /* Update decoder state */
421 decoder->p_idx = p_idx;
422 decoder->o_idx += n;
423
424 return i_idx;
425}
426
427int
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100428osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
429 const sbit_t *input)
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200430{
431 const struct osmo_conv_code *code = decoder->code;
432
433 int i, s, j;
434
435 int n_states;
436 unsigned int *ae;
437 unsigned int *ae_next;
438 uint8_t *state_history;
439 sbit_t *in_sym;
440
441 int i_idx, p_idx;
442
443 /* Prepare */
444 n_states = decoder->n_states;
445
446 ae = decoder->ae;
447 ae_next = decoder->ae_next;
448 state_history = &decoder->state_history[n_states * decoder->o_idx];
449
450 in_sym = alloca(sizeof(sbit_t) * code->N);
451
452 i_idx = 0;
453 p_idx = decoder->p_idx;
454
455 /* Scan the treillis */
456 for (i=0; i<code->K-1; i++)
457 {
458 /* Reset next accumulated error */
459 for (s=0; s<n_states; s++) {
460 ae_next[s] = MAX_AE;
461 }
462
463 /* Get input */
464 if (code->puncture) {
465 /* Hard way ... */
466 for (j=0; j<code->N; j++) {
467 int idx = ((decoder->o_idx + i) * code->N) + j;
468 if (idx == code->puncture[p_idx]) {
469 in_sym[j] = 0; /* Undefined */
470 p_idx++;
471 } else {
472 in_sym[j] = input[i_idx];
473 i_idx++;
474 }
475 }
476 } else {
477 /* Easy, just copy N bits */
478 memcpy(in_sym, &input[i_idx], code->N);
479 i_idx += code->N;
480 }
481
482 /* Scan all state */
483 for (s=0; s<n_states; s++)
484 {
485 int nae, ov, e;
486 uint8_t m;
487
488 /* Next output and state */
489 uint8_t out;
490 uint8_t state;
491
492 if (code->next_term_output) {
493 out = code->next_term_output[s];
494 state = code->next_term_state[s];
495 } else {
496 out = code->next_output[s][0];
497 state = code->next_state[s][0];
498 }
499
500 /* New error for this path */
501 nae = ae[s]; /* start from last error */
502 m = 1 << (code->N - 1); /* mask for 'out' bit selection */
503
504 for (j=0; j<code->N; j++) {
Sylvain Munaut1dd7c842011-04-28 22:30:30 +0200505 int is = (int)in_sym[j];
506 if (is) {
507 ov = (out & m) ? -127 : 127; /* sbit_t value for it */
508 e = is - ov; /* raw error for this bit */
509 nae += (e * e) >> 9; /* acc the squared/scaled value */
510 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200511 m >>= 1; /* next mask bit */
512 }
513
514 /* Is it survivor ? */
515 if (ae_next[state] > nae) {
516 ae_next[state] = nae;
517 state_history[(n_states * i) + state] = s;
518 }
519 }
520
521 /* Copy accumulated error */
522 memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
523 }
524
525 /* Update decoder state */
526 decoder->p_idx = p_idx;
527 decoder->o_idx += code->K - 1;
528
529 return i_idx;
530}
531
532int
533osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100534 ubit_t *output, int has_flush, int end_state)
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200535{
536 const struct osmo_conv_code *code = decoder->code;
537
538 int min_ae;
539 uint8_t min_state, cur_state;
540 int i, s, n;
541
542 uint8_t *sh_ptr;
543
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100544 /* End state ? */
545 if (end_state < 0) {
546 /* Find state with least error */
547 min_ae = MAX_AE;
548 min_state = 0xff;
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200549
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100550 for (s=0; s<decoder->n_states; s++)
551 {
552 if (decoder->ae[s] < min_ae) {
553 min_ae = decoder->ae[s];
554 min_state = s;
555 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200556 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200557
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100558 if (min_state == 0xff)
559 return -1;
560 } else {
561 min_state = (uint8_t) end_state;
562 min_ae = decoder->ae[end_state];
563 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200564
565 /* Traceback */
566 cur_state = min_state;
567
568 n = decoder->o_idx;
569
570 sh_ptr = &decoder->state_history[decoder->n_states * (n-1)];
571
572 /* No output for the K-1 termination input bits */
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100573 if (has_flush) {
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200574 for (i=0; i<code->K-1; i++) {
575 cur_state = sh_ptr[cur_state];
576 sh_ptr -= decoder->n_states;
577 }
578 n -= code->K - 1;
579 }
580
581 /* Generate output backward */
582 for (i=n-1; i>=0; i--)
583 {
584 min_state = cur_state;
585 cur_state = sh_ptr[cur_state];
586
587 sh_ptr -= decoder->n_states;
588
589 if (code->next_state[cur_state][0] == min_state)
590 output[i] = 0;
591 else
592 output[i] = 1;
593 }
594
595 return min_ae;
596}
597
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200598/*! All-in-one convolutional decoding function
Harald Welteba6988b2011-08-17 12:46:48 +0200599 * \param[in] code description of convolutional code to be used
600 * \param[in] input array of soft bits (coded)
601 * \param[out] output array of unpacked bits (decoded)
602 *
603 * This is an all-in-one function, taking care of
604 * \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan,
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100605 * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and
Harald Welteba6988b2011-08-17 12:46:48 +0200606 * \ref osmo_conv_decode_deinit.
607 */
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200608int
609osmo_conv_decode(const struct osmo_conv_code *code,
610 const sbit_t *input, ubit_t *output)
611{
612 struct osmo_conv_decoder decoder;
613 int rv, l;
614
Tom Tsou35536802016-11-24 19:24:32 +0700615 /* Use accelerated implementation for supported codes */
616 if ((code->N <= 4) && ((code->K == 5) || (code->K == 7)))
617 return osmo_conv_decode_acc(code, input, output);
618
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100619 osmo_conv_decode_init(&decoder, code, 0, 0);
620
621 if (code->term == CONV_TERM_TAIL_BITING) {
622 osmo_conv_decode_scan(&decoder, input, code->len);
623 osmo_conv_decode_rewind(&decoder);
624 }
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200625
626 l = osmo_conv_decode_scan(&decoder, input, code->len);
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200627
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100628 if (code->term == CONV_TERM_FLUSH)
Vadim Yanitskiy6e6978a2017-06-12 03:47:34 +0700629 osmo_conv_decode_flush(&decoder, &input[l]);
Sylvain Munaut297d13f2011-11-24 17:46:58 +0100630
631 rv = osmo_conv_decode_get_output(&decoder, output,
632 code->term == CONV_TERM_FLUSH, /* has_flush */
633 code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */
634 );
Sylvain Munaut19dc5c92011-04-23 16:09:19 +0200635
636 osmo_conv_decode_deinit(&decoder);
637
638 return rv;
639}
Harald Welteba6988b2011-08-17 12:46:48 +0200640
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200641/*! @} */