diff options
Diffstat (limited to 'src/osmo-bts-trx/pxxch.c')
-rw-r--r-- | src/osmo-bts-trx/pxxch.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/src/osmo-bts-trx/pxxch.c b/src/osmo-bts-trx/pxxch.c new file mode 100644 index 00000000..17a7c947 --- /dev/null +++ b/src/osmo-bts-trx/pxxch.c @@ -0,0 +1,473 @@ +/* + * pxxch.c + * + * Copyright (c) 2013 Andreas Eversberg <jolly@eversberg.eu> + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/conv.h> +#include <osmocom/core/crcgen.h> + +#include "pxxch.h" + + +/* + * GSM PDTCH parity (FIRE code) + * + * g(x) = (x^23 + 1)(x^17 + x^3 + 1) + * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 + */ + +const struct osmo_crc64gen_code pxxch_crc40 = { + .bits = 40, + .poly = 0x0004820009ULL, + .init = 0x0000000000ULL, + .remainder = 0xffffffffffULL, +}; + + +/* + * GSM PDTCH CS-2, CS-3 parity + * + * g(x) = x^16 + x^12 + x^5 + 1 + */ + +const struct osmo_crc16gen_code pdtch_crc16 = { + .bits = 16, + .poly = 0x1021, + .init = 0x0000, + .remainder = 0xffff, +}; + + +/* + * GSM PDTCH convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_cs1_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_cs1_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_cs1 = { + .N = 2, + .K = 5, + .len = 224, + .next_output = conv_cs1_next_output, + .next_state = conv_cs1_next_state, +}; + +static const uint8_t conv_cs2_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_cs2_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_cs2 = { + .N = 2, + .K = 5, + .len = 290, + .next_output = conv_cs2_next_output, + .next_state = conv_cs2_next_state, +}; + +static const uint8_t conv_cs3_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_cs3_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_cs3 = { + .N = 2, + .K = 5, + .len = 334, + .next_output = conv_cs3_next_output, + .next_state = conv_cs3_next_state, +}; + + +/* + * GSM PxxCH interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 4 blocks of 114 bits: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 4) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +static void +pxxch_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + cB[k] = iB[B * 114 + j]; + } +} + +static void +pxxch_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + iB[B * 114 + j] = cB[k]; + } +} + +static void +pxxch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn) +{ + memcpy(iB, eB, 57); + memcpy(iB+57, eB+59, 57); + + if (hl) + *hl = eB[57]; + + if (hn) + *hn = eB[58]; +} + +static void +pxxch_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *hl, ubit_t *hn) +{ + memcpy(eB, iB, 57); + memcpy(eB+59, iB+57, 57); + + if (hl) + eB[57] = *hl; + if (hn) + eB[58] = *hn; +} + +static ubit_t pdtch_hl_hn[4][8] = { + { 1,1, 1,1, 1,1, 1,1 }, + { 1,1, 0,0, 1,0, 0,0 }, + { 0,0, 1,0, 0,0, 0,1 }, + { 0,0, 0,1, 0,1, 1,0 }, +}; + +static ubit_t usf2six[8][6] = { + { 0,0,0, 0,0,0 }, + { 0,0,1, 0,1,1 }, + { 0,1,0, 1,1,0 }, + { 0,1,1, 1,0,1 }, + { 1,0,0, 1,0,1 }, + { 1,0,1, 1,1,0 }, + { 1,1,0, 0,1,1 }, + { 1,1,1, 0,0,0 }, +}; + +static ubit_t usf2twelve[8][12] = { + { 0,0,0, 0,0,0, 0,0,0, 0,0,0 }, + { 0,0,0, 0,1,1, 0,1,1, 1,0,1 }, + { 0,0,1, 1,0,1, 1,1,0, 1,1,0 }, + { 0,0,1, 1,1,0, 1,0,1, 0,1,1 }, + { 1,1,0, 1,0,0, 0,0,1, 0,1,1 }, + { 1,1,0, 1,1,1, 0,1,0, 1,1,0 }, + { 1,1,1, 0,0,1, 1,1,1, 1,0,1 }, + { 1,1,1, 0,1,0, 1,0,0, 0,0,0 }, +}; + +static uint8_t puncture_cs2[588] = { + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1 +}; + +static uint8_t puncture_cs3[676] = { + 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0 +}; + +int +pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) +{ + sbit_t iB[456], cB[676], hl_hn[8]; + ubit_t conv[456]; + int i, j, k, rv, best, cs, usf; + + for (i=0; i<4; i++) + pxxch_burst_unmap(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, + hl_hn + i*2 + 1); + + for (i=0, best=0, cs=1; i<4; j++) { + for (j=0, k=0; j<4; j++) { + if (pdtch_hl_hn[i][j] == hl_hn[j]) + k++; + } + if (k > best) + cs = i+1; + } + + pxxch_deinterleave(cB, iB); + + switch (cs) { + case 1: + osmo_conv_decode(&conv_cs1, cB, conv); + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv, 184, + conv+184); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); + + return 23; + case 2: + for (i=587, j=455; i>=0; i++) + if (!puncture_cs2[i]) + cB[i] = cB[j--]; + else + cB[i] = 0; + + osmo_conv_decode(&conv_cs2, cB, conv); + + for (i=0, best=0, usf=0; i<8; j++) { + for (j=0, k=0; j<6; j++) { + if (usf2six[i][j] == conv[j]) + k++; + } + if (k > best) + usf = i; + } + + conv[3] = (usf >> 2) & 1; + conv[4] = (usf >> 1) & 1; + conv[5] = usf & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+3, 271, + conv+3+271); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 271, 1); + + return 34; + case 3: + for (i=675, j=455; i>=0; i++) + if (!puncture_cs3[i]) + cB[i] = cB[j--]; + else + cB[i] = 0; + + osmo_conv_decode(&conv_cs3, cB, conv); + + for (i=0, best=0, usf=0; i<8; j++) { + for (j=0, k=0; j<6; j++) { + if (usf2six[i][j] == conv[j]) + k++; + } + if (k > best) + usf = i; + } + + conv[3] = (usf >> 2) & 1; + conv[4] = (usf >> 1) & 1; + conv[5] = usf & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+3, 315, + conv+3+315); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 315, 1); + + return 40; + case 4: + for (i=0; i<456;i++) + conv[i] = (cB[i] < 0) ? 1:0; + + for (i=0, best=0, usf=0; i<8; j++) { + for (j=0, k=0; j<12; j++) { + if (usf2twelve[i][j] == conv[j]) + k++; + } + if (k > best) + usf = i; + } + + conv[9] = (usf >> 2) & 1; + conv[10] = (usf >> 1) & 1; + conv[11] = usf & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+9, 431, + conv+9+431); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 431, 1); + + return 54; + } + + return -1; +} + +int +pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) +{ + ubit_t iB[456], cB[676], *hl_hn; + ubit_t conv[334]; + int i, j, usf; + + switch (l2_len) { + case 23: + osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); + + osmo_crc64gen_set_bits(&pxxch_crc40, conv, 184, conv+184); + + osmo_conv_encode(&conv_cs1, conv, cB); + + hl_hn = pdtch_hl_hn[0]; + + break; + case 34: + osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 271, 1); + usf = (conv[3] << 2) | (conv[4] << 1) | conv[5]; + + osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 271, conv+3+271); + + memcpy(conv, usf2six[usf], 6); + + osmo_conv_encode(&conv_cs2, conv, cB); + + for (i=0, j=0; i<588; i++) + if (!puncture_cs2[i]) + cB[j++] = cB[i]; + + hl_hn = pdtch_hl_hn[1]; + + break; + case 40: + osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 315, 1); + usf = (conv[3] << 2) | (conv[4] << 1) | conv[5]; + + osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 315, conv+3+315); + + memcpy(conv, usf2six[usf], 6); + + osmo_conv_encode(&conv_cs3, conv, cB); + + for (i=0, j=0; i<676; i++) + if (!puncture_cs3[i]) + cB[j++] = cB[i]; + + hl_hn = pdtch_hl_hn[2]; + + break; + case 54: + osmo_pbit2ubit_ext(cB, 9, l2_data, 0, 431, 1); + usf = (cB[9] << 2) | (cB[10] << 1) | conv[11]; + + osmo_crc16gen_set_bits(&pdtch_crc16, cB+9, 431, cB+9+431); + + memcpy(cB, usf2twelve[usf], 12); + + hl_hn = pdtch_hl_hn[3]; + + break; + default: + return -1; + } + + pxxch_interleave(cB, iB); + + for (i=0; i<4; i++) + pxxch_burst_map(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, + hl_hn + i*2 + 1); + + return 0; +} |