/* Tetra convolutional encoder, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ /* This converts from type type-2 bits into type-3 bits */ /* (C) 2011 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include static char *dump_state(struct conv_enc_state *ces) { static char pbuf[1024]; snprintf(pbuf, sizeof(pbuf), "%u-%u-%u-%u", ces->delayed[0], ces->delayed[1], ces->delayed[2], ces->delayed[3]); return pbuf; } /* Mother code according to Section 8.2.3.1.1 */ static uint8_t conv_enc_in_bit(struct conv_enc_state *ces, uint8_t bit, uint8_t *out) { uint8_t g1, g2, g3, g4; uint8_t *delayed = ces->delayed; DEBUGP("State in: %s, Input bit: %u: ", dump_state(ces), bit); /* G1 = 1 + D + D4 */ g1 = (bit + delayed[0] + delayed[3]) % 2; /* G2 = 1 + D2 + D3 + D4 */ g2 = (bit + delayed[1] + delayed[2] + delayed[3]) % 2; /* G3 = 1 + D + D2 + D4 */ g3 = (bit + delayed[0] + delayed[1] + delayed[3]) % 2; /* G4 = 1 + D + D3 + D4 */ g4 = (bit + delayed[0] + delayed[2] + delayed[3]) % 2; /* shift the state and input our new bit */ ces->delayed[3] = ces->delayed[2]; ces->delayed[2] = ces->delayed[1]; ces->delayed[1] = ces->delayed[0]; ces->delayed[0] = bit; DEBUGP("Output bits: %u-%u-%u-%u, State out: %s\n", g1, g2, g3, g4, dump_state(ces)); *out++ = g1; *out++ = g2; *out++ = g3; *out++ = g4; return (g1 | (g2 << 1) | (g3 << 2) | (g4 << 3)); } /* in: bit-per-byte (len), out: bit-per-byte (4*len) */ int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out) { int i; for (i = 0; i < len; i++) { conv_enc_in_bit(ces, in[i], out); out += 4; } return 0; } int conv_enc_init(struct conv_enc_state *ces) { memset(ces->delayed, 0, sizeof(ces->delayed)); return 0; } /* Puncturing */ const uint8_t P_rate2_3[] = { 0, 1, 2, 5 }; const uint8_t P_rate1_3[] = { 0, 1, 2, 3, 5, 6, 7 }; /* Voice */ const uint8_t P_rate8_12[] = { 0, 1, 2, 4 }; const uint8_t P_rate8_18[] = { 0, 1, 2, 3, 4, 5, 7, 8, 10, 11 }; const uint8_t P_rate8_17[] = { 0, 1, 2, 3, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23 }; struct puncturer { enum tetra_rcpc_puncturer type; const uint8_t *P; uint8_t t; uint8_t period; uint32_t (*i_func)(uint32_t j); }; static uint32_t i_func_equals(uint32_t j) { return j; } static uint32_t i_func_292(uint32_t j) { return (j + ((j-1)/65)); } static uint32_t i_func_148(uint32_t j) { return (j + ((j-1)/35)); } /* Section 8.2.3.1.3 */ static const struct puncturer punct_2_3 = { .type = TETRA_RCPC_PUNCT_2_3, .P = P_rate2_3, .t = 3, .period = 8, .i_func = &i_func_equals, }; /* Section 8.2.3.1.4 */ static const struct puncturer punct_1_3 = { .type = TETRA_RCPC_PUNCT_1_3, .P = P_rate1_3, .t = 6, .period = 8, .i_func = &i_func_equals, }; /* Section 8.2.3.1.5 */ static const struct puncturer punct_292_432 = { .type = TETRA_RCPC_PUNCT_292_432, .P = P_rate2_3, .t = 3, .period = 8, .i_func = &i_func_292, }; /* Section 8.2.3.1.6 */ static const struct puncturer punct_148_432 = { .type = TETRA_RCPC_PUNCT_148_432, .P = P_rate1_3, .t = 6, .period = 8, .i_func = &i_func_148, }; /* EN 300 395-2 Section 5.5.2.1 */ static const struct puncturer punct_112_168 = { .type = TETRA_RCPC_PUNCT_112_168, .P = P_rate8_12, .t = 3, .period = 6, .i_func = &i_func_equals, }; /* EN 300 395-2 Section 5.5.2.2 */ static const struct puncturer punct_72_162 = { .type = TETRA_RCPC_PUNCT_72_162, .P = P_rate8_18, .t = 9, .period = 12, .i_func = &i_func_equals, }; /* EN 300 395-2 Section 5.6.2.1 */ static const struct puncturer punct_38_80 = { .type = TETRA_RCPC_PUNCT_72_162, .P = P_rate8_17, .t = 17, .period = 24, .i_func = &i_func_equals, }; static const struct puncturer *tetra_puncts[] = { [TETRA_RCPC_PUNCT_2_3] = &punct_2_3, [TETRA_RCPC_PUNCT_1_3] = &punct_1_3, [TETRA_RCPC_PUNCT_292_432] = &punct_292_432, [TETRA_RCPC_PUNCT_148_432] = &punct_148_432, [TETRA_RCPC_PUNCT_112_168] = &punct_112_168, [TETRA_RCPC_PUNCT_72_162] = &punct_72_162, [TETRA_RCPC_PUNCT_38_80] = &punct_38_80, }; /* Puncture the mother code (in) and write 'len' symbols to out */ int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out) { const struct puncturer *punct; uint32_t i, j, k; uint8_t t; const uint8_t *P; if (pu >= ARRAY_SIZE(tetra_puncts)) return -EINVAL; punct = tetra_puncts[pu]; t = punct->t; P = punct->P; /* Section 8.2.3.1.2 */ for (j = 1; j <= len; j++) { i = punct->i_func(j); k = punct->period * ((i-1)/t) + P[i - t*((i-1)/t)]; DEBUGP("j = %u, i = %u, k = %u\n", j, i, k); out[j-1] = in[k-1]; } return 0; } /* De-Puncture the 'len' type-3 bits (in) and write mother code to out */ int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out) { const struct puncturer *punct; uint32_t i, j, k; uint8_t t; const uint8_t *P; if (pu >= ARRAY_SIZE(tetra_puncts)) return -EINVAL; punct = tetra_puncts[pu]; t = punct->t; P = punct->P; /* Section 8.2.3.1.2 */ for (j = 1; j <= len; j++) { i = punct->i_func(j); k = punct->period * ((i-1)/t) + P[i - t*((i-1)/t)]; DEBUGP("j = %u, i = %u, k = %u\n", j, i, k); out[k-1] = in[j-1]; } return 0; } struct punct_test_param { uint16_t type2_len; uint16_t type3_len; uint16_t mother_rate; /* rate 1/3 for speech, 1/4 for data */ enum tetra_rcpc_puncturer punct; }; static const struct punct_test_param punct_test_params[] = { { 80, 120, 4, TETRA_RCPC_PUNCT_2_3 }, /* BSCH */ { 292, 432, 4, TETRA_RCPC_PUNCT_292_432 }, /* TCH/4.8 */ { 148, 432, 4, TETRA_RCPC_PUNCT_148_432 }, /* TCH/2.4 */ { 144, 216, 4, TETRA_RCPC_PUNCT_2_3 }, /* SCH/HD, BNCH, STCH */ { 112, 168, 4, TETRA_RCPC_PUNCT_2_3 }, /* SCH/HU */ { 288, 432, 4, TETRA_RCPC_PUNCT_2_3 }, /* SCH/F */ { 112, 168, 3, TETRA_RCPC_PUNCT_112_168 }, /* Speech class 1 */ { 72, 162, 3, TETRA_RCPC_PUNCT_72_162 }, /* Speech class 2 */ { 38, 80, 3, TETRA_RCPC_PUNCT_38_80 }, /* Speech class 2 in STCH */ }; static int mother_memcmp(const uint8_t *mother, const uint8_t *depunct, int len) { unsigned int i, equal = 0; for (i = 0; i < len; i++) { /* ignore any 0xff-initialized part */ if (depunct[i] == 0xff) continue; if (depunct[i] != mother[i]) return -1; equal++; } return equal; } static int test_one_punct(const struct punct_test_param *ptp) { uint8_t *mother_buf; uint8_t *depunct_buf; uint8_t *type3_buf; int i, j, mother_len; printf("==> Testing Puncture/Depuncture mode %u (%u/%u)\n", ptp->punct, ptp->type2_len, ptp->type3_len); mother_len = ptp->type2_len * ptp->mother_rate; mother_buf = malloc(mother_len); depunct_buf = malloc(ptp->type2_len * ptp->mother_rate); type3_buf = malloc(ptp->type3_len); /* initialize mother buffer with sequence of bytes starting at 0 */ for (i = 0, j = 0; i < mother_len; i++, j++) { if (j == 0xff) j = 0; mother_buf[i] = j; } /* puncture the mother_buf to type3_buf using rate 2/3 on 60 bits */ get_punctured_rate(ptp->punct, mother_buf, ptp->type3_len, type3_buf); /* initialize the de-punctured buffer */ memset(depunct_buf, 0xff, mother_len); /* de-puncture into the depunct_buf (i.e. what happens at the receiver) */ tetra_rcpc_depunct(ptp->punct, type3_buf, ptp->type3_len, depunct_buf); DEBUGP("MOTH: %s\n", osmo_hexdump(mother_buf, mother_len)); DEBUGP("PUNC: %s\n", osmo_hexdump(type3_buf, ptp->type3_len)); DEBUGP("DEPU: %s\n", osmo_hexdump(depunct_buf, mother_len)); i = mother_memcmp(mother_buf, depunct_buf, mother_len); if (i < 0) { fprintf(stderr, "Mother buf != Depunct buf\n"); return i; } else if (i != ptp->type3_len) { fprintf(stderr, "Depunct buf only has %u equal symbols, we need %u\n", i, ptp->type3_len); return -EINVAL; } free(type3_buf); free(depunct_buf); free(mother_buf); return 0; } int tetra_punct_test(void) { int i, rc; for (i = 0; i < ARRAY_SIZE(punct_test_params); i++) { rc = test_one_punct(&punct_test_params[i]); if (rc < 0) return rc; } return 0; }