diff options
Diffstat (limited to 'src/osmo-bts-trx/gsm0503_coding.c')
-rw-r--r-- | src/osmo-bts-trx/gsm0503_coding.c | 479 |
1 files changed, 457 insertions, 22 deletions
diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 3f4ae159..0fb07283 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -338,6 +338,37 @@ static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits, int net_order) } } +static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) +{ + int i, j, k, l, o; + + if (net_order) { + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + while (i < 260) { + b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } + return; + } + + i = 0; /* counts bits */ + j = 4; /* counts input bits */ + k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset output bits */ + while (i < 260) { + b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; + if (--k < 0) { + o += gsm0503_gsm_fr_map[l]; + k = gsm0503_gsm_fr_map[++l]-1; + } + i++; + j++; + } +} + static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) { int i, j; @@ -367,36 +398,32 @@ static void tch_efr_disassemble(ubit_t *b_bits, uint8_t *tch_data) } } -static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) +static void tch_amr_reassemble(uint8_t *tch_data, ubit_t *d_bits, int len) { - int i, j, k, l, o; + int i, j; - if (net_order) { - i = 0; /* counts bits */ - j = 4; /* counts output bits */ - while (i < 260) { - b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; - i++; - j++; - } - return; - } + memset(tch_data, 0, (len + 7) >> 3); i = 0; /* counts bits */ - j = 4; /* counts input bits */ - k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset output bits */ - while (i < 260) { - b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; - if (--k < 0) { - o += gsm0503_gsm_fr_map[l]; - k = gsm0503_gsm_fr_map[++l]-1; - } + j = 0; /* counts output bits */ + while (i < len) { + tch_data[j>>3] |= (d_bits[i] << (7-(j&7))); i++; j++; } +} +static void tch_amr_disassemble(ubit_t *d_bits, uint8_t *tch_data, int len) +{ + int i, j; + + i = 0; /* counts bits */ + j = 0; /* counts output bits */ + while (i < len) { + d_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } } static void tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) @@ -496,6 +523,21 @@ static void tch_efr_unreorder(ubit_t *s, ubit_t *p, ubit_t *w) memcpy(s+224, w+232, 20); memcpy(p, w+252, 8); } + +static void tch_amr_merge(ubit_t *u, ubit_t *d, ubit_t *p, int len, int prot) +{ + memcpy(u, d, prot); + memcpy(u+prot, p, 6); + memcpy(u+prot+6, d+prot, len-prot); +} + +static void tch_amr_unmerge(ubit_t *d, ubit_t *p, ubit_t *u, int len, int prot) +{ + memcpy(d, u, prot); + memcpy(p, u+prot, 6); + memcpy(d+prot, u+prot+6, len-prot); +} + int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr) { sbit_t iB[912], cB[456], h; @@ -611,6 +653,399 @@ coding_efr_fr: return 0; } +static float amr_calc_ber(sbit_t *orig, ubit_t *test, int len) +{ + int i, err = 0; + + /* count number of wrong bits (sbits with 0-value are omitted) */ + for (i=0; i<len; i++) { + if ((*orig) > 0 && (*test)) + err++; + else if ((*orig) < 0 && !(*test)) + err++; + orig++; + test++; + } + + return (float)err / (float)len; +} + +int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, + uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, float *ber) +{ + sbit_t iB[912], cB[456], h; + ubit_t test[456], d[244], p[6], conv[250]; + int i, j, k, best = 0, rv, len, steal = 0, id = 0; + + for (i=0; i<8; i++) { + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, + i>>2); + steal -= h; + } + + gsm0503_tch_fr_deinterleave(cB, iB); + + if (steal > 0) { + rv = _xcch_decode_cB(tch_data, cB); + if (rv) + return -1; + + return 23; + } + + for (i=0; i<4; i++) { + for (j=0, k=0; j<8; j++) + k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - + ((int)cB[j])); + if (i == 0 || k < best) { + best = k; + id = i; + } + } + + /* check if indicated codec fits into range of codecs */ + if (id >= codecs) { + /* codec mode out of range, return id */ + return id; + } + + switch ((codec_mode_req) ? codec[*ft] : codec[id]) { + case 7: /* TCH/AFS12.2 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_12_2, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 244, 81); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 81, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 244); + + len = 31; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_12_2, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 6: /* TCH/AFS10.2 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_10_2, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 204, 65); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 65, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 204); + + len = 26; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_10_2, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 5: /* TCH/AFS7.95 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_7_95, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 159, 75); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 75, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 159); + + len = 20; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_7_95, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 4: /* TCH/AFS7.4 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_7_4, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 148, 61); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 148); + + len = 19; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_7_4, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 3: /* TCH/AFS6.7 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_6_7, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 134, 55); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 134); + + len = 17; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_6_7, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 2: /* TCH/AFS5.9 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_5_9, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 118, 55); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 118); + + len = 15; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_5_9, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 1: /* TCH/AFS5.15 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_5_15, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 103, 49); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 103); + + len = 13; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_5_15, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 0: /* TCH/AFS4.75 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_4_75, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 95, 39); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 95); + + len = 12; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_4_75, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + default: + fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); + + return -1; + } + + /* change codec request / indication, if frame is valid */ + if (codec_mode_req) + *cmr = id; + else + *ft = id; + + return len; +} + +int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, + uint8_t cmr) +{ + ubit_t iB[912], cB[456], h; + ubit_t d[244], p[6], conv[250]; + int i; + uint8_t id; + + if (len == 23) { /* FACCH */ + _xcch_encode_cB(cB, tch_data); + + h = 1; + + goto facch; + } + + h = 0; + + if (codec_mode_req) { + if (cmr >= codecs) { + fprintf(stderr, "FIXME: CMR ID %d not in codec list!\n", + cmr); + return -1; + } + id = cmr; + } else { + if (ft >= codecs) { + fprintf(stderr, "FIXME: FT ID %d not in codec list!\n", + ft); + return -1; + } + id = ft; + } + + switch (codec[ft]) { + case 7: /* TCH/AFS12.2 */ + if (len != 31) { +invalid_length: + fprintf(stderr, "FIXME: payload length %d does not " + "comply with codec type %d!\n", len, ft); + return -1; + } + + tch_amr_disassemble(d, tch_data, 244); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p); + + tch_amr_merge(conv, d, p, 244, 81); + + osmo_conv_encode(&gsm0503_conv_tch_afs_12_2, conv, cB+8); + + break; + case 6: /* TCH/AFS10.2 */ + if (len != 26) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 204); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p); + + tch_amr_merge(conv, d, p, 204, 65); + + osmo_conv_encode(&gsm0503_conv_tch_afs_10_2, conv, cB+8); + + break; + case 5: /* TCH/AFS7.95 */ + if (len != 20) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 159); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p); + + tch_amr_merge(conv, d, p, 159, 75); + + osmo_conv_encode(&gsm0503_conv_tch_afs_7_95, conv, cB+8); + + break; + case 4: /* TCH/AFS7.4 */ + if (len != 19) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 148); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p); + + tch_amr_merge(conv, d, p, 148, 61); + + osmo_conv_encode(&gsm0503_conv_tch_afs_7_4, conv, cB+8); + + break; + case 3: /* TCH/AFS6.7 */ + if (len != 17) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 134); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p); + + tch_amr_merge(conv, d, p, 134, 55); + + osmo_conv_encode(&gsm0503_conv_tch_afs_6_7, conv, cB+8); + + break; + case 2: /* TCH/AFS5.9 */ + if (len != 15) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 118); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p); + + tch_amr_merge(conv, d, p, 118, 55); + + osmo_conv_encode(&gsm0503_conv_tch_afs_5_9, conv, cB+8); + + break; + case 1: /* TCH/AFS5.15 */ + if (len != 13) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 103); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p); + + tch_amr_merge(conv, d, p, 103, 49); + + osmo_conv_encode(&gsm0503_conv_tch_afs_5_15, conv, cB+8); + + break; + case 0: /* TCH/AFS4.75 */ + if (len != 12) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 95); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p); + + tch_amr_merge(conv, d, p, 95, 39); + + osmo_conv_encode(&gsm0503_conv_tch_afs_4_75, conv, cB+8); + + break; + default: + fprintf(stderr, "FIXME: FT %d not supported!\n", ft); + + return -1; + } + + memcpy(cB, gsm0503_afs_ic_ubit[id], 8); + +facch: + gsm0503_tch_fr_interleave(cB, iB); + + for (i=0; i<8; i++) + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>2); + + return 0; +} + /* * GSM RACH transcoding */ |