aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2013-03-31 12:19:26 +0200
committerHarald Welte <laforge@gnumonks.org>2015-09-22 16:41:28 +0200
commitc5241c3aa42d7ff89a369f2a816a98bdca932217 (patch)
treee87ca67347b6902aa6ce3b7b10bb41ce365c7269
parentc910a332b261d48fa594f1a3133987c8f650dcb1 (diff)
TRX: Support for AMR half speech
-rw-r--r--src/osmo-bts-trx/gsm0503_coding.c369
-rw-r--r--src/osmo-bts-trx/gsm0503_coding.h6
-rw-r--r--src/osmo-bts-trx/gsm0503_conv.c309
-rw-r--r--src/osmo-bts-trx/gsm0503_conv.h6
-rw-r--r--src/osmo-bts-trx/gsm0503_tables.c14
-rw-r--r--src/osmo-bts-trx/gsm0503_tables.h2
-rw-r--r--src/osmo-bts-trx/scheduler.c42
7 files changed, 748 insertions, 0 deletions
diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c
index 490d0f66..c3ce9e57 100644
--- a/src/osmo-bts-trx/gsm0503_coding.c
+++ b/src/osmo-bts-trx/gsm0503_coding.c
@@ -1227,6 +1227,375 @@ facch:
return 0;
}
+int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd,
+ 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[135];
+ int i, j, k, best = 0, rv, len, steal = 0, id = 0;
+
+ /* only unmap the stealing bits */
+ if (!odd) {
+ for (i=0; i<4; i++) {
+ gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 0);
+ steal -= h;
+ }
+ for (i=2; i<5; i++) {
+ gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1);
+ steal -= h;
+ }
+ }
+
+ /* if we found a stole FACCH, but only at correct alignment */
+ if (steal > 0) {
+ for (i=0; i<6; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116],
+ NULL, i>>2);
+ for (i=2; i<4; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114 + 456],
+ &bursts[i * 116], NULL, 1);
+
+ gsm0503_tch_fr_deinterleave(cB, iB);
+
+ rv = _xcch_decode_cB(tch_data, cB);
+ if (rv)
+ return -1;
+
+ return 23;
+ }
+
+ for (i=0; i<4; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL,
+ i>>1);
+
+ gsm0503_tch_hr_deinterleave(cB, iB);
+
+ for (i=0; i<4; i++) {
+ for (j=0, k=0; j<4; j++)
+ k += abs(((int)gsm0503_ahs_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 5: /* TCH/AHS7.95 */
+ osmo_conv_decode(&gsm0503_conv_tch_ahs_7_95, cB+4, conv);
+
+ tch_amr_unmerge(d, p, conv, 123, 67);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 67, p);
+ if (rv)
+ return -1;
+
+ for (i=0; i<36;i++)
+ d[i+123] = (cB[i+192] < 0) ? 1:0;
+
+ tch_amr_reassemble(tch_data, d, 159);
+
+ len = 20;
+
+ if (ber) {
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_7_95, conv,
+ test+4);
+ *ber = amr_calc_ber(cB+4, test+4, 188);
+ }
+
+ break;
+ case 4: /* TCH/AHS7.4 */
+ osmo_conv_decode(&gsm0503_conv_tch_ahs_7_4, cB+4, conv);
+
+ tch_amr_unmerge(d, p, conv, 120, 61);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p);
+ if (rv)
+ return -1;
+
+ for (i=0; i<28;i++)
+ d[i+120] = (cB[i+200] < 0) ? 1:0;
+
+ tch_amr_reassemble(tch_data, d, 148);
+
+ len = 19;
+
+ if (ber) {
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_7_4, conv,
+ test+4);
+ *ber = amr_calc_ber(cB+4, test+4, 196);
+ }
+
+ break;
+ case 3: /* TCH/AHS6.7 */
+ osmo_conv_decode(&gsm0503_conv_tch_ahs_6_7, cB+4, conv);
+
+ tch_amr_unmerge(d, p, conv, 110, 55);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p);
+ if (rv)
+ return -1;
+
+ for (i=0; i<24;i++)
+ d[i+110] = (cB[i+204] < 0) ? 1:0;
+
+ tch_amr_reassemble(tch_data, d, 134);
+
+ len = 17;
+
+ if (ber) {
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_6_7, conv,
+ test+4);
+ *ber = amr_calc_ber(cB+4, test+4, 200);
+ }
+
+ break;
+ case 2: /* TCH/AHS5.9 */
+ osmo_conv_decode(&gsm0503_conv_tch_ahs_5_9, cB+4, conv);
+
+ tch_amr_unmerge(d, p, conv, 102, 55);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p);
+ if (rv)
+ return -1;
+
+ for (i=0; i<16;i++)
+ d[i+102] = (cB[i+212] < 0) ? 1:0;
+
+ tch_amr_reassemble(tch_data, d, 118);
+
+ len = 15;
+
+ if (ber) {
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_5_9, conv,
+ test+4);
+ *ber = amr_calc_ber(cB+4, test+4, 208);
+ }
+
+ break;
+ case 1: /* TCH/AHS5.15 */
+ osmo_conv_decode(&gsm0503_conv_tch_ahs_5_15, cB+4, conv);
+
+ tch_amr_unmerge(d, p, conv, 91, 49);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p);
+ if (rv)
+ return -1;
+
+ for (i=0; i<12;i++)
+ d[i+91] = (cB[i+216] < 0) ? 1:0;
+
+ tch_amr_reassemble(tch_data, d, 103);
+
+ len = 13;
+
+ if (ber) {
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_5_15, conv,
+ test+4);
+ *ber = amr_calc_ber(cB+4, test+4, 212);
+ }
+
+ break;
+ case 0: /* TCH/AHS4.75 */
+ osmo_conv_decode(&gsm0503_conv_tch_ahs_4_75, cB+4, conv);
+
+ tch_amr_unmerge(d, p, conv, 83, 39);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p);
+ if (rv)
+ return -1;
+
+ for (i=0; i<12;i++)
+ d[i+83] = (cB[i+216] < 0) ? 1:0;
+
+ tch_amr_reassemble(tch_data, d, 95);
+
+ len = 12;
+
+ if (ber) {
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_4_75, conv,
+ test+4);
+ *ber = amr_calc_ber(cB+4, test+4, 212);
+ }
+
+ 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_ahs_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[135];
+ int i;
+ uint8_t id;
+
+ if (len == 23) { /* FACCH */
+ _xcch_encode_cB(cB, tch_data);
+
+ h = 1;
+
+ gsm0503_tch_fr_interleave(cB, iB);
+
+ for (i=0; i<6; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116],
+ &h, i>>2);
+ for (i=2; i<4; i++)
+ gsm0503_tch_burst_map(&iB[i * 114 + 456],
+ &bursts[i * 116], &h, 1);
+
+ return 0;
+ }
+
+ 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 5: /* TCH/AHS7.95 */
+ if (len != 20) {
+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, 159);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p);
+
+ tch_amr_merge(conv, d, p, 123, 67);
+
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_7_95, conv, cB+4);
+
+ memcpy(cB+192, d+123, 36);
+
+ break;
+ case 4: /* TCH/AHS7.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, 120, 61);
+
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_7_4, conv, cB+4);
+
+ memcpy(cB+200, d+120, 28);
+
+ break;
+ case 3: /* TCH/AHS6.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, 110, 55);
+
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_6_7, conv, cB+4);
+
+ memcpy(cB+204, d+110, 24);
+
+ break;
+ case 2: /* TCH/AHS5.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, 102, 55);
+
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_5_9, conv, cB+4);
+
+ memcpy(cB+212, d+102, 16);
+
+ break;
+ case 1: /* TCH/AHS5.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, 91, 49);
+
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_5_15, conv, cB+4);
+
+ memcpy(cB+216, d+91, 12);
+
+ break;
+ case 0: /* TCH/AHS4.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, 83, 39);
+
+ osmo_conv_encode(&gsm0503_conv_tch_ahs_4_75, conv, cB+4);
+
+ memcpy(cB+216, d+83, 12);
+
+ break;
+ default:
+ fprintf(stderr, "FIXME: FT %d not supported!\n", ft);
+
+ return -1;
+ }
+
+ memcpy(cB, gsm0503_afs_ic_ubit[id], 4);
+
+ gsm0503_tch_hr_interleave(cB, iB);
+
+ for (i=0; i<4; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>1);
+
+ return 0;
+}
+
/*
* GSM RACH transcoding
*/
diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h
index 6bc7dd0e..33b58d3b 100644
--- a/src/osmo-bts-trx/gsm0503_coding.h
+++ b/src/osmo-bts-trx/gsm0503_coding.h
@@ -14,6 +14,12 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req,
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 tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, float *ber);
+int tch_ahs_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 71738969..2a814ca3 100644
--- a/src/osmo-bts-trx/gsm0503_conv.c
+++ b/src/osmo-bts-trx/gsm0503_conv.c
@@ -639,3 +639,312 @@ const struct osmo_conv_code gsm0503_conv_tch_afs_4_75 = {
};
+/* TCH/AHS7.95 */
+/* ----------- */
+
+static const uint8_t conv_tch_ahs_7_95_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_ahs_7_95_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_ahs_7_95_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_ahs_7_95_next_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
+};
+
+static int conv_tch_ahs_7_95_puncture[] = {
+ 1, 3, 5, 7, 11, 15, 19, 23, 27, 31, 35, 43,
+ 47, 51, 55, 59, 63, 67, 71, 79, 83, 87, 91, 95,
+ 99, 103, 107, 115, 119, 123, 127, 131, 135, 139, 143, 151,
+ 155, 159, 163, 167, 171, 175, 177, 179, 183, 185, 187, 191,
+ 193, 195, 197, 199, 203, 205, 207, 211, 213, 215, 219, 221,
+ 223, 227, 229, 231, 233, 235, 239, 241, 243, 247, 249, 251,
+ 255, 257, 259, 261, 263, 265,
+ -1, /* end */
+};
+
+const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95 = {
+ .N = 2,
+ .K = 5,
+ .len = 129,
+ .next_output = conv_tch_ahs_7_95_next_output,
+ .next_state = conv_tch_ahs_7_95_next_state,
+ .next_term_output = conv_tch_ahs_7_95_next_term_output,
+ .next_term_state = conv_tch_ahs_7_95_next_term_state,
+ .puncture = conv_tch_ahs_7_95_puncture,
+};
+
+
+/* TCH/AHS7.4 */
+/* ---------- */
+
+static const uint8_t conv_tch_ahs_7_4_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_ahs_7_4_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_ahs_7_4_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_ahs_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_ahs_7_4_puncture[] = {
+ 1, 3, 7, 11, 19, 23, 27, 35, 39, 43, 51, 55,
+ 59, 67, 71, 75, 83, 87, 91, 99, 103, 107, 115, 119,
+ 123, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171,
+ 175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219,
+ 221, 223, 227, 229, 231, 235, 237, 239, 243, 245, 247, 251,
+ 253, 255, 257, 259,
+ -1, /* end */
+};
+
+const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4 = {
+ .N = 2,
+ .K = 5,
+ .len = 126,
+ .next_output = conv_tch_ahs_7_4_next_output,
+ .next_state = conv_tch_ahs_7_4_next_state,
+ .next_term_output = conv_tch_ahs_7_4_next_term_output,
+ .next_term_state = conv_tch_ahs_7_4_next_term_state,
+ .puncture = conv_tch_ahs_7_4_puncture,
+};
+
+
+/* TCH/AHS6.7 */
+/* ---------- */
+
+static const uint8_t conv_tch_ahs_6_7_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_ahs_6_7_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_ahs_6_7_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_ahs_6_7_next_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
+};
+
+static int conv_tch_ahs_6_7_puncture[] = {
+ 1, 3, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99,
+ 109, 119, 129, 139, 149, 159, 167, 169, 177, 179, 187, 189,
+ 197, 199, 203, 207, 209, 213, 217, 219, 223, 227, 229, 231,
+ 233, 235, 237, 239,
+ -1, /* end */
+};
+
+const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7 = {
+ .N = 2,
+ .K = 5,
+ .len = 116,
+ .next_output = conv_tch_ahs_6_7_next_output,
+ .next_state = conv_tch_ahs_6_7_next_state,
+ .next_term_output = conv_tch_ahs_6_7_next_term_output,
+ .next_term_state = conv_tch_ahs_6_7_next_term_state,
+ .puncture = conv_tch_ahs_6_7_puncture,
+};
+
+
+/* TCH/AHS5.9 */
+/* ---------- */
+
+static const uint8_t conv_tch_ahs_5_9_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_ahs_5_9_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_ahs_5_9_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_ahs_5_9_next_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
+};
+
+static int conv_tch_ahs_5_9_puncture[] = {
+ 1, 15, 71, 127, 139, 151, 163, 175, 187, 195, 203, 211,
+ 215, 219, 221, 223,
+ -1, /* end */
+};
+
+const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9 = {
+ .N = 2,
+ .K = 5,
+ .len = 108,
+ .next_output = conv_tch_ahs_5_9_next_output,
+ .next_state = conv_tch_ahs_5_9_next_state,
+ .next_term_output = conv_tch_ahs_5_9_next_term_output,
+ .next_term_state = conv_tch_ahs_5_9_next_term_state,
+ .puncture = conv_tch_ahs_5_9_puncture,
+};
+
+
+/* TCH/AHS5.15 */
+/* ----------- */
+
+static const uint8_t conv_tch_ahs_5_15_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_ahs_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_ahs_5_15_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_ahs_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_ahs_5_15_puncture[] = {
+ 0, 1, 3, 4, 6, 9, 12, 15, 18, 21, 27, 33,
+ 39, 45, 51, 54, 57, 63, 69, 75, 81, 87, 90, 93,
+ 99, 105, 111, 117, 123, 126, 129, 135, 141, 147, 153, 159,
+ 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195,
+ 198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231,
+ 234, 237, 240, 243, 244, 246, 249, 252, 255, 256, 258, 261,
+ 264, 267, 268, 270, 273, 276, 279, 280, 282, 285, 288, 289,
+ 291, 294, 295, 297, 298, 300, 301,
+ -1, /* end */
+};
+
+const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15 = {
+ .N = 3,
+ .K = 5,
+ .len = 97,
+ .next_output = conv_tch_ahs_5_15_next_output,
+ .next_state = conv_tch_ahs_5_15_next_state,
+ .next_term_output = conv_tch_ahs_5_15_next_term_output,
+ .next_term_state = conv_tch_ahs_5_15_next_term_state,
+ .puncture = conv_tch_ahs_5_15_puncture,
+};
+
+
+/* TCH/AHS4.75 */
+/* ----------- */
+
+static const uint8_t conv_tch_ahs_4_75_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_ahs_4_75_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_ahs_4_75_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_ahs_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_ahs_4_75_puncture[] = {
+ 1, 2, 4, 5, 7, 8, 10, 13, 16, 22, 28, 34,
+ 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106,
+ 112, 118, 124, 130, 136, 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, 241, 244, 247, 250, 253, 256,
+ 259, 262, 265, 268, 271, 274, 275, 277, 278, 280, 281, 283,
+ 284,
+ -1, /* end */
+};
+
+const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75 = {
+ .N = 3,
+ .K = 7,
+ .len = 89,
+ .next_output = conv_tch_ahs_4_75_next_output,
+ .next_state = conv_tch_ahs_4_75_next_state,
+ .next_term_output = conv_tch_ahs_4_75_next_term_output,
+ .next_term_state = conv_tch_ahs_4_75_next_term_state,
+ .puncture = conv_tch_ahs_4_75_puncture,
+};
+
diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h
index 045a0959..68ad4844 100644
--- a/src/osmo-bts-trx/gsm0503_conv.h
+++ b/src/osmo-bts-trx/gsm0503_conv.h
@@ -16,5 +16,11 @@ 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;
+extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95;
+extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4;
+extern const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7;
+extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9;
+extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15;
+extern const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75;
#endif /* _0503_CONV_H */
diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c
index 1c272d8f..a3817842 100644
--- a/src/osmo-bts-trx/gsm0503_tables.c
+++ b/src/osmo-bts-trx/gsm0503_tables.c
@@ -136,6 +136,20 @@ const sbit_t gsm0503_afs_ic_sbit[4][8] = {
{ -127,-127,-127, 127, 127,-127,-127,-127 },
};
+const ubit_t gsm0503_ahs_ic_ubit[4][4] = {
+ { 0,0,0,0 },
+ { 1,0,0,1 },
+ { 1,1,1,0 },
+ { 0,1,1,1 },
+};
+
+const sbit_t gsm0503_ahs_ic_sbit[4][4] = {
+ { 127, 127, 127, 127 },
+ { -127, 127, 127,-127 },
+ { -127,-127,-127, 127 },
+ { 127,-127,-127,-127 },
+};
+
const uint8_t gsm0503_tch_hr_interleaving[228][2] = {
{ 0 ,0 }, { 1 ,2 }, { 78 ,1 }, { 79 ,3 }, { 48 ,0 }, { 49 ,2 },
{ 54 ,1 }, { 55 ,3 }, { 24 ,0 }, { 25 ,2 }, { 30 ,1 }, { 31 ,3 },
diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h
index b068fdd6..63b45a4d 100644
--- a/src/osmo-bts-trx/gsm0503_tables.h
+++ b/src/osmo-bts-trx/gsm0503_tables.h
@@ -12,6 +12,8 @@ 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];
+extern const ubit_t gsm0503_ahs_ic_ubit[4][4];
+extern const sbit_t gsm0503_ahs_ic_sbit[4][4];
extern const uint8_t gsm0503_tch_hr_interleaving[228][2];
#endif /* _0503_TABLES_H */
diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c
index 65da30eb..9126e342 100644
--- a/src/osmo-bts-trx/scheduler.c
+++ b/src/osmo-bts-trx/scheduler.c
@@ -1065,6 +1065,7 @@ static ubit_t *tx_tchh_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];
@@ -1118,6 +1119,15 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
chan_state->dl_ongoing_facch = 1; /* first of two tch frames */
} else if (chan_state->dl_ongoing_facch) /* second of two tch frames */
chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
+ else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ /* the first FN 4,13,21 or 5,14,22 defines that CMI is included
+ * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
+ * included in frame. */
+ tch_ahs_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_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch));
@@ -1486,6 +1496,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
uint8_t tch_mode = chan_state->tch_mode;
uint8_t tch_data[128]; /* just to be safe */
int rc, amr = 0;
+ float ber;
LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n",
trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
@@ -1544,6 +1555,28 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
rc = tch_hr_decode(tch_data, *bursts_p,
(((fn + 26 - 10) % 26) >> 2) & 1);
break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* the first FN 0,8,17 or 1,9,18 defines that CMI is included
+ * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR
+ * is included in frame.
+ */
+ rc = tch_ahs_decode(tch_data + 2, *bursts_p,
+ (((fn + 26 - 10) % 26) >> 2) & 1,
+ (((fn + 26 - 10) % 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 two */
+ /* 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);
@@ -1578,6 +1611,15 @@ bfi:
memset(tch_data + 1, 0, 14);
rc = 15;
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");