aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/osmo-bts-trx/gsm0503_coding.c181
-rw-r--r--src/osmo-bts-trx/gsm0503_coding.h2
-rw-r--r--src/osmo-bts-trx/gsm0503_conv.c74
-rw-r--r--src/osmo-bts-trx/gsm0503_conv.h1
-rw-r--r--src/osmo-bts-trx/gsm0503_interleaving.c46
-rw-r--r--src/osmo-bts-trx/gsm0503_interleaving.h2
-rw-r--r--src/osmo-bts-trx/gsm0503_parity.c2
-rw-r--r--src/osmo-bts-trx/gsm0503_tables.c41
-rw-r--r--src/osmo-bts-trx/gsm0503_tables.h1
-rw-r--r--src/osmo-bts-trx/l1_if.h4
-rw-r--r--src/osmo-bts-trx/scheduler.c224
-rw-r--r--tests/bursts/bursts_test.c76
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 0fb0728..490d0f6 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 97da7ba..6bc7dd0 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 4b95f72..7173896 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 7e0121c..045a095 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 014b4c9..ec47a6b 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 d4c2fdb..4d546f6 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 0c5f77c..ba2e144 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 4c5ab7a..1c272d8 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 db0e920..b068fdd 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 9cc8ee3..0b5d085 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 eb6130e..65da30e 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 935cbc6..608baac 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);