aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2013-03-26 09:05:14 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2014-04-06 08:57:54 +0200
commit934056e7f8ea62ff1eba0a3110a3840a7af58857 (patch)
tree62c3a515e9e7a6e9d8b22a6905293e5d6b9f35fc
parentaad7482d8147398ce7949b3d1b1162f2297aaa0e (diff)
Support for AMR full speech
-rw-r--r--src/osmo-bts-trx/Makefile.am4
-rw-r--r--src/osmo-bts-trx/gsm0503_coding.c479
-rw-r--r--src/osmo-bts-trx/gsm0503_coding.h5
-rw-r--r--src/osmo-bts-trx/gsm0503_conv.c484
-rw-r--r--src/osmo-bts-trx/gsm0503_conv.h8
-rw-r--r--src/osmo-bts-trx/gsm0503_interleaving.c2
-rw-r--r--src/osmo-bts-trx/gsm0503_parity.c13
-rw-r--r--src/osmo-bts-trx/gsm0503_parity.h1
-rw-r--r--src/osmo-bts-trx/gsm0503_tables.c15
-rw-r--r--src/osmo-bts-trx/gsm0503_tables.h2
-rw-r--r--src/osmo-bts-trx/l1_if.c17
-rw-r--r--src/osmo-bts-trx/l1_if.h11
-rw-r--r--src/osmo-bts-trx/loops.c97
-rw-r--r--src/osmo-bts-trx/loops.h5
-rw-r--r--src/osmo-bts-trx/scheduler.c139
-rw-r--r--src/osmo-bts-trx/scheduler.h5
16 files changed, 1250 insertions, 37 deletions
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<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
*/
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 <osmo-bts/rsl.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/bts_model.h>
+#include <osmo-bts/amr.h>
#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 <osmo-bts/logging.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/amr.h>
#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,