From 934056e7f8ea62ff1eba0a3110a3840a7af58857 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 26 Mar 2013 09:05:14 +0100 Subject: Support for AMR full speech --- src/osmo-bts-trx/Makefile.am | 4 +- src/osmo-bts-trx/gsm0503_coding.c | 479 +++++++++++++++++++++++++++++-- src/osmo-bts-trx/gsm0503_coding.h | 5 + src/osmo-bts-trx/gsm0503_conv.c | 484 ++++++++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_conv.h | 8 + src/osmo-bts-trx/gsm0503_interleaving.c | 2 +- src/osmo-bts-trx/gsm0503_parity.c | 13 + src/osmo-bts-trx/gsm0503_parity.h | 1 + src/osmo-bts-trx/gsm0503_tables.c | 15 + src/osmo-bts-trx/gsm0503_tables.h | 2 + src/osmo-bts-trx/l1_if.c | 17 +- src/osmo-bts-trx/l1_if.h | 11 + src/osmo-bts-trx/loops.c | 97 +++++++ src/osmo-bts-trx/loops.h | 5 + src/osmo-bts-trx/scheduler.c | 139 ++++++++- src/osmo-bts-trx/scheduler.h | 5 +- 16 files changed, 1250 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index d9ddca1f..05056cef 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -2,10 +2,10 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp -EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h +EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h amr.h bin_PROGRAMS = osmobts-trx -osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c +osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c amr.c osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) 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 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 */ diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 96b162f3..97da7baf 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -7,6 +7,11 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr); int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order); +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); +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); int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); int sch_decode(uint8_t *sb_info, sbit_t *burst); diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index f9fcaa82..4b95f72c 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -81,3 +81,487 @@ const struct osmo_conv_code gsm0503_conv_tch_fr = { }; +/* TCH/AFS12.2 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_12_2_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_afs_12_2_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 }, + { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_12_2_next_term_output[] = { + 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1, +}; + +static const uint8_t conv_tch_afs_12_2_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_12_2_puncture[] = { + 321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363, + 365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401, + 405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441, + 443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477, + 481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_12_2 = { + .N = 2, + .K = 5, + .len = 250, + .next_output = conv_tch_afs_12_2_next_output, + .next_state = conv_tch_afs_12_2_next_state, + .next_term_output = conv_tch_afs_12_2_next_term_output, + .next_term_state = conv_tch_afs_12_2_next_term_state, + .puncture = conv_tch_afs_12_2_puncture, +}; + + +/* TCH/AFS10.2 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_10_2_next_output[][2] = { + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, +}; + +static const uint8_t conv_tch_afs_10_2_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_10_2_next_term_output[] = { + 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4, +}; + +static const uint8_t conv_tch_afs_10_2_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_10_2_puncture[] = { + 1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43, + 46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91, + 94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139, + 142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187, + 190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235, + 238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283, + 286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328, + 331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364, + 367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400, + 403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436, + 439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472, + 475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508, + 511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544, + 547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580, + 583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613, + 616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637, + 639, 640, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_10_2 = { + .N = 3, + .K = 5, + .len = 210, + .next_output = conv_tch_afs_10_2_next_output, + .next_state = conv_tch_afs_10_2_next_state, + .next_term_output = conv_tch_afs_10_2_next_term_output, + .next_term_state = conv_tch_afs_10_2_next_term_state, + .puncture = conv_tch_afs_10_2_puncture, +}; + + +/* TCH/AFS7.95 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_7_95_next_output[][2] = { + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, +}; + +static const uint8_t conv_tch_afs_7_95_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 }, + { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 }, + { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 }, + { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 }, + { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 }, + { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 }, + { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 }, + { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 }, + { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 }, + { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 }, +}; + +static const uint8_t conv_tch_afs_7_95_next_term_output[] = { + 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0, + 4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4, + 7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7, + 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3, +}; + +static const uint8_t conv_tch_afs_7_95_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_afs_7_95_puncture[] = { + 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310, + 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367, + 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415, + 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463, + 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505, + 506, 508, 509, 511, 512, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_7_95 = { + .N = 3, + .K = 7, + .len = 165, + .next_output = conv_tch_afs_7_95_next_output, + .next_state = conv_tch_afs_7_95_next_state, + .next_term_output = conv_tch_afs_7_95_next_term_output, + .next_term_state = conv_tch_afs_7_95_next_term_state, + .puncture = conv_tch_afs_7_95_puncture, +}; + + +/* TCH/AFS7.4 */ +/* ---------- */ + +static const uint8_t conv_tch_afs_7_4_next_output[][2] = { + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, +}; + +static const uint8_t conv_tch_afs_7_4_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_7_4_next_term_output[] = { + 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4, +}; + +static const uint8_t conv_tch_afs_7_4_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_7_4_puncture[] = { + 0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415, + 421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469, + 471, 472, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_7_4 = { + .N = 3, + .K = 5, + .len = 154, + .next_output = conv_tch_afs_7_4_next_output, + .next_state = conv_tch_afs_7_4_next_state, + .next_term_output = conv_tch_afs_7_4_next_term_output, + .next_term_state = conv_tch_afs_7_4_next_term_state, + .puncture = conv_tch_afs_7_4_puncture, +}; + + +/* TCH/AFS6.7 */ +/* ---------- */ + +static const uint8_t conv_tch_afs_6_7_next_output[][2] = { + { 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 }, + { 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 }, + { 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 }, + { 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 }, +}; + +static const uint8_t conv_tch_afs_6_7_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static int conv_tch_afs_6_7_puncture[] = { + 1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107, + 119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267, + 279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327, + 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371, + 375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403, + 407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435, + 439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467, + 471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499, + 503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531, + 535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559, + 561, 563, 565, 567, 569, 571, 573, 575, + -1, /* end */ +}; + +static const uint8_t conv_tch_afs_6_7_next_term_output[] = { + 0, 11, 7, 12, 11, 0, 12, 7, 15, 4, 8, 3, 4, 15, 3, 8, +}; + +static const uint8_t conv_tch_afs_6_7_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_6_7 = { + .N = 4, + .K = 5, + .len = 140, + .next_output = conv_tch_afs_6_7_next_output, + .next_state = conv_tch_afs_6_7_next_state, + .next_term_output = conv_tch_afs_6_7_next_term_output, + .next_term_state = conv_tch_afs_6_7_next_term_state, + .puncture = conv_tch_afs_6_7_puncture, +}; + + +/* TCH/AFS5.9 */ +/* ---------- */ + +static const uint8_t conv_tch_afs_5_9_next_output[][2] = { + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, +}; + +static const uint8_t conv_tch_afs_5_9_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 }, + { 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 }, + { 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 }, + { 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 }, + { 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 }, + { 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, + { 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 }, + { 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 }, + { 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 }, + { 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 }, + { 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 }, + { 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 }, +}; + +static const uint8_t conv_tch_afs_5_9_next_term_output[] = { + 0, 7, 11, 12, 11, 12, 0, 7, 7, 0, 12, 11, 12, 11, 7, 0, + 8, 15, 3, 4, 3, 4, 8, 15, 15, 8, 4, 3, 4, 3, 15, 8, + 15, 8, 4, 3, 4, 3, 15, 8, 8, 15, 3, 4, 3, 4, 8, 15, + 7, 0, 12, 11, 12, 11, 7, 0, 0, 7, 11, 12, 11, 12, 0, 7, +}; + +static const uint8_t conv_tch_afs_5_9_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_afs_5_9_puncture[] = { + 0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95, + 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287, + 303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375, + 379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439, + 443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491, + 495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_5_9 = { + .N = 4, + .K = 7, + .len = 124, + .next_output = conv_tch_afs_5_9_next_output, + .next_state = conv_tch_afs_5_9_next_state, + .next_term_output = conv_tch_afs_5_9_next_term_output, + .next_term_state = conv_tch_afs_5_9_next_term_state, + .puncture = conv_tch_afs_5_9_puncture, +}; + + +/* TCH/AFS5.15 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_5_15_next_output[][2] = { + { 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 }, + { 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 }, + { 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 }, + { 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 }, +}; + +static const uint8_t conv_tch_afs_5_15_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_5_15_next_term_output[] = { + 0, 27, 7, 28, 27, 0, 28, 7, 31, 4, 24, 3, 4, 31, 3, 24, +}; + +static const uint8_t conv_tch_afs_5_15_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_5_15_puncture[] = { + 0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40, + 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, + 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, + 290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345, + 350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385, + 390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425, + 430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465, + 470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505, + 510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540, + 544, 545, 549, 550, 554, 555, 559, 560, 564, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_5_15 = { + .N = 5, + .K = 5, + .len = 109, + .next_output = conv_tch_afs_5_15_next_output, + .next_state = conv_tch_afs_5_15_next_state, + .next_term_output = conv_tch_afs_5_15_next_term_output, + .next_term_state = conv_tch_afs_5_15_next_term_state, + .puncture = conv_tch_afs_5_15_puncture, +}; + + +/* TCH/AFS4.75 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_4_75_next_output[][2] = { + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, +}; + +static const uint8_t conv_tch_afs_4_75_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 }, + { 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 }, + { 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 }, + { 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 }, + { 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 }, + { 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, + { 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 }, + { 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 }, + { 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 }, + { 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 }, + { 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 }, + { 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 }, +}; + +static const uint8_t conv_tch_afs_4_75_next_term_output[] = { + 0, 7, 27, 28, 27, 28, 0, 7, 7, 0, 28, 27, 28, 27, 7, 0, + 24, 31, 3, 4, 3, 4, 24, 31, 31, 24, 4, 3, 4, 3, 31, 24, + 31, 24, 4, 3, 4, 3, 31, 24, 24, 31, 3, 4, 3, 4, 24, 31, + 7, 0, 28, 27, 28, 27, 7, 0, 0, 7, 27, 28, 27, 28, 0, 7, +}; + +static const uint8_t conv_tch_afs_4_75_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_afs_4_75_puncture[] = { + 0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55, + 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175, + 185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295, + 305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405, + 410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460, + 465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509, + 510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530, + 531, 532, 534, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_4_75 = { + .N = 5, + .K = 7, + .len = 101, + .next_output = conv_tch_afs_4_75_next_output, + .next_state = conv_tch_afs_4_75_next_state, + .next_term_output = conv_tch_afs_4_75_next_term_output, + .next_term_state = conv_tch_afs_4_75_next_term_state, + .puncture = conv_tch_afs_4_75_puncture, +}; + + diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h index a73d24ba..7e0121c9 100644 --- a/src/osmo-bts-trx/gsm0503_conv.h +++ b/src/osmo-bts-trx/gsm0503_conv.h @@ -7,5 +7,13 @@ extern const struct osmo_conv_code gsm0503_conv_cs3; extern const struct osmo_conv_code gsm0503_conv_rach; extern const struct osmo_conv_code gsm0503_conv_sch; extern const struct osmo_conv_code gsm0503_conv_tch_fr; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_12_2; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_10_2; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_95; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_4; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_6_7; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_9; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_15; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_4_75; #endif /* _0503_CONV_H */ diff --git a/src/osmo-bts-trx/gsm0503_interleaving.c b/src/osmo-bts-trx/gsm0503_interleaving.c index 7a721b57..014b4c9c 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.c +++ b/src/osmo-bts-trx/gsm0503_interleaving.c @@ -51,7 +51,7 @@ void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB) } /* - * GSM TCH FR/EFR interleaving and burst mapping + * GSM TCH FR/EFR/AFS interleaving and burst mapping * * Interleaving: * diff --git a/src/osmo-bts-trx/gsm0503_parity.c b/src/osmo-bts-trx/gsm0503_parity.c index cbb2bf0b..0c5f77cf 100644 --- a/src/osmo-bts-trx/gsm0503_parity.c +++ b/src/osmo-bts-trx/gsm0503_parity.c @@ -88,3 +88,16 @@ const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = { .remainder = 0x00, }; +/* + * GSM AMR parity + * + * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 + */ + +const struct osmo_crc8gen_code gsm0503_amr_crc6 = { + .bits = 6, + .poly = 0x2f, + .init = 0x00, + .remainder = 0x3f, +}; + diff --git a/src/osmo-bts-trx/gsm0503_parity.h b/src/osmo-bts-trx/gsm0503_parity.h index e3678b22..ee52328b 100644 --- a/src/osmo-bts-trx/gsm0503_parity.h +++ b/src/osmo-bts-trx/gsm0503_parity.h @@ -7,5 +7,6 @@ const struct osmo_crc8gen_code gsm0503_rach_crc6; const struct osmo_crc16gen_code gsm0503_sch_crc10; const struct osmo_crc8gen_code gsm0503_tch_fr_crc3; const struct osmo_crc8gen_code gsm0503_tch_efr_crc8; +const struct osmo_crc8gen_code gsm0503_amr_crc6; #endif /* _0503_PARITY_H */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 837cc68e..4c5ab7a1 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -121,3 +121,18 @@ const uint8_t gsm0503_gsm_efr_protected_bits[65] = { 240, 88,138,191,241 }; +/* Encoded in-band data for speech frames */ +const ubit_t gsm0503_afs_ic_ubit[4][8] = { + { 0,0,0,0,0,0,0,0 }, + { 0,1,0,1,1,1,0,1 }, + { 1,0,1,1,1,0,1,0 }, + { 1,1,1,0,0,1,1,1 }, +}; + +const sbit_t gsm0503_afs_ic_sbit[4][8] = { + { 127, 127, 127, 127, 127, 127, 127, 127 }, + { 127,-127, 127,-127,-127,-127, 127,-127 }, + { -127, 127,-127,-127,-127, 127,-127, 127 }, + { -127,-127,-127, 127, 127,-127,-127,-127 }, +}; + diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index 66f325bf..db0e920a 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -10,5 +10,7 @@ extern const uint8_t gsm0503_puncture_cs2[588]; extern const uint8_t gsm0503_puncture_cs3[676]; extern const uint8_t gsm0503_gsm_fr_map[76]; extern const uint8_t gsm0503_gsm_efr_protected_bits[65]; +extern const ubit_t gsm0503_afs_ic_ubit[4][8]; +extern const sbit_t gsm0503_afs_ic_sbit[4][8]; #endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 61c02cd4..9dbec31b 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "l1_if.h" #include "trx_if.h" @@ -476,7 +477,13 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) trx_sched_set_lchan(l1h, chan_nr, 0x40, 1); /* set mode */ trx_sched_set_mode(l1h, chan_nr, - lchan->rsl_cmode, lchan->tch_mode); + lchan->rsl_cmode, lchan->tch_mode, + lchan->tch.amr_mr.num_modes, + lchan->tch.amr_mr.mode[0].mode, + lchan->tch.amr_mr.mode[1].mode, + lchan->tch.amr_mr.mode[2].mode, + lchan->tch.amr_mr.mode[3].mode, + amr_get_initial_mode(lchan)); /* init lapdm */ lchan_init_lapdm(lchan); /* confirm */ @@ -487,7 +494,13 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) if (l1sap->u.info.type == PRIM_INFO_MODIFY) { /* change mode */ trx_sched_set_mode(l1h, chan_nr, - lchan->rsl_cmode, lchan->tch_mode); + lchan->rsl_cmode, lchan->tch_mode, + lchan->tch.amr_mr.num_modes, + lchan->tch.amr_mr.mode[0].mode, + lchan->tch.amr_mr.mode[1].mode, + lchan->tch.amr_mr.mode[2].mode, + lchan->tch.amr_mr.mode[3].mode, + amr_get_initial_mode(lchan)); break; } if ((chan_nr & 0x80)) { diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index ff5c3913..9cc8ee3b 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -65,6 +65,17 @@ struct trx_chan_state { /* mode */ uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */ + /* AMR */ + uint8_t codec[4]; /* 4 possible codecs for amr */ + int codecs; /* number of possible codecs */ + float ber_sum; /* sum of bit error rates */ + int ber_num; /* number of bit error rates */ + uint8_t ul_ft; /* current uplink FT index */ + uint8_t dl_ft; /* current downlink FT index */ + uint8_t ul_cmr; /* current uplink CMR index */ + uint8_t dl_cmr; /* current downlink CMR index */ + uint8_t amr_loop; /* if AMR loop is enabled */ + /* encryption */ int ul_encr_algo; /* A5/x encry algo downlink */ int dl_encr_algo; /* A5/x encry algo uplink */ diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c index ee12e8bd..8b6844be 100644 --- a/src/osmo-bts-trx/loops.c +++ b/src/osmo-bts-trx/loops.c @@ -245,3 +245,100 @@ int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, return 0; } +int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state, float ber) +{ + struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)] + .lchan[l1sap_chan2ss(chan_nr)]; + int c_i; + + /* check if loop is enabled */ + if (!chan_state->amr_loop) + return 0; + + /* wait for MS to use the requested codec */ + if (chan_state->ul_ft != chan_state->dl_cmr) + return 0; + + /* count bit errors */ + if (L1SAP_IS_CHAN_TCHH(chan_nr)) { + chan_state->ber_num += 2; + chan_state->ber_sum += (ber + ber); + } else { + chan_state->ber_num++; + chan_state->ber_sum += ber; + } + + /* count frames */ + if (chan_state->ber_num < 48) + return 0; + + /* calculate average (reuse ber variable) */ + ber = chan_state->ber_sum / chan_state->ber_num; + + /* FIXME: calculate C/I from BER */ + c_i = ber * 100; + + /* reset bit errors */ + chan_state->ber_num = 0; + chan_state->ber_sum = 0; + + LOGP(DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f " + "codec id %d of trx=%u chan_nr=0x%02x\n", ber, + chan_state->ul_ft, l1h->trx->nr, chan_nr); + + /* degrade */ + if (chan_state->dl_cmr > 0) { + /* degrade, if ber is above threshold FIXME: C/I */ + if (ber > + lchan->tch.amr_mr.mode[chan_state->dl_cmr-1].threshold_bts) { + LOGP(DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f " + "from codec id %d to %d of trx=%u " + "chan_nr=0x%02x\n", ber, chan_state->dl_cmr, + chan_state->dl_cmr - 1, l1h->trx->nr, chan_nr); + chan_state->dl_cmr--; + } + + return 0; + } + + /* upgrade */ + if (chan_state->dl_cmr < chan_state->codecs - 1) { + /* degrade, if ber is above threshold FIXME: C/I*/ + if (ber < + lchan->tch.amr_mr.mode[chan_state->dl_cmr].threshold_bts + - lchan->tch.amr_mr.mode[chan_state->dl_cmr].hysteresis_bts) { + LOGP(DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f " + "from codec id %d to %d of trx=%u " + "chan_nr=0x%02x\n", ber, chan_state->dl_cmr, + chan_state->dl_cmr + 1, l1h->trx->nr, chan_nr); + chan_state->dl_cmr++; + } + + return 0; + } + + return 0; +} + +int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop) +{ + if (chan_state->amr_loop && !loop) { + chan_state->amr_loop = 0; + + return 0; + } + + if (!chan_state->amr_loop && loop) { + chan_state->amr_loop = 1; + + /* reset bit errors */ + chan_state->ber_num = 0; + chan_state->ber_sum = 0; + + return 0; + } + + return 0; +} + diff --git a/src/osmo-bts-trx/loops.h b/src/osmo-bts-trx/loops.h index 8b1d973d..27b0ef23 100644 --- a/src/osmo-bts-trx/loops.h +++ b/src/osmo-bts-trx/loops.h @@ -23,4 +23,9 @@ int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr, int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, struct trx_chan_state *chan_state); +int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state, float ber); + +int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop); + #endif /* _TRX_LOOPS_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6a40f97d..eb6130eb 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -33,12 +33,15 @@ #include #include #include +#include #include "l1_if.h" #include "scheduler.h" #include "gsm0503_coding.h" #include "trx_if.h" #include "loops.h" +#include "amr.h" +#include "loops.h" /* Enable this to multiply TOA of RACH by 10. * This usefull to check tenth of timing advances with RSSI test tool. @@ -757,7 +760,7 @@ send_burst: static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch, - struct msgb **_msg_facch) + struct msgb **_msg_facch, int codec_mode_request) { struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; @@ -786,6 +789,15 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(tch_data, 0, 31); len = 31; break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + len = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], 1); + if (len < 2) + break; + memset(tch_data + 2, 0, len - 2); + compose_tch_ind(l1h, tn, 0, chan, tch_data, len); + break; default: inval_mode1: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " @@ -841,9 +853,11 @@ inval_mode1: msg_facch = NULL; } - /* check validity of message */ + /* check validity of message, get AMR ft and cmr */ if (!msg_facch && msg_tch) { int len; + uint8_t bfi, cmr_codec, ft_codec; + int cmr, ft, i; if (rsl_cmode != RSL_CMOD_SPD_SPEECH) { LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, " @@ -878,12 +892,62 @@ inval_mode1: goto free_bad_msg; } break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + len = amr_decompose_payload(msg_tch->l2h, + msgb_l2len(msg_tch), &cmr_codec, &ft_codec, + &bfi); + cmr = -1; + ft = -1; + for (i = 0; i < chan_state->codecs; i++) { + if (chan_state->codec[i] == cmr_codec) + cmr = i; + if (chan_state->codec[i] == ft_codec) + ft = i; + } + if (cmr >= 0) { /* new request */ + chan_state->dl_cmr = cmr; + /* disable AMR loop */ + trx_loop_amr_set(chan_state, 0); + } else { + /* enable AMR loop */ + trx_loop_amr_set(chan_state, 1); + } + if (ft < 0) { + LOGP(DL1C, LOGL_ERROR, "%s Codec (FT = %d) " + " of RTP frame not in list. " + "trx=%u ts=%u\n", + trx_chan_desc[chan].name, ft_codec, + l1h->trx->nr, tn); + goto free_bad_msg; + } + if (codec_mode_request && chan_state->dl_ft != ft) { + LOGP(DL1C, LOGL_NOTICE, "%s Codec (FT = %d) " + " of RTP cannot be changed now, but in " + "next frame. trx=%u ts=%u\n", + trx_chan_desc[chan].name, ft_codec, + l1h->trx->nr, tn); + goto free_bad_msg; + } + chan_state->dl_ft = ft; + if (bfi) { + LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad " + "AMR frame' trx=%u ts=%u at fn=%u.\n", + trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + break; default: inval_mode2: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " "fix!\n"); goto free_bad_msg; } + if (len < 0) { + LOGP(DL1C, LOGL_ERROR, "Cannot send invalid AMR " + "payload\n"); + goto free_bad_msg; + } if (msgb_l2len(msg_tch) != len) { LOGP(DL1C, LOGL_ERROR, "Cannot send payload with " "invalid length! (expecing %d, received %d)\n", @@ -906,6 +970,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { struct msgb *msg_tch = NULL, *msg_facch = NULL; struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + uint8_t tch_mode = chan_state->tch_mode; ubit_t *burst, **bursts_p = &chan_state->dl_bursts; static ubit_t bits[148]; @@ -916,7 +981,8 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch); + tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch, + (((fn + 4) % 26) >> 2) & 1); /* alloc burst memory, if not already, * otherwise shift buffer by 4 bursts for interleaving */ @@ -941,6 +1007,15 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg_facch) tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), 1); + else if (tch_mode == GSM48_CMODE_SPEECH_AMR) + /* the first FN 4,13,21 defines that CMI is included in frame, + * the first FN 0,8,17 defines that CMR is included in frame. + */ + tch_afs_encode(*bursts_p, msg_tch->l2h + 2, + msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, + chan_state->codec, chan_state->codecs, + chan_state->dl_ft, + chan_state->dl_cmr); else tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); @@ -1176,7 +1251,8 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t rsl_cmode = chan_state->rsl_cmode; uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ - int rc; + int rc, amr = 0; + float ber; LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1225,6 +1301,27 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, case GSM48_CMODE_SPEECH_EFR: /* EFR */ rc = tch_fr_decode(tch_data, *bursts_p, 1, 1); break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* the first FN 0,8,17 defines that CMI is included in frame, + * the first FN 4,13,21 defines that CMR is included in frame. + * NOTE: A frame ends 7 FN after start. + */ + rc = tch_afs_decode(tch_data + 2, *bursts_p, + (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec, + chan_state->codecs, &chan_state->ul_ft, + &chan_state->ul_cmr, &ber); + if (rc) + trx_loop_amr_input(l1h, + trx_chan_desc[chan].chan_nr | tn, chan_state, + ber); + amr = 2; /* we store tch_data + 2 header bytes */ + /* only good speech frames get rtp header */ + if (rc != 23 && rc >= 4) { + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->ul_cmr], + chan_state->codec[chan_state->ul_ft], 0); + } + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", tch_mode); @@ -1246,7 +1343,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data, 23, rssi); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1259,6 +1356,15 @@ bfi: memset(tch_data, 0, 31); rc = 31; break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], + 1); + if (rc < 2) + break; + memset(tch_data + 2, 0, rc - 2); + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " "please fix!\n"); @@ -2106,21 +2212,38 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, - uint8_t tch_mode) + uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, + uint8_t codec2, uint8_t codec3, uint8_t initial_id) { uint8_t tn = L1SAP_CHAN2TS(chan_nr); int i; int rc = -EINVAL; + struct trx_chan_state *chan_state; /* look for all matching chan_nr/link_id */ for (i = 0; i < _TRX_CHAN_MAX; i++) { if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) && trx_chan_desc[i].link_id == 0x00) { + chan_state = &l1h->chan_states[tn][i]; LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u on " "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, trx_chan_desc[i].name, l1h->trx->nr, tn); - l1h->chan_states[tn][i].rsl_cmode = rsl_cmode; - l1h->chan_states[tn][i].tch_mode = tch_mode; + chan_state->rsl_cmode = rsl_cmode; + chan_state->tch_mode = tch_mode; + if (rsl_cmode == RSL_CMOD_SPD_SPEECH + && tch_mode == GSM48_CMODE_SPEECH_AMR) { + chan_state->codecs = codecs; + chan_state->codec[0] = codec0; + chan_state->codec[1] = codec1; + chan_state->codec[2] = codec2; + chan_state->codec[3] = codec3; + chan_state->ul_ft = initial_id; + chan_state->dl_ft = initial_id; + chan_state->ul_cmr = initial_id; + chan_state->dl_cmr = initial_id; + chan_state->ber_sum = 0; + chan_state->ber_num = 0; + } rc = 0; } } diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index 6e7c5293..2ab7abeb 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -24,11 +24,12 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, - int downlink); + int active); /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, - uint8_t tch_mode); + uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, + uint8_t codec2, uint8_t codec3, uint8_t initial_codec); /* setting cipher on logical channels */ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, -- cgit v1.2.3