From b0d65315abf5d0c4fcc5820fe9d84cf772c2efe8 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 31 Mar 2013 12:19:26 +0200 Subject: [PATCH] TRX: Support for AMR half speech --- src/osmo-bts-trx/gsm0503_coding.c | 369 ++++++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_coding.h | 6 + src/osmo-bts-trx/gsm0503_conv.c | 309 +++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_conv.h | 6 + src/osmo-bts-trx/gsm0503_tables.c | 14 ++ src/osmo-bts-trx/gsm0503_tables.h | 2 + src/osmo-bts-trx/scheduler.c | 42 ++++ 7 files changed, 748 insertions(+) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 490d0f66c..c3ce9e570 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 6bc7dd0e3..33b58d3bf 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 71738969d..2a814ca33 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 045a09598..68ad4844c 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 1c272d8f9..a3817842c 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 b068fdd61..63b45a4df 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 65da30eb7..9126e342a 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");