From bcdf2f088653feb57ce785ebfb7129eca95654b6 Mon Sep 17 00:00:00 2001 From: ismagom Date: Tue, 13 May 2014 16:03:39 +0100 Subject: [PATCH] Added Turbo Decoder --- lte/include/lte/fec/tc_interl.h | 41 ++++ lte/include/lte/fec/turbocoder.h | 45 ++++ lte/include/lte/fec/turbodecoder.h | 44 ++++ lte/include/lte/fec/turbodecoder.h2 | 68 ++++++ lte/lib/fec/src/tc_interl_lte.c | 117 ++++++++++ lte/lib/fec/src/tc_interl_umts.c | 257 ++++++++++++++++++++++ lte/lib/fec/src/turbocoder.c | 134 ++++++++++++ lte/lib/fec/src/turbodecoder.c | 299 +++++++++++++++++++++++++ lte/lib/fec/src/turbodecoder.c2 | 304 ++++++++++++++++++++++++++ lte/lib/fec/test/turbocoder_test.c | 324 ++++++++++++++++++++++++++++ lte/lib/fec/test/turbocoder_test.h | 168 +++++++++++++++ 11 files changed, 1801 insertions(+) create mode 100644 lte/include/lte/fec/tc_interl.h create mode 100644 lte/include/lte/fec/turbocoder.h create mode 100644 lte/include/lte/fec/turbodecoder.h create mode 100644 lte/include/lte/fec/turbodecoder.h2 create mode 100644 lte/lib/fec/src/tc_interl_lte.c create mode 100644 lte/lib/fec/src/tc_interl_umts.c create mode 100644 lte/lib/fec/src/turbocoder.c create mode 100644 lte/lib/fec/src/turbodecoder.c create mode 100644 lte/lib/fec/src/turbodecoder.c2 create mode 100644 lte/lib/fec/test/turbocoder_test.c create mode 100644 lte/lib/fec/test/turbocoder_test.h diff --git a/lte/include/lte/fec/tc_interl.h b/lte/include/lte/fec/tc_interl.h new file mode 100644 index 000000000..1fbaa8293 --- /dev/null +++ b/lte/include/lte/fec/tc_interl.h @@ -0,0 +1,41 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef _PERMUTE_H +#define _PERMUTE_H + +typedef struct { + int *forward; + int *reverse; +}tc_interl_t; + +int tc_interl_LTE_init(tc_interl_t *h, int long_cb); +int tc_interl_UMTS_init(tc_interl_t *h, int long_cb); + +void tc_interl_free(tc_interl_t *h); + +#endif diff --git a/lte/include/lte/fec/turbocoder.h b/lte/include/lte/fec/turbocoder.h new file mode 100644 index 000000000..ea215972d --- /dev/null +++ b/lte/include/lte/fec/turbocoder.h @@ -0,0 +1,45 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#define NUMREGS 3 + +#define RATE 3 +#define TOTALTAIL 12 + +typedef struct { + int long_cb; + tc_interl_t interl; + +}tcod_t; + +int tcod_init(tcod_t *h, int long_cb); +void tcod_free(tcod_t *h); +void tcod_encode(tcod_t *h, char *input, char *output); + + + diff --git a/lte/include/lte/fec/turbodecoder.h b/lte/include/lte/fec/turbodecoder.h new file mode 100644 index 000000000..fc97de5cd --- /dev/null +++ b/lte/include/lte/fec/turbodecoder.h @@ -0,0 +1,44 @@ +#define RATE 3 +#define TOTALTAIL 12 + +#define LOG18 -2.07944 + +#define NUMSTATES 8 +#define NINPUTS 2 +#define TAIL 3 +#define TOTALTAIL 12 + +#define INF 9e4 +#define ZERO 9e-4 + +#define MAX_LONG_CB 6114 +#define MAX_LONG_CODED (RATE*MAX_LONG_CB+TOTALTAIL) + +typedef float llr_t; + +typedef struct { + int long_cb; + llr_t *beta; +}map_gen_t; + +typedef struct { + int long_cb; + + map_gen_t dec; + + llr_t *llr1; + llr_t *llr2; + llr_t *w; + llr_t *syst; + llr_t *parity; + + tc_interl_t interleaver; +}tdec_t; + +int tdec_init(tdec_t *h, int long_cb); +void tdec_free(tdec_t *h); + +void tdec_reset(tdec_t *h); +void tdec_iteration(tdec_t *h, llr_t *input); +void tdec_decision(tdec_t *h, char *output); +void tdec_run_all(tdec_t *h, llr_t *input, char *output, int nof_iterations); diff --git a/lte/include/lte/fec/turbodecoder.h2 b/lte/include/lte/fec/turbodecoder.h2 new file mode 100644 index 000000000..003cfbbde --- /dev/null +++ b/lte/include/lte/fec/turbodecoder.h2 @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define RATE 3 +#define TOTALTAIL 12 + +#define LOG18 -2.07944 + +#define NUMSTATES 8 +#define NINPUTS 2 +#define TOTALTAIL 12 + +#define SCALE 80 + +#define INF 9e4 +#define ZERO 9e-4 + +#define MAX_LONG_CB 6114 +#define MAX_LONG_CODED (RATE*MAX_LONG_CB+TOTALTAIL) + +typedef float llr_t; + +typedef struct { + int long_cb; + int max_iterations; + int halt_threshold; + enum { HALT_MIN = 2, HALT_MEAN = 1, HALT_NONE = 0} halt_mode; + llr_t alfa[NUMSTATES]; + llr_t beta[(MAX_LONG_CB + 1) * NUMSTATES]; + llr_t LLR1[MAX_LONG_CB + TOTALTAIL]; + llr_t LLR2[MAX_LONG_CB + TOTALTAIL]; + llr_t W[MAX_LONG_CB + TOTALTAIL]; + llr_t data[RATE*(MAX_LONG_CB + TOTALTAIL)]; + llr_t *parity; + struct permute_t permuta; + int iteration; + int HALT_min; + +}tdec_t; + +int tdec_init(tdec_t *h); + +int turbo_decoder(tdec_t *h, llr_t *input, char *output, int *halt); + diff --git a/lte/lib/fec/src/tc_interl_lte.c b/lte/lib/fec/src/tc_interl_lte.c new file mode 100644 index 000000000..1db75cf92 --- /dev/null +++ b/lte/lib/fec/src/tc_interl_lte.c @@ -0,0 +1,117 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include + +#include "lte/common/base.h" +#include "lte/fec/tc_interl.h" +#include "lte/fec/turbocoder.h" +#include "lte/utils/debug.h" + +/************************************************ + * + * LTE TURBO CODE INTERLEAVER + * + ************************************************/ + +const int f1_list[NOF_TC_CB_SIZES] = { 3, 7, 19, 7, 7, 11, 5, 11, 7, 41, 103, 15, 9, 17, + 9, 21, 101, 21, 57, 23, 13, 27, 11, 27, 85, 29, 33, 15, 17, 33, 103, 19, + 19, 37, 19, 21, 21, 115, 193, 21, 133, 81, 45, 23, 243, 151, 155, 25, + 51, 47, 91, 29, 29, 247, 29, 89, 91, 157, 55, 31, 17, 35, 227, 65, 19, + 37, 41, 39, 185, 43, 21, 155, 79, 139, 23, 217, 25, 17, 127, 25, 239, + 17, 137, 215, 29, 15, 147, 29, 59, 65, 55, 31, 17, 171, 67, 35, 19, 39, + 19, 199, 21, 211, 21, 43, 149, 45, 49, 71, 13, 17, 25, 183, 55, 127, 27, + 29, 29, 57, 45, 31, 59, 185, 113, 31, 17, 171, 209, 253, 367, 265, 181, + 39, 27, 127, 143, 43, 29, 45, 157, 47, 13, 111, 443, 51, 51, 451, 257, + 57, 313, 271, 179, 331, 363, 375, 127, 31, 33, 43, 33, 477, 35, 233, + 357, 337, 37, 71, 71, 37, 39, 127, 39, 39, 31, 113, 41, 251, 43, 21, 43, + 45, 45, 161, 89, 323, 47, 23, 47, 263 }; + +const int f2_list[NOF_TC_CB_SIZES] = { 10, 12, 42, 16, 18, 20, 22, 24, 26, 84, 90, 32, + 34, 108, 38, 120, 84, 44, 46, 48, 50, 52, 36, 56, 58, 60, 62, 32, 198, + 68, 210, 36, 74, 76, 78, 120, 82, 84, 86, 44, 90, 46, 94, 48, 98, 40, + 102, 52, 106, 72, 110, 168, 114, 58, 118, 180, 122, 62, 84, 64, 66, 68, + 420, 96, 74, 76, 234, 80, 82, 252, 86, 44, 120, 92, 94, 48, 98, 80, 102, + 52, 106, 48, 110, 112, 114, 58, 118, 60, 122, 124, 84, 64, 66, 204, 140, + 72, 74, 76, 78, 240, 82, 252, 86, 88, 60, 92, 846, 48, 28, 80, 102, 104, + 954, 96, 110, 112, 114, 116, 354, 120, 610, 124, 420, 64, 66, 136, 420, + 216, 444, 456, 468, 80, 164, 504, 172, 88, 300, 92, 188, 96, 28, 240, + 204, 104, 212, 192, 220, 336, 228, 232, 236, 120, 244, 248, 168, 64, + 130, 264, 134, 408, 138, 280, 142, 480, 146, 444, 120, 152, 462, 234, + 158, 80, 96, 902, 166, 336, 170, 86, 174, 176, 178, 120, 182, 184, 186, + 94, 190, 480 }; + +int tc_interl_LTE_init(tc_interl_t *h, int long_cb) { + int cb_table_idx, f1, f2; + unsigned long long i, j; + + cb_table_idx = lte_find_cb_index(long_cb); + if (cb_table_idx == -1) { + fprintf(stderr, "Can't find long_cb=%d in valid TC CB table\n", long_cb); + return -1; + } + + h->forward = h->reverse = NULL; + h->forward = malloc(sizeof(int) * (long_cb)); + if (!h->forward) { + return -1; + } + h->reverse = malloc(sizeof(int) * (long_cb)); + if (!h->reverse) { + perror("malloc"); + free(h->forward); + h->forward = h->reverse = NULL; + return -1; + } + + f1 = f1_list[cb_table_idx]; + f2 = f2_list[cb_table_idx]; + + DEBUG("table_idx: %d, f1: %d, f2: %d\n", cb_table_idx, f1, f2); + + h->forward[0] = 0; + h->reverse[0] = 0; + for (i = 1; i < long_cb; i++) { + j = (f1*i + f2*i*i) % (long_cb); + h->forward[i] = j; + h->reverse[j] = i; + } + return 0; + +} + + + + + + + + + + + diff --git a/lte/lib/fec/src/tc_interl_umts.c b/lte/lib/fec/src/tc_interl_umts.c new file mode 100644 index 000000000..a71d2f0e0 --- /dev/null +++ b/lte/lib/fec/src/tc_interl_umts.c @@ -0,0 +1,257 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include + +#include "lte/fec/tc_interl.h" +#include "lte/fec/turbocoder.h" + +#define TURBO_RATE 3 + +int mcd(int x, int y); + +/************************************************ + * + * UMTS TURBO CODE INTERLEAVER + * + ************************************************/ + +#define MAX_ROWS 20 +#define MAX_COLS 256 + +const unsigned short table_p[52] = { 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, + 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257 }; +const unsigned char table_v[52] = { 3, 2, 2, 3, 2, 5, 2, 3, 2, 6, 3, 5, 2, 2, 2, + 2, 7, 5, 3, 2, 3, 5, 2, 5, 2, 6, 3, 3, 2, 3, 2, 2, 6, 5, 2, 5, 2, 2, 2, + 19, 5, 2, 3, 2, 3, 2, 6, 3, 7, 7, 6, 3 }; + +void tc_interl_free(tc_interl_t *h) { + if (h->forward) { + free(h->forward); + } + if (h->reverse) { + free(h->reverse); + } + h->forward = h->reverse = NULL; +} + +int tc_interl_UMTS_init(tc_interl_t *h, int long_cb) { + + int i, j; + int res, prim, aux; + int kp, k; + int *per, *desper; + unsigned char v; + unsigned short p; + unsigned short s[MAX_COLS], q[MAX_ROWS], r[MAX_ROWS], T[MAX_ROWS]; + unsigned short U[MAX_COLS * MAX_ROWS]; + int M_Rows, M_Cols, M_long; + + h->forward = h->reverse = NULL; + h->forward = malloc(sizeof(int) * (long_cb)); + if (!h->forward) { + return -1; + } + h->reverse = malloc(sizeof(int) * (long_cb)); + if (!h->reverse) { + perror("malloc"); + free(h->forward); + h->forward = h->reverse = NULL; + return -1; + } + M_long = long_cb; + + /* Find R*/ + if ((40 <= M_long) && (M_long <= 159)) + M_Rows = 5; + else if (((160 <= M_long) && (M_long <= 200)) + || ((481 <= M_long) && (M_long <= 530))) + M_Rows = 10; + else + M_Rows = 20; + + /* Find p i v*/ + if ((481 <= M_long) && (M_long <= 530)) { + p = 53; + v = 2; + M_Cols = p; + } else { + i = 0; + do { + p = table_p[i]; + v = table_v[i]; + i++; + } while (M_long > (M_Rows * (p + 1))); + + } + + /* Find C*/ + if ((M_long) <= (M_Rows) * ((p) - 1)) + M_Cols = (p) - 1; + else if (((M_Rows) * (p - 1) < M_long) && (M_long <= (M_Rows) * (p))) + M_Cols = p; + else if ((M_Rows) * (p) < M_long) + M_Cols = (p) + 1; + + q[0] = 1; + prim = 6; + + for (i = 1; i < M_Rows; i++) { + do { + prim++; + res = mcd(prim, p - 1); + } while (res != 1); + q[i] = prim; + } + + s[0] = 1; + for (i = 1; i < p - 1; i++) { + s[i] = (v * s[i - 1]) % p; + } + + if (M_long <= 159 && M_long >= 40) { + T[0] = 4; + T[1] = 3; + T[2] = 2; + T[3] = 1; + T[4] = 0; + } else if ((M_long <= 200 && M_long >= 160) + || (M_long <= 530 && M_long >= 481)) { + T[0] = 9; + T[1] = 8; + T[2] = 7; + T[3] = 6; + T[4] = 5; + T[5] = 4; + T[6] = 3; + T[7] = 2; + T[8] = 1; + T[9] = 0; + } else if ((M_long <= 2480 && M_long >= 2281) + || (M_long <= 3210 && M_long >= 3161)) { + T[0] = 19; + T[1] = 9; + T[2] = 14; + T[3] = 4; + T[4] = 0; + T[5] = 2; + T[6] = 5; + T[7] = 7; + T[8] = 12; + T[9] = 18; + T[10] = 16; + T[11] = 13; + T[12] = 17; + T[13] = 15; + T[14] = 3; + T[15] = 1; + T[16] = 6; + T[17] = 11; + T[18] = 8; + T[19] = 10; + } else { + T[0] = 19; + T[1] = 9; + T[2] = 14; + T[3] = 4; + T[4] = 0; + T[5] = 2; + T[6] = 5; + T[7] = 7; + T[8] = 12; + T[9] = 18; + T[10] = 10; + T[11] = 8; + T[12] = 13; + T[13] = 17; + T[14] = 3; + T[15] = 1; + T[16] = 16; + T[17] = 6; + T[18] = 15; + T[19] = 11; + } + + for (i = 0; i < M_Rows; i++) { + r[T[i]] = q[i]; + } + + for (i = 0; i < M_Rows; i++) { + for (j = 0; j < p - 1; j++) { + U[i * M_Cols + j] = s[(j * r[i]) % (p - 1)]; + if (M_Cols == (p - 1)) + U[i * M_Cols + j] -= 1; + } + } + + if (M_Cols == p) { + for (i = 0; i < M_Rows; i++) + U[i * M_Cols + p - 1] = 0; + } else if (M_Cols == p + 1) { + for (i = 0; i < M_Rows; i++) { + U[i * M_Cols + p - 1] = 0; + U[i * M_Cols + p] = p; + } + if (M_long == M_Cols * M_Rows) { + aux = U[(M_Rows - 1) * M_Cols + p]; + U[(M_Rows - 1) * M_Cols + p] = U[(M_Rows - 1) * M_Cols + 0]; + U[(M_Rows - 1) * M_Cols + 0] = aux; + } + } + + per = h->forward; + desper = h->reverse; + + k = 0; + for (j = 0; j < M_Cols; j++) { + for (i = 0; i < M_Rows; i++) { + kp = T[i] * M_Cols + U[i * M_Cols + j]; + if (kp < M_long) { + desper[kp] = k; + per[k] = kp; + k++; + } + } + } + + return 0; + +} + +int mcd(int x, int y) { + int r = 1; + + while (r) { + r = x % y; + x = y; + y = r; + } + return x; +} diff --git a/lte/lib/fec/src/turbocoder.c b/lte/lib/fec/src/turbocoder.c new file mode 100644 index 000000000..4ff72df26 --- /dev/null +++ b/lte/lib/fec/src/turbocoder.c @@ -0,0 +1,134 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include "lte/fec/tc_interl.h" +#include "lte/fec/turbocoder.h" + +#define NOF_REGS 3 + +int tcod_init(tcod_t *h, int long_cb) { + + if (tc_interl_LTE_init(&h->interl, long_cb)) { + return -1; + } + h->long_cb = long_cb; + return 0; +} + +void tcod_free(tcod_t *h) { + tc_interl_free(&h->interl); + h->long_cb = 0; +} + +void tcod_encode(tcod_t *h, char *input, char *output) { + + char reg1_0,reg1_1,reg1_2, reg2_0,reg2_1,reg2_2; + int i,k=0,j; + char bit; + char in,out; + int *per; + + per=h->interl.forward; + + reg1_0=0; + reg1_1=0; + reg1_2=0; + + reg2_0=0; + reg2_1=0; + reg2_2=0; + + k=0; + for (i=0;ilong_cb;i++) { + bit=input[i]; + + output[k]=bit; + k++; + + in=bit^(reg1_2^reg1_1); + out=reg1_2^(reg1_0^in); + + reg1_2=reg1_1; + reg1_1=reg1_0; + reg1_0=in; + + output[k]=out; + k++; + + bit=input[per[i]]; + + in=bit^(reg2_2^reg2_1); + out=reg2_2^(reg2_0^in); + + reg2_2=reg2_1; + reg2_1=reg2_0; + reg2_0=in; + + output[k]=out; + k++; + } + + k=3*h->long_cb; + + /* TAILING CODER #1 */ + for (j=0;j +#include +#include +#include + +#include "lte/fec/tc_interl.h" +#include "lte/fec/turbodecoder.h" + +/************************************************ + * + * MAP_GEN is the MAX-LOG-MAP generic implementation of the + * Decoder + * + ************************************************/ +void map_gen_beta(map_gen_t *s, llr_t *input, llr_t *parity) { + llr_t m_b[8], new[8], old[8]; + llr_t x, y, xy; + int k; + int end = s->long_cb + RATE; + llr_t *beta = s->beta; + int i; + + for (i=0;i<8;i++) { + old[i] = beta[8 * (end) + i]; + } + + for (k = end - 1; k >= 0; k--) { + x = input[k]; + y = parity[k]; + + xy = x + y; + + m_b[0] = old[4] + xy; + m_b[1] = old[4]; + m_b[2] = old[5] + y; + m_b[3] = old[5] + x; + m_b[4] = old[6] + x; + m_b[5] = old[6] + y; + m_b[6] = old[7]; + m_b[7] = old[7] + xy; + + new[0] = old[0]; + new[1] = old[0] + xy; + new[2] = old[1] + x; + new[3] = old[1] + y; + new[4] = old[2] + y; + new[5] = old[2] + x; + new[6] = old[3] + xy; + new[7] = old[3]; + + for (i=0;i<8;i++) { + if (m_b[i] > new[i]) + new[i] = m_b[i]; + beta[8 * k + i] = new[i]; + old[i] = new[i]; + } + } +} + +void map_gen_alpha(map_gen_t *s, llr_t *input, llr_t *parity, llr_t *output) { + llr_t m_b[8], new[8], old[8], max1[8], max0[8]; + llr_t m1, m0; + llr_t x, y, xy; + llr_t out; + int k; + int end = s->long_cb; + llr_t *beta = s->beta; + int i; + + old[0] = 0; + for (i=1;i<8;i++) { + old[i] = -INF; + } + + for (k = 1; k < end + 1; k++) { + x = input[k - 1]; + y = parity[k - 1]; + + xy = x + y; + + m_b[0] = old[0]; + m_b[1] = old[3] + y; + m_b[2] = old[4] + y; + m_b[3] = old[7]; + m_b[4] = old[1]; + m_b[5] = old[2] + y; + m_b[6] = old[5] + y; + m_b[7] = old[6]; + + new[0] = old[1] + xy; + new[1] = old[2] + x; + new[2] = old[5] + x; + new[3] = old[6] + xy; + new[4] = old[0] + xy; + new[5] = old[3] + x; + new[6] = old[4] + x; + new[7] = old[7] + xy; + + for (i=0;i<8;i++) { + max0[i] = m_b[i] + beta[8 * k + i]; + max1[i] = new[i] + beta[8 * k + i]; + } + + m1 = max1[0]; + m0 = max0[0]; + + for (i=1;i<8;i++) { + if (max1[i] > m1) + m1 = max1[i]; + if (max0[i] > m0) + m0 = max0[i]; + } + + for (i=0;i<8;i++) { + if (m_b[i] > new[i]) + new[i] = m_b[i]; + old[i] = new[i]; + } + + out = m1 - m0; + output[k - 1] = out; + } +} + +int map_gen_init(map_gen_t *h, int long_cb) { + bzero(h, sizeof(map_gen_t)); + h->beta = malloc(sizeof(llr_t) * (long_cb + TOTALTAIL + 1)* NUMSTATES); + if (!h->beta) { + perror("malloc"); + return -1; + } + h->long_cb = long_cb; + return 0; +} + +void map_gen_free(map_gen_t *h) { + if (h->beta) { + free(h->beta); + } + bzero(h, sizeof(map_gen_t)); +} + +void map_gen_dec(map_gen_t *h, llr_t *input, llr_t *parity, llr_t *output) { + int k; + + h->beta[(h->long_cb + TAIL) * NUMSTATES] = 0; + for (k = 1; k < NUMSTATES; k++) + h->beta[(h->long_cb + TAIL) * NUMSTATES + k] = -INF; + + map_gen_beta(h, input, parity); + map_gen_alpha(h, input, parity, output); +} + + + + + + + + + +/************************************************ + * + * TURBO DECODER INTERFACE + * + ************************************************/ +int tdec_init(tdec_t *h, int long_cb) { + int ret = -1; + bzero(h, sizeof(tdec_t)); + int len = long_cb + TOTALTAIL; + + h->llr1 = malloc(sizeof(llr_t) * len); + if (!h->llr1) { + perror("malloc"); + goto clean_and_exit; + } + h->llr2 = malloc(sizeof(llr_t) * len); + if (!h->llr2) { + perror("malloc"); + goto clean_and_exit; + } + h->w = malloc(sizeof(llr_t) * len); + if (!h->w) { + perror("malloc"); + goto clean_and_exit; + } + h->syst = malloc(sizeof(llr_t) * len); + if (!h->syst) { + perror("malloc"); + goto clean_and_exit; + } + h->parity = malloc(sizeof(llr_t) * len); + if (!h->parity) { + perror("malloc"); + goto clean_and_exit; + } + + if (map_gen_init(&h->dec, long_cb)) { + goto clean_and_exit; + } + + h->long_cb = long_cb; + + if (tc_interl_LTE_init(&h->interleaver, h->long_cb) < 0) { + goto clean_and_exit; + } + + ret = 0; +clean_and_exit: + if (ret == -1) { + tdec_free(h); + } + return ret; +} + +void tdec_free(tdec_t *h) { + if (h->llr1) { + free(h->llr1); + } + if (h->llr2) { + free(h->llr2); + } + if (h->w) { + free(h->w); + } + if (h->syst) { + free(h->syst); + } + if (h->parity) { + free(h->parity); + } + + map_gen_free(&h->dec); + + tc_interl_free(&h->interleaver); + + bzero(h, sizeof(tdec_t)); +} + +void tdec_iteration(tdec_t *h, llr_t *input) { + int i; + + // Prepare systematic and parity bits for MAP DEC #1 + for (i = 0; i < h->long_cb; i++) { + h->syst[i] = input[RATE * i] + h->w[i]; + h->parity[i] = input[RATE * i + 1]; + } + for (i=h->long_cb;ilong_cb+RATE;i++) { + h->syst[i] = input[RATE * h->long_cb + NINPUTS * (i - h->long_cb)]; + h->parity[i] = input[RATE * h->long_cb + NINPUTS * (i - h->long_cb) + 1]; + } + + // Run MAP DEC #1 + map_gen_dec(&h->dec, h->syst, h->parity, h->llr1); + + // Prepare systematic and parity bits for MAP DEC #1 + for (i = 0; i < h->long_cb; i++) { + h->syst[i] = h->llr1[h->interleaver.forward[i]] - h->w[h->interleaver.forward[i]]; + h->parity[i] = input[RATE * i + 2]; + } + for (i=h->long_cb;ilong_cb+RATE;i++) { + h->syst[i] = input[RATE * h->long_cb + NINPUTS * RATE + NINPUTS * (i - h->long_cb)]; + h->parity[i] = input[RATE * h->long_cb + NINPUTS * RATE + NINPUTS * (i - h->long_cb) + 1]; + } + + // Run MAP DEC #1 + map_gen_dec(&h->dec, h->syst, h->parity, h->llr2); + + // Update a-priori LLR from the last iteration + for (i = 0; i < h->long_cb; i++) { + h->w[i] += h->llr2[h->interleaver.reverse[i]] - h->llr1[i]; + } + +} + +void tdec_reset(tdec_t *h) { + memset(h->w, 0, sizeof(llr_t) * h->long_cb); +} + +void tdec_decision(tdec_t *h, char *output) { + int i; + for (i = 0; i < h->long_cb; i++) { + output[i] = (h->llr2[h->interleaver.reverse[i]] > 0) ? 1 : 0; + } +} + +void tdec_run_all(tdec_t *h, llr_t *input, char *output, int nof_iterations) { + int iter = 0; + + tdec_reset(h); + + do { + tdec_iteration(h, input); + iter++; + } while (iter < nof_iterations); + + tdec_decision(h, output); +} + diff --git a/lte/lib/fec/src/turbodecoder.c2 b/lte/lib/fec/src/turbodecoder.c2 new file mode 100644 index 000000000..a146236da --- /dev/null +++ b/lte/lib/fec/src/turbodecoder.c2 @@ -0,0 +1,304 @@ +#include +#include +#include + +#include "lte/fec/permute.h" + +#include "lte/fec/turbodecoder.h" + + +void compute_beta(llr_t *beta, llr_t *data, llr_t *parity, int long_cb, int dec) { + llr_t m_b0, m_b1, m_b2, m_b3, m_b4, m_b5, m_b6, m_b7; + llr_t new0, new1, new2, new3, new4, new5, new6, new7; + llr_t old0, old1, old2, old3, old4, old5, old6, old7; + + llr_t x, y, xy; + int k; + int end = long_cb + RATE; + + old0 = beta[8 * (end) + 0]; + old1 = beta[8 * (end) + 1]; + old2 = beta[8 * (end) + 2]; + old3 = beta[8 * (end) + 3]; + old4 = beta[8 * (end) + 4]; + old5 = beta[8 * (end) + 5]; + old6 = beta[8 * (end) + 6]; + old7 = beta[8 * (end) + 7]; + + for (k = end - 1; k >= 0; k--) { + if (k > long_cb - 1) { + if (dec == 1) { + x = data[RATE * (long_cb ) + NINPUTS * (k - long_cb)]; + y = data[RATE * (long_cb ) + NINPUTS * (k - long_cb) + 1]; + } else { + x = data[RATE * (long_cb ) + NINPUTS * RATE + NINPUTS * (k - long_cb)]; + y = data[RATE * (long_cb ) + NINPUTS * RATE + NINPUTS * (k - long_cb) + 1]; + } + } else { + x = data[RATE * k]; + y = parity[RATE * k]; + } + xy = x + y; + + m_b0 = old4 + xy; + m_b1 = old4; + m_b2 = old5 + y; + m_b3 = old5 + x; + m_b4 = old6 + x; + m_b5 = old6 + y; + m_b6 = old7; + m_b7 = old7 + xy; + + new0 = old0; + new1 = old0 + xy; + new2 = old1 + x; + new3 = old1 + y; + new4 = old2 + y; + new5 = old2 + x; + new6 = old3 + xy; + new7 = old3; + + if (m_b0 > new0) new0 = m_b0; + beta[8 * k + 0] = new0; + old0 = new0; + + if (m_b1 > new1) new1 = m_b1; + beta[8 * k + 1] = new1; + old1 = new1; + + if (m_b2 > new2) new2 = m_b2; + beta[8 * k + 2] = new2; + old2 = new2; + + if (m_b3 > new3) new3 = m_b3; + beta[8 * k + 3] = new3; + old3 = new3; + + if (m_b4 > new4) new4 = m_b4; + beta[8 * k + 4] = new4; + old4 = new4; + + if (m_b5 > new5) new5 = m_b5; + beta[8 * k + 5] = new5; + old5 = new5; + + if (m_b6 > new6) new6 = m_b6; + beta[8 * k + 6] = new6; + old6 = new6; + + if (m_b7 > new7) new7 = m_b7; + beta[8 * k + 7] = new7; + old7 = new7; + + } + +} + +void compute_alfa(llr_t *alfa, llr_t *beta, llr_t *data, llr_t *parity, llr_t *output, int long_cb, int dec) { + llr_t m_b0, m_b1, m_b2, m_b3, m_b4, m_b5, m_b6, m_b7; + llr_t new0, new1, new2, new3, new4, new5, new6, new7; + llr_t old0, old1, old2, old3, old4, old5, old6, old7; + llr_t max1_0, max1_1, max1_2, max1_3, max1_4, max1_5, max1_6, max1_7; + llr_t max0_0, max0_1, max0_2, max0_3, max0_4, max0_5, max0_6, max0_7; + llr_t m1, m0; + llr_t x, y, xy; + llr_t out; + int k; + int end = long_cb; + + old0 = alfa[0]; + old1 = alfa[1]; + old2 = alfa[2]; + old3 = alfa[3]; + old4 = alfa[4]; + old5 = alfa[5]; + old6 = alfa[6]; + old7 = alfa[7]; + + for (k = 1; k < end + 1; k++) { + x = data[RATE * (k - 1)]; + y = parity[RATE * (k - 1)]; + + xy = x + y; + + m_b0 = old0; + m_b1 = old3 + y; + m_b2 = old4 + y; + m_b3 = old7; + m_b4 = old1; + m_b5 = old2 + y; + m_b6 = old5 + y; + m_b7 = old6; + + new0 = old1 + xy; + new1 = old2 + x; + new2 = old5 + x; + new3 = old6 + xy; + new4 = old0 + xy; + new5 = old3 + x; + new6 = old4 + x; + new7 = old7 + xy; + + max0_0 = m_b0 + beta[8 * k + 0]; + max0_1 = m_b1 + beta[8 * k + 1]; + max0_2 = m_b2 + beta[8 * k + 2]; + max0_3 = m_b3 + beta[8 * k + 3]; + max0_4 = m_b4 + beta[8 * k + 4]; + max0_5 = m_b5 + beta[8 * k + 5]; + max0_6 = m_b6 + beta[8 * k + 6]; + max0_7 = m_b7 + beta[8 * k + 7]; + + max1_0 = new0 + beta[8 * k + 0]; + max1_1 = new1 + beta[8 * k + 1]; + max1_2 = new2 + beta[8 * k + 2]; + max1_3 = new3 + beta[8 * k + 3]; + max1_4 = new4 + beta[8 * k + 4]; + max1_5 = new5 + beta[8 * k + 5]; + max1_6 = new6 + beta[8 * k + 6]; + max1_7 = new7 + beta[8 * k + 7]; + + m1 = max1_0; + if (max1_1 > m1) m1 = max1_1; + if (max1_2 > m1) m1 = max1_2; + if (max1_3 > m1) m1 = max1_3; + if (max1_4 > m1) m1 = max1_4; + if (max1_5 > m1) m1 = max1_5; + if (max1_6 > m1) m1 = max1_6; + if (max1_7 > m1) m1 = max1_7; + + m0 = max0_0; + if (max0_1 > m0) m0 = max0_1; + if (max0_2 > m0) m0 = max0_2; + if (max0_3 > m0) m0 = max0_3; + if (max0_4 > m0) m0 = max0_4; + if (max0_5 > m0) m0 = max0_5; + if (max0_6 > m0) m0 = max0_6; + if (max0_7 > m0) m0 = max0_7; + + + if (m_b0 > new0) new0 = m_b0; + old0 = new0; + + if (m_b1 > new1) new1 = m_b1; + old1 = new1; + + if (m_b2 > new2) new2 = m_b2; + old2 = new2; + + if (m_b3 > new3) new3 = m_b3; + old3 = new3; + + if (m_b4 > new4) new4 = m_b4; + old4 = new4; + + if (m_b5 > new5) new5 = m_b5; + old5 = new5; + + if (m_b6 > new6) new6 = m_b6; + old6 = new6; + + if (m_b7 > new7) new7 = m_b7; + old7 = new7; + + out = m1 - m0; + + /* + if (dec == 2) { + if (abs(out) < HALT_min) { + HALT_min = abs(out); + } + } + */ + output[k - 1] = out; + } + + alfa[0] = old0; + alfa[1] = old1; + alfa[2] = old2; + alfa[3] = old3; + alfa[4] = old4; + alfa[5] = old5; + alfa[6] = old6; + alfa[7] = old7; +} + +void DEC_RSC(tdec_t *q, llr_t *input, llr_t *output, int *per, int dec) { + int k; + int i; + int last_state = q->long_cb + RATE; + + /** Initialize alfa states */ + q->alfa[0] = 0; + for (k = 1; k < NUMSTATES; k++) { + q->alfa[k] = -INF; + } + + q->beta[last_state * NUMSTATES] = 0; + for (k = 1; k < NUMSTATES; k++) + q->beta[last_state * NUMSTATES + k] = -INF; + + /* copy data temporal buffer (to allow fastest loops)*/ + memcpy(q->data, input, RATE * last_state * sizeof (llr_t)); + + q->parity = &input[dec]; + + if (dec == 1) { + for (i = 0; i < last_state; i++) { + q->data[RATE * i] += q->W[i ]; + } + } else { + for (i = 0; i < last_state; i++) { + q->data[RATE * i] = q->LLR1[per[i ]] - q->W[per[i ]]; + } + } + + compute_beta(q->beta, q->data, &input[dec], q->long_cb, dec); + compute_alfa(q->alfa, q->beta, q->data, &input[dec], output, q->long_cb, dec); +} + +void decide(llr_t *LLR2, char *output, int *desper, int long_cb) { + int i; + + for (i = 0; i < long_cb; i++) + output[i] = (LLR2[desper[i]] > 0) ? 1 : 0; + +} + +void update_W(llr_t *W, llr_t *LLR1, llr_t *LLR2, int *desper, int long_cb) { + int i; + + for (i = 0; i < long_cb; i++) { + W[i] += LLR2[desper[i]] - LLR1[i]; + } +} + +int turbo_decoder(tdec_t *q, llr_t *input, char *output, int *halt) { + + int i; + long halt_mean=0; + int stop=0; + q->iteration = 0; + + + if (ComputePermutation(&q->permuta, q->long_cb,PER_UMTS)<0) + return -1; + + memset(q->W, 0, sizeof (llr_t) * q->long_cb); + + do { + if (q->iteration) + update_W(q->W, q->LLR1, q->LLR2, q->permuta.DESPER, q->long_cb); + + + DEC_RSC(q, input, q->LLR1, q->permuta.PER, 1); + + DEC_RSC(q, input, q->LLR2, q->permuta.PER, 2); + + q->iteration++; + + } while (q->iteration < q->max_iterations && stop==0); + decide(q->LLR2, output, q->permuta.DESPER, q->long_cb); + + return q->iteration; +} + diff --git a/lte/lib/fec/test/turbocoder_test.c b/lte/lib/fec/test/turbocoder_test.c new file mode 100644 index 000000000..ad28ee470 --- /dev/null +++ b/lte/lib/fec/test/turbocoder_test.c @@ -0,0 +1,324 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "lte.h" + +#include "turbocoder_test.h" + +typedef _Complex float cf_t; + +int frame_length = 1000, nof_frames=100; +float ebno_db = 100.0; +unsigned int seed = 0; +int K = -1; + +#define MAX_ITERATIONS 4 +int nof_iterations = MAX_ITERATIONS; +int test_known_data = 0; +int test_errors = 0; + +#define SNR_POINTS 8 +#define SNR_MIN 0.0 +#define SNR_MAX 4.0 + +void usage(char *prog) { + printf("Usage: %s [nlesv]\n", prog); + printf("\t-k Test with known data (ignores frame_length) [Default disabled]\n"); + printf("\t-i nof_iterations [Default %d]\n", nof_iterations); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-l frame_length [Default %d]\n", frame_length); + printf("\t-e ebno in dB [Default scan]\n"); + printf("\t-t test: check errors on exit [Default disabled]\n"); + printf("\t-s seed [Default 0=time]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "inlstvekt")) != -1) { + switch (opt) { + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'k': + test_known_data = 1; + break; + case 't': + test_errors = 1; + break; + case 'i': + nof_iterations = atoi(argv[optind]); + break; + case 'l': + frame_length = atoi(argv[optind]); + break; + case 'e': + ebno_db = atof(argv[optind]); + break; + case 's': + seed = (unsigned int) strtoul(argv[optind], NULL, 0); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +void output_matlab(float ber[MAX_ITERATIONS][SNR_POINTS], int snr_points) { + int i, j; + FILE *f = fopen("turbocoder_snr.m", "w"); + if (!f) { + perror("fopen"); + exit(-1); + } + fprintf(f, "ber=["); + for (j=0;j known_data_errors[j]) { + fprintf(stderr, "Expected %d errors but got %d\n", + known_data_errors[j], errors[j]); + exit(-1); + }else { + printf("Iter %d ok\n", j+1); + } + } + } else { + for (j=0;j get_expected_errors(frame_cnt, seed, j+1, frame_length, ebno_db)) { + fprintf(stderr, "Expected %d errors but got %d\n", + get_expected_errors(frame_cnt, seed, j+1, frame_length, ebno_db), + errors[j]); + exit(-1); + } else { + printf("Iter %d ok\n", j+1); + } + } + } + } + } + } + + free(data_tx); + free(symbols); + free(llr); + free(llr_c); + free(data_rx); + + tdec_free(&tdec); + tcod_free(&tcod); + + printf("\n"); + output_matlab(ber, snr_points); + printf("Done\n"); + exit(0); +} diff --git a/lte/lib/fec/test/turbocoder_test.h b/lte/lib/fec/test/turbocoder_test.h new file mode 100644 index 000000000..768f5406b --- /dev/null +++ b/lte/lib/fec/test/turbocoder_test.h @@ -0,0 +1,168 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +typedef struct { + int n; + unsigned int s; + int iterations; + int len; + float ebno; + int errors; +} expected_errors_t; + +static expected_errors_t expected_errors[] = { + { 100, 1, 1, 504, 1.0, 3989 }, + { 100, 1, 2, 504, 1.0, 1922 }, + { 100, 1, 3, 504, 1.0, 1096 }, + { 100, 1, 4, 504, 1.0, 957 }, + + { 100, 1, 1, 504, 2.0, 803 }, + { 100, 1, 2, 504, 2.0, 47 }, + { 100, 1, 3, 504, 2.0, 7 }, + { 100, 1, 4, 504, 2.0, 0 }, + + { 100, 1, 1, 6144, 1.5, 24719 }, + { 100, 1, 2, 6144, 1.5, 897 }, + { 100, 1, 3, 6144, 1.5, 2 }, + { 100, 1, 4, 6144, 1.5, 0 }, + { -1, 0, -1, -1, -1.0, -1} +}; + + +int get_expected_errors(int n, unsigned int s, int iterations, int len, float ebno) { + int i; + i = 0; + while (expected_errors[i].n != -1) { + if (expected_errors[i].n == n + && expected_errors[i].s == s + && expected_errors[i].len == len + && expected_errors[i].iterations == iterations + && expected_errors[i].ebno == ebno) { + break; + } else { + i++; + } + } + return expected_errors[i].errors; +} + +#define KNOWN_DATA_NFRAMES 1 +#define KNOWN_DATA_SEED 1 +#define KNOWN_DATA_EBNO 0.5 +const int known_data_errors[4] = {47, 18, 0, 0}; + +#define KNOWN_DATA_LEN 504 +const char known_data[KNOWN_DATA_LEN] = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, + 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, + 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, + 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1 }; + +const char known_data_encoded[3 * KNOWN_DATA_LEN + 12] = { 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, + 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, + 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, + 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, + 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, + 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, + 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, + 1, 0, 1, 1, 1 };