From c910a332b261d48fa594f1a3133987c8f650dcb1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 31 Mar 2013 12:17:02 +0200 Subject: TRX: Support for TCH/H and GSM half rate transcoding --- src/osmo-bts-trx/gsm0503_coding.c | 181 ++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_coding.h | 2 + src/osmo-bts-trx/gsm0503_conv.c | 74 +++++++++++ src/osmo-bts-trx/gsm0503_conv.h | 1 + src/osmo-bts-trx/gsm0503_interleaving.c | 46 +++++++ src/osmo-bts-trx/gsm0503_interleaving.h | 2 + src/osmo-bts-trx/gsm0503_parity.c | 2 +- src/osmo-bts-trx/gsm0503_tables.c | 41 ++++++ src/osmo-bts-trx/gsm0503_tables.h | 1 + src/osmo-bts-trx/l1_if.h | 4 + src/osmo-bts-trx/scheduler.c | 224 ++++++++++++++++++++++++++++++-- tests/bursts/bursts_test.c | 76 +++++++++++ 12 files changed, 645 insertions(+), 9 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 0fb07283..490d0f66 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -369,6 +369,35 @@ static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) } } +static void tch_hr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +{ + int i, j; + + tch_data[0] = 0x00; /* F = 0, FT = 000 */ + memset(tch_data + 1, 0, 14); + + i = 0; /* counts bits */ + j = 8; /* counts output bits */ + while (i < 112) { + tch_data[j>>3] |= (b_bits[i] << (7-(j&7))); + i++; + j++; + } +} + +static void tch_hr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +{ + int i, j; + + i = 0; /* counts bits */ + j = 8; /* counts output bits */ + while (i < 112) { + b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } +} + static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) { int i, j; @@ -442,6 +471,35 @@ static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) d_bits[i] = b_bits[gsm610_bitorder[i]]; } +static void tch_hr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) +{ + int i; + + const uint16_t *map; + + if (!d_bits[93] && !d_bits[94]) + map = gsm620_unvoiced_bitorder; + else + map = gsm620_voiced_bitorder; + + for (i = 0; i < 112; i++) + b_bits[map[i]] = d_bits[i]; +} + +static void tch_hr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) +{ + int i; + const uint16_t *map; + + if (!b_bits[34] && !b_bits[35]) + map = gsm620_unvoiced_bitorder; + else + map = gsm620_voiced_bitorder; + + for (i = 0; i < 112; i++) + d_bits[i] = b_bits[map[i]]; +} + static void tch_efr_d_to_w(ubit_t *b_bits, ubit_t *d_bits) { int i; @@ -490,6 +548,18 @@ static void tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) u[91+i] = p[i]; } +static void tch_hr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) +{ + memcpy(d, u, 95); + memcpy(p, u+95, 3); +} + +static void tch_hr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) +{ + memcpy(u, d, 95); + memcpy(u+95, p, 3); +} + static void tch_efr_reorder(ubit_t *w, ubit_t *s, ubit_t *p) { memcpy(w, s, 71); @@ -653,6 +723,117 @@ coding_efr_fr: return 0; } +int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd) +{ + sbit_t iB[912], cB[456], h; + ubit_t conv[98], b[112], d[112], p[3]; + int i, rv, steal = 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); + + osmo_conv_decode(&gsm0503_conv_tch_hr, cB, conv); + + tch_hr_unreorder(d, p, conv); + + for (i=0; i<17; i++) + d[i+95] = (cB[i+211] < 0) ? 1:0; + + rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p); + if (rv) + return -1; + + tch_hr_d_to_b(b, d); + + tch_hr_reassemble(tch_data, b); + + return 15; +} + +int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len) +{ + ubit_t iB[912], cB[456], h; + ubit_t conv[98], b[112], d[112], p[3]; + int i; + + switch (len) { + case 15: /* TCH HR */ + tch_hr_disassemble(b, tch_data); + + tch_hr_b_to_d(d, b); + + osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p); + + tch_hr_reorder(conv, d, p); + + osmo_conv_encode(&gsm0503_conv_tch_hr, conv, cB); + + memcpy(cB+211, d+95, 17); + + h = 0; + + 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); + + break; + case 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); + + break; + default: + return -1; + } + + return 0; +} + static float amr_calc_ber(sbit_t *orig, ubit_t *test, int len) { int i, err = 0; diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 97da7baf..6bc7dd0e 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -7,6 +7,8 @@ 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_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd); +int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int 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); int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index 4b95f72c..71738969 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -81,6 +81,80 @@ const struct osmo_conv_code gsm0503_conv_tch_fr = { }; +/* + * GSM HR convolutional coding + * + * G_0 = 1 + x^2 + x^3 + x^5 + x^6 + * G_1 = 1 + x + x^4 + x^6 + * G_3 = 1 + x + x^2 + x^3 + x^4 + x^6 + */ + +static const uint8_t conv_tch_hr_next_output[][2] = { + { 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 }, + { 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 }, + { 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 }, + { 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 }, + { 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 }, + { 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 }, + { 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 }, + { 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 }, + { 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 }, + { 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 }, +}; + +static const uint8_t conv_tch_hr_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, + { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, + { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, +}; + +static const int conv_tch_hr_puncture[] = { + /* Class 1 bits */ + 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, + 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, + 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, + 109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142, + 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178, + 181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 214, + 217, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250, + 253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283, + + /* Tail bits */ + 295, 298, 301, 304, 307, 310, 313, + + /* End */ + -1, +}; + +const struct osmo_conv_code gsm0503_conv_tch_hr = { + .N = 3, + .K = 7, + .len = 98, + .next_output = conv_tch_hr_next_output, + .next_state = conv_tch_hr_next_state, + .puncture = conv_tch_hr_puncture, +}; + + /* TCH/AFS12.2 */ /* ----------- */ diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h index 7e0121c9..045a0959 100644 --- a/src/osmo-bts-trx/gsm0503_conv.h +++ b/src/osmo-bts-trx/gsm0503_conv.h @@ -7,6 +7,7 @@ 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_hr; 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; diff --git a/src/osmo-bts-trx/gsm0503_interleaving.c b/src/osmo-bts-trx/gsm0503_interleaving.c index 014b4c9c..ec47a6b3 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.c +++ b/src/osmo-bts-trx/gsm0503_interleaving.c @@ -96,3 +96,49 @@ void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB) } } +/* + * GSM TCH HR/AHS interleaving and burst mapping + * + * Interleaving: + * + * Given 288 coded input bits, form 4 blocks of 114 bits, + * where even bits of the first 2 blocks and odd bits of the last 2 blocks + * are used: + * + * i(B, j) = c(n, k) k = 0, ..., 227 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 2n + b + * j, b = table[k]; + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<228; k++) { + B = gsm0503_tch_hr_interleaving[k][1]; + j = gsm0503_tch_hr_interleaving[k][0]; + cB[k] = iB[B * 114 + j]; + } +} + +void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<228; k++) { + B = gsm0503_tch_hr_interleaving[k][1]; + j = gsm0503_tch_hr_interleaving[k][0]; + iB[B * 114 + j] = cB[k]; + } +} + diff --git a/src/osmo-bts-trx/gsm0503_interleaving.h b/src/osmo-bts-trx/gsm0503_interleaving.h index d4c2fdbb..4d546f6a 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.h +++ b/src/osmo-bts-trx/gsm0503_interleaving.h @@ -5,5 +5,7 @@ void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB); void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB); void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB); void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB); +void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB); +void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB); #endif /* _0503_INTERLEAVING_H */ diff --git a/src/osmo-bts-trx/gsm0503_parity.c b/src/osmo-bts-trx/gsm0503_parity.c index 0c5f77cf..ba2e144a 100644 --- a/src/osmo-bts-trx/gsm0503_parity.c +++ b/src/osmo-bts-trx/gsm0503_parity.c @@ -63,7 +63,7 @@ const struct osmo_crc16gen_code gsm0503_sch_crc10 = { /* - * GSM TCH FR/EFR parity + * GSM TCH FR/HR/EFR parity * * g(x) = x^3 + x + 1 */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 4c5ab7a1..1c272d8f 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -136,3 +136,44 @@ const sbit_t gsm0503_afs_ic_sbit[4][8] = { { -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 }, + { 72 ,0 }, { 73 ,2 }, { 6 ,1 }, { 7 ,3 }, { 96 ,0 }, { 97 ,2 }, + { 12 ,0 }, { 13 ,2 }, { 102,1 }, { 103,3 }, { 60 ,0 }, { 61 ,2 }, + { 66 ,1 }, { 67 ,3 }, { 90 ,1 }, { 91 ,3 }, { 36 ,0 }, { 37 ,2 }, + { 42 ,1 }, { 43 ,3 }, { 18 ,1 }, { 19 ,3 }, { 84 ,0 }, { 85 ,2 }, + { 108,0 }, { 109,2 }, { 2 ,0 }, { 3 ,2 }, { 80 ,1 }, { 81 ,3 }, + { 50 ,0 }, { 51 ,2 }, { 56 ,1 }, { 57 ,3 }, { 26 ,0 }, { 27 ,2 }, + { 32 ,1 }, { 33 ,3 }, { 74 ,0 }, { 75 ,2 }, { 8 ,1 }, { 9 ,3 }, + { 98 ,0 }, { 99 ,2 }, { 14 ,0 }, { 15 ,2 }, { 104,1 }, { 105,3 }, + { 62 ,0 }, { 63 ,2 }, { 68 ,1 }, { 69 ,3 }, { 92 ,1 }, { 93 ,3 }, + { 38 ,0 }, { 39 ,2 }, { 44 ,1 }, { 45 ,3 }, { 20 ,1 }, { 21 ,3 }, + { 86 ,0 }, { 87 ,2 }, { 110,0 }, { 111,2 }, { 4 ,0 }, { 5 ,2 }, + { 82 ,1 }, { 83 ,3 }, { 52 ,0 }, { 53 ,2 }, { 58 ,1 }, { 59 ,3 }, + { 28 ,0 }, { 29 ,2 }, { 34 ,1 }, { 35 ,3 }, { 76 ,0 }, { 77 ,2 }, + { 10 ,1 }, { 12 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 }, + { 106,1 }, { 107,3 }, { 64 ,0 }, { 65 ,2 }, { 70 ,1 }, { 71 ,3 }, + { 94 ,1 }, { 95 ,3 }, { 40 ,0 }, { 41 ,2 }, { 46 ,1 }, { 47 ,3 }, + { 22 ,1 }, { 23 ,3 }, { 88 ,0 }, { 89 ,2 }, { 112,0 }, { 113,2 }, + { 6 ,0 }, { 7 ,2 }, { 84 ,1 }, { 85 ,3 }, { 54 ,0 }, { 55 ,2 }, + { 60 ,1 }, { 61 ,3 }, { 30 ,0 }, { 31 ,2 }, { 36 ,1 }, { 37 ,3 }, + { 78 ,0 }, { 79 ,2 }, { 12 ,1 }, { 13 ,3 }, { 102,0 }, { 103,2 }, + { 18 ,0 }, { 19 ,2 }, { 108,1 }, { 109,3 }, { 66 ,0 }, { 67 ,2 }, + { 72 ,1 }, { 73 ,3 }, { 96 ,1 }, { 97 ,3 }, { 42 ,0 }, { 43 ,2 }, + { 48 ,1 }, { 49 ,3 }, { 24 ,1 }, { 25 ,3 }, { 90 ,0 }, { 91 ,2 }, + { 0 ,1 }, { 1 ,3 }, { 8 ,0 }, { 9 ,2 }, { 86 ,1 }, { 87 ,3 }, + { 56 ,0 }, { 57 ,2 }, { 62 ,1 }, { 63 ,3 }, { 32 ,0 }, { 33 ,2 }, + { 38 ,1 }, { 39 ,3 }, { 80 ,0 }, { 81 ,2 }, { 14 ,1 }, { 15 ,3 }, + { 104,0 }, { 105,2 }, { 20 ,0 }, { 21 ,2 }, { 110,1 }, { 111,3 }, + { 68 ,0 }, { 69 ,2 }, { 74 ,1 }, { 75 ,3 }, { 98 ,1 }, { 99 ,3 }, + { 44 ,0 }, { 45 ,2 }, { 50 ,1 }, { 51 ,3 }, { 26 ,1 }, { 27 ,3 }, + { 92 ,0 }, { 93 ,2 }, { 2 ,1 }, { 3 ,3 }, { 10 ,0 }, { 11 ,2 }, + { 88 ,1 }, { 89 ,3 }, { 58 ,0 }, { 59 ,2 }, { 64 ,1 }, { 65 ,3 }, + { 34 ,0 }, { 35 ,2 }, { 40 ,1 }, { 41 ,3 }, { 82 ,0 }, { 83 ,2 }, + { 16 ,1 }, { 17 ,3 }, { 106,0 }, { 107,2 }, { 22 ,0 }, { 23 ,2 }, + { 112,1 }, { 113,3 }, { 70 ,0 }, { 71 ,2 }, { 76 ,1 }, { 77 ,3 }, + { 100,1 }, { 101,3 }, { 46 ,0 }, { 47 ,2 }, { 52 ,1 }, { 53 ,3 }, + { 28 ,1 }, { 29 ,3 }, { 94 ,0 }, { 95 ,2 }, { 4 ,1 }, { 5 ,3 }, +}; + diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index db0e920a..b068fdd6 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -12,5 +12,6 @@ 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 uint8_t gsm0503_tch_hr_interleaving[228][2]; #endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 9cc8ee3b..0b5d0855 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -76,6 +76,10 @@ struct trx_chan_state { uint8_t dl_cmr; /* current downlink CMR index */ uint8_t amr_loop; /* if AMR loop is enabled */ + /* TCH/H */ + uint8_t dl_ongoing_facch; /* FACCH/H on downlink */ + uint8_t ul_ongoing_facch; /* FACCH/H on uplink */ + /* 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/scheduler.c b/src/osmo-bts-trx/scheduler.c index eb6130eb..65da30eb 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -421,8 +421,8 @@ static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan) { - // FIXME - return 0; + /* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */ + return rts_tch_common(l1h, tn, fn, chan, ((fn % 26) >> 2) & 1); } @@ -780,6 +780,12 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* indicate bad frame */ switch (tch_mode) { case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + if (chan != TRXC_TCHF) { /* HR */ + tch_data[0] = 0x70; /* F = 0, FT = 111 */ + memset(tch_data + 1, 0, 14); + len = 15; + break; + } memset(tch_data, 0, 33); len = 33; break; @@ -869,6 +875,20 @@ inval_mode1: switch (tch_mode) { case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + if (chan != TRXC_TCHF) { /* HR */ + len = 15; + if (msgb_l2len(msg_tch) >= 1 + && (msg_tch->l2h[0] & 0xf0) != 0x00) { + LOGP(DL1C, LOGL_NOTICE, "%s " + "Transmitting 'bad " + "HR frame' trx=%u ts=%u at " + "fn=%u.\n", + trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + } len = 33; if (msgb_l2len(msg_tch) >= 1 && (msg_tch->l2h[0] >> 4) != 0xd) { @@ -1043,10 +1063,84 @@ send_burst: static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { - // FIXME - return NULL; -} + struct msgb *msg_tch = NULL, *msg_facch = NULL; + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + ubit_t *burst, **bursts_p = &chan_state->dl_bursts; + static ubit_t bits[148]; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + /* get TCH and/or FACCH */ + tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch, + (((fn + 4) % 26) >> 2) & 1); + + /* check for FACCH alignment */ + if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) { + LOGP(DL1C, LOGL_ERROR, "%s Cannot transmit FACCH starting on " + "even frames, please fix RTS!\n", + trx_chan_desc[chan].name); + msgb_free(msg_facch); + msg_facch = NULL; + } + + /* alloc burst memory, if not already, + * otherwise shift buffer by 2 bursts for interleaving */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 696); + if (!*bursts_p) + return NULL; + } else { + memcpy(*bursts_p, *bursts_p + 232, 232); + if (chan_state->dl_ongoing_facch) { + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + memset(*bursts_p + 464, 0, 232); + } else { + memset(*bursts_p + 232, 0, 232); + } + } + + /* mo message at all */ + if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { + LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + goto send_burst; + } + + /* encode bursts (priorize FACCH) */ + if (msg_facch) { + tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); + 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 + tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); + + /* free message */ + if (msg_tch) + msgb_free(msg_tch); + if (msg_facch) + msgb_free(msg_facch); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, tsc[l1h->config.tsc], 26); + memcpy(bits + 87, burst + 58, 58); + memset(bits + 145, 0, 3); + + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + return bits; +} /* @@ -1385,11 +1479,125 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, float toa) { - LOGP(DL1C, LOGL_DEBUG, "TCH/H Received %s fn=%u ts=%u trx=%u bid=%u\n", + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint8_t *mask = &chan_state->ul_mask; + 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, amr = 0; + + 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); - // FIXME - return 0; + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 696); + if (!*bursts_p) + return -ENOMEM; + } + + /* clear burst */ + if (bid == 0) { + memset(*bursts_p + 464, 0, 232); + *mask = 0x0; + } + + /* update mask */ + *mask |= (1 << bid); + + /* copy burst to end of buffer of 6 bursts */ + burst = *bursts_p + bid * 116 + 464; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + /* wait until complete set of bursts */ + if (bid != 1) + return 0; + + /* check for complete set of bursts */ + if ((*mask & 0x3) != 0x3) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " + "at fn=%u (%u/%u) for %s\n", fn, + fn % l1h->mf_period[tn], l1h->mf_period[tn], + trx_chan_desc[chan].name); + } + *mask = 0x0; + + /* skip second of two TCH frames of FACCH was received */ + if (chan_state->ul_ongoing_facch) { + chan_state->ul_ongoing_facch = 0; + memcpy(*bursts_p, *bursts_p + 232, 232); + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + goto bfi; + } + + /* decode + * also shift buffer by 4 bursts for interleaving */ + switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 + : tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* HR or signalling */ + /* Note on FN-10: If we are at FN 10, we decoded an even aligned + * TCH/FACCH frame, because our burst buffer carries 6 bursts. + * Even FN ending at: 10,11,19,20,2,3 + */ + rc = tch_hr_decode(tch_data, *bursts_p, + (((fn + 26 - 10) % 26) >> 2) & 1); + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", + tch_mode); + return -EINVAL; + } + memcpy(*bursts_p, *bursts_p + 232, 232); + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + if (rc < 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, trx_chan_desc[chan].name); + goto bfi; + } + if (rc < 4) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s with codec mode %d (out of range)\n", + fn, trx_chan_desc[chan].name, rc); + goto bfi; + } + + /* FACCH */ + if (rc == 23) { + chan_state->ul_ongoing_facch = 1; + compose_ph_data_ind(l1h, tn, + (fn + 2715648 - 10 - ((fn % 26) >= 19)) % 2715648, chan, + tch_data + amr, 23, rssi); +bfi: + if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { + /* indicate bad frame */ + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* HR */ + tch_data[0] = 0x70; /* F = 0, FT = 111 */ + memset(tch_data + 1, 0, 14); + rc = 15; + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " + "please fix!\n"); + return -EINVAL; + } + } + } + + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + + /* TCH or BFI */ + /* Note on FN 19 or 20: If we received the last burst of a frame, + * it actually starts at FN 8 or 9. A burst starting there, overlaps + * with the slot 12, so an extra FN must be substracted to get correct + * start of frame. + */ + return compose_tch_ind(l1h, tn, + (fn + 2715648 - 10 - ((fn%26)==19) - ((fn%26)==20)) % 2715648, + chan, tch_data, rc); } diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index 935cbc6d..608baacb 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -254,6 +254,73 @@ static void test_fr(uint8_t *speech, int len) printd("\n"); } +static void test_hr(uint8_t *speech, int len) +{ + uint8_t result[23]; + ubit_t bursts_u[116 * 6]; + sbit_t bursts_s[116 * 6]; + int rc; + + memset(bursts_u, 0x23, sizeof(bursts_u)); + memset(bursts_s, 0, sizeof(bursts_s)); + + printd("Encoding: %s\n", osmo_hexdump(speech, len)); + + /* encode */ + tch_hr_encode(bursts_u, speech, len); + + printd("U-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57), + bursts_u[57], bursts_u[58]); + printd("%s\n", osmo_hexdump(bursts_u + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57), + bursts_u[57 + 116], bursts_u[58 + 116]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57), + bursts_u[57 + 232], bursts_u[58 + 232]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57), + bursts_u[57 + 348], bursts_u[58 + 348]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57), + bursts_u[57 + 464], bursts_u[58 + 464]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57), + bursts_u[57 + 580], bursts_u[58 + 580]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57)); + ubits2sbits(bursts_u, bursts_s, 116 * 6); + printd("S-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57), + (uint8_t)bursts_s[57], (uint8_t)bursts_s[58]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57), + (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57), + (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57), + (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57), + (uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57), + (uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57)); + + /* decode */ + rc = tch_hr_decode(result, bursts_s, 0); + + ASSERT_TRUE(rc == len); + + printd("Decoded: %s\n", osmo_hexdump(result, len)); + + ASSERT_TRUE(!memcmp(speech, result, len)); + + printd("\n"); +} + static void test_pdtch(uint8_t *l2, int len) { uint8_t result[len]; @@ -351,6 +418,7 @@ uint8_t test_macblock[][54] = { uint8_t test_speech_fr[33]; uint8_t test_speech_efr[31]; +uint8_t test_speech_hr[15]; int main(int argc, char **argv) { @@ -381,6 +449,14 @@ int main(int argc, char **argv) for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_fr(test_l2[i], sizeof(test_l2[0])); + for (i = 0; i < sizeof(test_speech_hr); i++) + test_speech_hr[i] = i*17; + test_speech_hr[0] = 0x00; + test_hr(test_speech_hr, sizeof(test_speech_hr)); + + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) + test_hr(test_l2[i], sizeof(test_l2[0])); + for (i = 0; i < sizeof(test_macblock) / sizeof(test_macblock[0]); i++) { test_pdtch(test_macblock[i], 23); test_pdtch(test_macblock[i], 34); -- cgit v1.2.3