Change in libosmo-abis[master]: WIP: trau_frame: New API

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Thu May 14 11:33:06 UTC 2020


laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-abis/+/18249 )


Change subject: WIP: trau_frame: New API
......................................................................

WIP: trau_frame: New API

Change-Id: I5cf42e6c445d9224be18503cebc7584b3beba08c
---
M include/osmocom/abis/trau_frame.h
M src/trau_frame.c
2 files changed, 1,295 insertions(+), 2 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmo-abis refs/changes/49/18249/1

diff --git a/include/osmocom/abis/trau_frame.h b/include/osmocom/abis/trau_frame.h
index 0f3d1d2..f14b439 100644
--- a/include/osmocom/abis/trau_frame.h
+++ b/include/osmocom/abis/trau_frame.h
@@ -21,6 +21,7 @@
  */
 
 #include <stdint.h>
+#include <stdbool.h>
 #include <osmocom/core/bits.h>
 
 /*! \defgroup trau_frame TRAU frame handling
@@ -29,6 +30,10 @@
  *  \file trau_frame.h
  */
 
+/*********************************************************************************
+ * Old API
+ *********************************************************************************/
+
 /*! \brief Maximum number of C-bits in a TRAU frame:
  * 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */
 #define MAX_C_BITS	25
@@ -41,6 +46,8 @@
 #define MAX_S_BITS	6
 /*! \brief Maximum number of M-bits in a TRAU frame  for E-data */
 #define MAX_M_BITS	2
+/*! \brief Maximum number of XC-bits in a TRAU frame for 8k */
+#define MAX_XC_BITS	6
 
 /*! \brief a decoded TRAU frame, extracted C/D/T/S/M bits */
 struct decoded_trau_frame {
@@ -51,6 +58,7 @@
 	ubit_t m_bits[MAX_M_BITS];
 };
 
+/*! 16k sub-slot types: Bits C1..C5 */
 #define TRAU_FT_FR_UP		0x02	/* 0 0 0 1 0 - 3.5.1.1.1 */
 #define TRAU_FT_HR_UP		0x03	/* 0 0 0 1 1 - TS 08.61 5.1.4.1.1 */
 #define TRAU_FT_FR_DOWN		0x1c	/* 1 1 1 0 0 - 3.5.1.1.1 */
@@ -62,12 +70,19 @@
 #define TRAU_FT_DATA_UP		0x08	/* 0 1 0 0 0 - 3.5.3 */
 #define TRAU_FT_DATA_UP_HR	0x09	/* 0 1 0 0 1 - TS 08.61 5.1.4.2 */
 #define TRAU_FT_DATA_DOWN	0x16	/* 1 0 1 1 0 - 3.5.3 */
-#define TRAU_FT_DATA_DOWN_HR	0x17	/* 0 1 0 0 1 - TS 08.61 5.1.4.2 */
+#define TRAU_FT_DATA_DOWN_HR	0x17	/* 1 0 1 1 1 - TS 08.61 5.1.4.2 */
 #define TRAU_FT_D145_SYNC	0x14	/* 1 0 1 0 0 - 3.5.3 */
 #define TRAU_FT_EDATA		0x1f	/* 1 1 1 1 1 - 3.5.4 */
 #define TRAU_FT_IDLE_UP		0x10	/* 1 0 0 0 0 - 3.5.5 */
 #define TRAU_FT_IDLE_DOWN	0x0e	/* 0 1 1 1 0 - 3.5.5 */
 
+/* 8k sub-slot types: Bits C1..C5*/
+#define TRAU8_FT_SPEECH_UP	0x02	/* 0 0 0 1 P - TS 08.61 5.2.4.1.1 */
+#define TRAU8_FT_DATA_UP	0x06	/* 0 0 1 1 P - TS 08.61 5.2.4.1.1 */
+#define TRAU8_FT_OM_UP		0x0a	/* 0 1 0 1 P - TS 08.61 5.2.4.1.1 */
+#define TRAU8_FT_SPEECH_DOWN	0x00	/* 0 0 0 U P - TS 08.61 5.2.4.1.2 */
+#define TRAU8_FT_DATA_DOWN	0x04	/* 0 0 1 U P - TS 08.61 5.2.4.1.2 */
+#define TRAU8_FT_OM_DOWN	0x08	/* 0 1 0 U P - TS 08.61 5.2.4.1.2 */
 
 int decode_trau_frame(struct decoded_trau_frame *fr, const ubit_t *trau_bits);
 int encode_trau_frame(ubit_t *trau_bits, const struct decoded_trau_frame *fr);
@@ -75,6 +90,74 @@
 
 ubit_t *trau_idle_frame(void);
 
+
+/*********************************************************************************
+ * New API
+ *********************************************************************************/
+
+enum osmo_trau_frame_type {
+	OSMO_TRAU16_FT_NONE,
+	OSMO_TRAU16_FT_FR = 1,
+	OSMO_TRAU16_FT_HR,
+	OSMO_TRAU16_FT_EFR,
+	OSMO_TRAU16_FT_AMR,
+	OSMO_TRAU16_FT_OAM,
+	OSMO_TRAU16_FT_DATA,
+	OSMO_TRAU16_FT_EDATA,
+	OSMO_TRAU16_FT_D145_SYNC,
+	OSMO_TRAU16_FT_DATA_HR,
+	OSMO_TRAU16_FT_IDLE,
+
+	OSMO_TRAU8_SPEECH,
+	OSMO_TRAU8_DATA,
+	OSMO_TRAU8_OAM,
+	OSMO_TRAU8_AMR_LOW,
+	OSMO_TRAU8_AMR_6k7,
+	OSMO_TRAU8_AMR_7k4,
+};
+
+enum osmo_trau_frame_direction {
+	OSMO_TRAU_DIR_UL	= 0,
+	OSMO_TRAU_DIR_DL	= 1,
+};
+
+struct osmo_trau_frame {
+	enum osmo_trau_frame_type type;
+	enum osmo_trau_frame_direction dir;
+
+	ubit_t c_bits[MAX_C_BITS];
+	ubit_t d_bits[MAX_D_BITS];
+	ubit_t t_bits[MAX_T_BITS];
+	ubit_t s_bits[MAX_S_BITS];
+	ubit_t m_bits[MAX_M_BITS];
+	ubit_t ufi;			/*!< Unreliable Frame Indication (HR) */
+	ubit_t crc_bits[3];		/*!< CRC0..CRC2 (HR) */
+	ubit_t xc_bits[MAX_XC_BITS];	/*!< XC1..XC6 (8k) */
+};
+
+/*! decode an 8k TRAU frame
+ *  \param[out] fr caller-allocated output data structure
+ *  \param[in] bits unpacked input bits
+ *  \param[in] dir direction
+ *  \return 0 on success; negative in case of error */
+int osmo_trau_frame_decode_8k(struct osmo_trau_frame *fr, const ubit_t *bits,
+			      enum osmo_trau_frame_direction dir);
+
+/*! decode an 16k TRAU frame
+ *  \param[out] fr caller-allocated output data structure
+ *  \param[in] bits unpacked input bits
+ *  \param[in] dir direction
+ *  \return 0 on success; negative in case of error */
+int osmo_trau_frame_decode_16k(struct osmo_trau_frame *fr, const ubit_t *bits,
+			       enum osmo_trau_frame_direction dir);
+
+/*! encode a TRAU frame
+ *  \param[out] bits caller-allocated memory for unpacked output bits
+ *  \param[out] fr input data structure describing TRAU frame
+ *  \return number of bits encoded */
+int osmo_trau_frame_encode(ubit_t *bits, size_t n_bits, const struct osmo_trau_frame *fr);
+
+
 /* }@ */
 
 #endif /* _TRAU_FRAME_H */
diff --git a/src/trau_frame.c b/src/trau_frame.c
index bc6c5bf..c990c1d 100644
--- a/src/trau_frame.c
+++ b/src/trau_frame.c
@@ -1,4 +1,4 @@
-/* TRAU frame handling according to GSM TS 08.60 */
+/* TRAU frame handling according to GSM TS 08.60 + 08.61 */
 
 /* (C) 2009,2020 by Harald Welte <laforge at gnumonks.org>
  * All Rights Reserved
@@ -37,6 +37,10 @@
  *  \file trau_frame.c
  */
 
+/*********************************************************************************
+ * Old API; introduced in 2009 for original bs11_abis/OsmoNITB
+ *********************************************************************************/
+
 static uint32_t get_bits(const ubit_t *bitbuf, int offset, int num)
 {
 	int i;
@@ -480,4 +484,1210 @@
 	return encoded_idle_frame;
 }
 
+/*********************************************************************************
+ * New API; introduced in 2020 for use by osmo-mgw
+ *********************************************************************************/
+
+/* 16k sub-slots */
+static const ubit_t ft_fr_up_bits[5] =		{ 0, 0, 0, 1, 0 };
+static const ubit_t ft_data_up_bits[5] =	{ 0, 1, 0, 0, 0 };
+static const ubit_t ft_idle_up_bits[5] =	{ 1, 0, 0, 0, 0 };
+static const ubit_t ft_efr_bits[5] =		{ 1, 1, 0, 1, 0 };
+static const ubit_t ft_amr_bits[5] =		{ 0, 0, 1, 1, 0 };
+static const ubit_t ft_oam_up_bits[5] = 	{ 0, 0, 1, 0, 1 };
+static const ubit_t ft_oam_down_bits[5] =	{ 1, 1, 0, 1, 1 };
+static const ubit_t ft_d145s_bits[5] =		{ 1, 0, 1, 0, 0 };
+static const ubit_t ft_edata_bits[5] =		{ 1, 1, 1, 1, 1 };
+
+/* 8k sub-slots */
+static const ubit_t ft_hr_up_bits[5] =		{ 0, 0, 0, 1, 1 };
+static const ubit_t ft_hr_down_bits[5] =	{ 1, 1, 1, 0, 1 };
+static const ubit_t ft_data_hr_up_bits[5] =	{ 0, 1, 0, 0, 1 };
+static const ubit_t ft_data_hr_down_bits[5] =	{ 1, 0, 1, 1, 1 };
+
+
+/* generate the sync pattern described in TS 08.60 4.8.1 */
+static void encode_sync16(ubit_t *trau_bits)
+{
+	int i;
+
+	/* 16 '0' bits in  header */
+	memset(trau_bits, 0, 16);
+	/* '1'-bit in regular intervals */
+	for (i = 16; i < 40*8; i += 16)
+		trau_bits[i] = 1;
+}
+
+/* TS 08.60 Section 3.1.1 */
+static int encode16_fr(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	const ubit_t *cbits5;
+	int i;
+	int d_idx = 0;
+
+	switch (fr->type) {
+	case OSMO_TRAU16_FT_IDLE:
+		if (fr->dir == OSMO_TRAU_DIR_UL)
+			cbits5 = ft_idle_up_bits;
+		else
+			cbits5 = ft_idle_down_bits;
+		break;
+	case OSMO_TRAU16_FT_FR:
+		if (fr->dir == OSMO_TRAU_DIR_UL)
+			cbits5 = ft_fr_up_bits;
+		else
+			cbits5 = ft_fr_down_bits;
+		break;
+	case OSMO_TRAU16_FT_EFR:
+		cbits5 = ft_efr_bits;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	encode_sync16(trau_bits);
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5 + 0, 5);
+	/* C6 .. C15 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5);
+	/* D1 .. D255 */
+	for (i = 32; i < 304; i += 16) {
+		trau_bits[i] = 1;
+		memcpy(trau_bits + i + 1, fr->d_bits + d_idx, 15);
+		d_idx += 15;
+	}
+	/* D256 .. D260 */
+	trau_bits[304] = 1;
+	memcpy(trau_bits + 305, fr->d_bits + d_idx, 5);
+	/* C16 .. C21 */
+	memcpy(trau_bits + 310, fr->c_bits + 15, 6);
+
+	/* FIXME: handle timing adjustment */
+
+	/* T1 .. T4 */
+	memcpy(trau_bits + 316, fr->t_bits+0, 4);
+
+	return 40 * 8;
+}
+
+/* TS 08.60 Section 3.1.1 */
+static int decode16_fr(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			enum osmo_trau_frame_direction dir)
+{
+	int i;
+	int d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits + 0, trau_bits + 17, 15);
+	/* C16 .. C21 */
+	memcpy(fr->c_bits + 15, trau_bits + 310, 6);
+	/* T1 .. T4 */
+	memcpy(fr->t_bits + 0, trau_bits + 316, 4);
+	/* D1 .. D255 */
+	for (i = 32; i < 304; i+= 16) {
+		memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15);
+		d_idx += 15;
+	}
+	/* D256 .. D260 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 305, 5);
+
+	return 0;
+}
+
+/* TS 08.60 Section 3.1.2 */
+static int encode16_amr(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	const ubit_t *cbits5 = ft_amr_bits;
+	int i, d_idx;
+
+	encode_sync16(trau_bits);
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5 + 0, 5);
+	/* C6 .. C15 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5);
+
+	trau_bits[32] = 1;
+	/* C16 .. C25 */
+	memcpy(trau_bits + 33, fr->c_bits + 15, 10);
+	/* D1 .. D5 */
+	memcpy(trau_bits + 43, fr->d_bits + 0, 5);
+
+	/* D6 .. D256 */
+	for (i = 48, d_idx = 5; i <= 315; i += 16, d_idx += 15) {
+		trau_bits[i] = 1;
+		memcpy(trau_bits + i + 1, fr->d_bits + d_idx, 15);
+	}
+
+	/* FIXME: handle timing adjustment */
+
+	/* T1 .. T4 */
+	memcpy(trau_bits + 316, fr->t_bits + 0, 4);
+
+	return 40 * 8;
+}
+
+/* TS 08.60 Section 3.1.2 */
+static int decode16_amr(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			enum osmo_trau_frame_direction dir)
+{
+	int i;
+	int d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits + 0, trau_bits + 17, 15);
+	/* C16 .. C25 */
+	memcpy(fr->c_bits + 15, trau_bits + 33, 10);
+	/* T1 .. T4 */
+	memcpy(fr->t_bits + 0, trau_bits + 316, 4);
+	/* D1 .. D5 */
+	memcpy(fr->d_bits, trau_bits + 43, 5);
+	d_idx += 5;
+	/* D6 .. D245 */
+	for (i = 48; i < 304; i += 16) {
+		memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15);
+		d_idx += 15;
+	}
+	/* D246 .. D256 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 305, 11);
+
+	return 0;
+}
+
+/* TS 08.60 Section 3.2 */
+static int encode16_oam(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	const ubit_t *cbits5;
+	int i, d_idx;
+
+	if (fr->dir == OSMO_TRAU_DIR_UL)
+		cbits5 = ft_oam_up_bits;
+	else
+		cbits5 = ft_oam_down_bits;
+
+	encode_sync16(trau_bits);
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5, 5);
+	/* C6 .. C15 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits, 15 - 5);
+
+	/* D1 .. D255 */
+	for (i = 32, d_idx = 0; i < 304; i += 16, d_idx += 15) {
+		trau_bits[i] = 1;
+		memcpy(trau_bits + i + 1, fr->d_bits + d_idx, 15);
+	}
+	/* D256 .. D264 */
+	memcpy(trau_bits + 305, fr->d_bits + 256, 9);
+
+	/* S1 .. S6 */
+	memcpy(trau_bits + 314, fr->s_bits, 6);
+
+	return 40 * 8;
+}
+
+/* TS 08.60 Section 3.2 */
+static int decode16_oam(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits + 0, trau_bits + 17, 15);
+
+	/* D1 .. D255 */
+	for (i = 33, d_idx = 0; i < 312; i+= 16, d_idx += 15)
+		memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15);
+	/* D256 .. D264 */
+	memcpy(fr->d_bits+d_idx, trau_bits + 305, 9);
+
+	/* S1 .. S6 */
+	memcpy(fr->s_bits, trau_bits + 314, 6);
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.1.1.1 */
+static int encode16_hr(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int d_idx = 0;
+	const ubit_t *cbits5;
+
+	if (fr->dir == OSMO_TRAU_DIR_UL)
+		cbits5 = ft_hr_up_bits;
+	else
+		cbits5 = ft_hr_down_bits;
+
+	encode_sync16(trau_bits);
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5, 5);
+	/* C6 .. C15 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 10);
+
+	/*  UFI */
+	trau_bits[33] = fr->ufi;
+
+	/* D1 .. D14 */
+	memcpy(trau_bits + 4 * 8 + 2, fr->d_bits+d_idx, 14); d_idx += 14;
+	/* D15 .. D29 */
+	memcpy(trau_bits + 6 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15;
+	/* D30 .. D44 */
+	memcpy(trau_bits + 8 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15;
+	/* CRC */
+	memcpy(trau_bits + 10 * 8 + 1, fr->crc_bits, 3);
+	/* D45 .. D56 */
+	memcpy(trau_bits + 10 * 8 + 4, fr->d_bits+d_idx, 12); d_idx += 12;
+	/* D57 .. D71 */
+	memcpy(trau_bits + 12 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15;
+	/* D72 .. D86 */
+	memcpy(trau_bits + 14 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15;
+	/* D87 .. D101 */
+	memcpy(trau_bits + 16 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15;
+	/* D102 .. D112 */
+	memcpy(trau_bits + 18 * 8 + 1, fr->d_bits+d_idx, 11); d_idx += 11;
+
+	memset(trau_bits + 19 * 8 + 4, 0x01, 4 + 18 * 8 + 6);
+	/* C16 .. C21 */
+	memcpy(trau_bits + 38 * 8 + 6, fr->c_bits + 15, 6);
+
+	/* FIXME: handle timing adjustment */
+	/* T1 .. T4 */
+	memcpy(trau_bits + 39 * 8 + 4, fr->t_bits, 4);
+
+	return 40 * 8;
+}
+
+/* TS 08.61 Section 5.1.1.1 */
+static int decode16_hr(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			enum osmo_trau_frame_direction dir)
+{
+	int d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits + 0, trau_bits + 17, 15);
+	/* C16 .. C21 */
+	memcpy(fr->c_bits + 15, trau_bits + 38 * 8 + 6, 6);
+	/* T1 .. T4 */
+	memcpy(fr->t_bits + 0, trau_bits + 39 * 8 + 4, 4);
+
+	/* UFI */
+	fr->ufi = trau_bits[33];
+	/* D1 .. D14 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 4 * 8 + 2, 14); d_idx += 14;
+	/* D15 .. D29 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 6 * 8 + 1, 15); d_idx += 15;
+	/* D30 .. D44 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 8 * 8 + 1, 15); d_idx += 15;
+	/* CRC0..2 */
+	memcpy(fr->crc_bits, trau_bits + 10 * 8 + 1, 3);
+	/* D45 .. D56 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 10 * 8 + 4, 12); d_idx += 12;
+	/* D57 .. D71 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 12 * 8 + 1, 15); d_idx += 15;
+	/* D72 .. D86 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 14 * 8 + 1, 15); d_idx += 15;
+	/* D87 .. D101 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 16 * 8 + 1, 15); d_idx += 15;
+	/* D102 .. D112 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 18 * 8 + 1, 11); d_idx += 11;
+
+	return 0;
+}
+
+/* TS 08.60 Section 3.4 */
+static int encode16_idle(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	memcpy(trau_bits, trau_idle_frame(), 40*8);
+	return 40 * 8;
+}
+
+/* TS 08.60 Section 3.3.1 */
+static int decode16_data(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			 enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits + 0, trau_bits + 17, 15);
+
+	/* D1 .. D63 */
+	for (i = 0; i < 9; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + (4 + i) * 8 + 1, 7);
+		d_idx += 7;
+	}
+	/* D64 .. D126 */
+	for (i = 0; i < 9; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + (13 + i) * 8 + 1, 7);
+		d_idx += 7;
+	}
+
+	/* D127 .. D189 */
+	for (i = 0; i < 9; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + (22 + i) * 8 + 1, 7);
+		d_idx += 7;
+	}
+
+	/* D190 .. D252 */
+	for (i = 0; i < 9; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + (31 + i) * 8 + 1, 7);
+		d_idx += 7;
+	}
+
+	return 0;
+}
+
+/* TS 08.60 Section 3.3.1 */
+static int encode16_data(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	const ubit_t *cbits5;
+	int i, d_idx = 0;
+
+	if (fr->dir == OSMO_TRAU_DIR_UL)
+		cbits5 = ft_data_up_bits;
+	else
+		cbits5 = ft_data_down_bits;
+
+	encode_sync16(trau_bits);
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5, 5);
+	/* C6 .. C15 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5);
+
+	/* D1 .. D63 */
+	for (i = 0; i < 9; i++) {
+		unsigned int offset = (4 + i) * 8;
+		trau_bits[offset] = 1;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	/* D64 .. D126 */
+	for (i = 0; i < 9; i++) {
+		unsigned int offset = (13 + i) * 8;
+		trau_bits[offset] = 1;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	/* D127 .. D189 */
+	for (i = 0; i < 9; i++) {
+		unsigned int offset = (22 + i) * 8;
+		trau_bits[offset] = 1;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	/* D190 .. D252 */
+	for (i = 0; i < 9; i++) {
+		unsigned int offset = (31 + i) * 8;
+		trau_bits[offset] = 1;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	return 40 * 8;
+}
+
+/* TS 08.60 3.3.2 */
+static int decode16_edata(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			  enum osmo_trau_frame_direction dir)
+{
+	/* C1 .. C13 */
+	memcpy(fr->c_bits, trau_bits + 17, 13);
+
+	/* M1 .. M2 */
+	memcpy(fr->m_bits, trau_bits + 30, 2);
+
+	/* D1 .. D288 */
+	memcpy(fr->d_bits, trau_bits + 4 * 8, 288);
+
+	return 0;
+}
+
+/* TS 08.60 Section 3.3.2 */
+static int encode16_edata(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	const ubit_t *cbits5;
+
+	if (fr->type == OSMO_TRAU16_FT_D145_SYNC)
+		cbits5 = ft_d145s_bits;
+	else
+		cbits5 = ft_edata_bits;
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5, 5);
+
+	/* C6 .. C13 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 8);
+
+	/* M1 .. M2 */
+	memcpy(trau_bits + 30, fr->m_bits, 2);
+
+	/* D1 .. D288 */
+	memcpy(trau_bits + 4 * 8, fr->d_bits, 288);
+
+	return 40 * 8;
+}
+
+/* TS 08.61 Section 5.1.2 */
+static int decode16_data_hr(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			  enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits+0, trau_bits + 17, 15);
+
+	/* D1 .. D63 */
+	for (i = 0; i < 9; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + (4 + i) * 8 + 1, 7);
+		d_idx += 7;
+	}
+
+	/* D'1 .. D'63 (mapped to D64..D127) */
+	for (i = 0; i < 9; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + (22 + i) * 8 + 1, 7);
+		d_idx += 7;
+	}
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.1.2 */
+static int encode16_data_hr(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	const ubit_t *cbits5;
+	int i, d_idx = 0;
+
+	if (fr->dir == OSMO_TRAU_DIR_UL)
+		cbits5 = ft_data_hr_up_bits;
+	else
+		cbits5 = ft_data_hr_down_bits;
+
+	encode_sync16(trau_bits);
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 17, cbits5, 5);
+	/* C6 .. C15 */
+	memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5);
+
+	/* D1 .. D63 */
+	for (i = 4; i < 4 + 9; i++) {
+		unsigned int offset = i * 8;
+		trau_bits[offset] = 1;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	memset(trau_bits + 13*8, 1, 9*8);
+
+	/* D'1 .. D'63 (mapped to D64..D127) */
+	for (i = 22; i < 22 + 9; i++) {
+		unsigned int offset = i * 8;
+		trau_bits[offset] = 1;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	memset(trau_bits + 31*8, 1, 9*8);
+
+	return 40 * 8;
+}
+
+/* TS 08.61 Section 5.2.1.1 */
+static int decode8_hr(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+		      enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* C1 .. C5 */
+	memcpy(fr->c_bits, trau_bits + 9, 5);
+	/* XC1 .. XC2 */
+	memcpy(fr->xc_bits, trau_bits + 8 + 6, 2);
+	/* XC3 .. XC6 */
+	memcpy(fr->xc_bits + 2, trau_bits + 2 * 8 + 2, 4);
+	/* D1 .. D2 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 6, 2);
+	d_idx += 2;
+
+	/* D1 .. D44 */
+	for (i = 3; i < 3 + 6; i++) {
+		int offset = i * 8;
+		memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 7);
+		d_idx += 7;
+	}
+
+	/* CRC0 .. CRC2 */
+	fr->crc_bits[2] = trau_bits[82];
+	fr->crc_bits[1] = trau_bits[83];
+	fr->crc_bits[0] = trau_bits[84];
+
+	/* D45 .. D48 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 85, 4);
+
+	/* D49 .. D111 */
+	for (i = 10; i < 10 + 10; i++) {
+		int offset = i * 8;
+		memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 7);
+		d_idx += 7;
+	}
+	/* D112 */
+	fr->d_bits[d_idx++] = trau_bits[19 * 8 + 1];
+
+	/* C6 .. C9 */
+	memcpy(fr->c_bits + 5, trau_bits + 19 * 8 + 2, 4);
+
+	/* FIXME: handle timing adjustment */
+	/* T1 .. T2 */
+	fr->t_bits[0] = trau_bits[19 * 8 + 6];
+	fr->t_bits[1] = trau_bits[19 * 8 + 7];
+
+	return 0;
+}
+
+/* compute the odd parity bit of the given input bit sequence */
+static ubit_t compute_odd_parity(const ubit_t *in, unsigned int num_bits)
+{
+	int i;
+	unsigned int sum = 0;
+
+	for (i = 0; i < num_bits; i++) {
+		if (in[i])
+			sum++;
+	}
+
+	if (sum % 1)
+		return 0;
+	else
+		return 1;
+}
+
+/* TS 08.61 Section 5.2.1.1 */
+static int encode8_hr(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int i, d_idx = 0;
+
+	/* sync pattern */
+	memset(trau_bits, 0, 8);
+	trau_bits[8] = 1;
+	trau_bits[16] = 0;
+	trau_bits[17] = 1;
+	for (i = 3; i < 20; i++)
+		trau_bits[i * 8] = 1;
+
+	/* C1 .. C5 */
+	ubit_t *cbits_out = trau_bits + 1 * 8 + 1;
+	if (fr->dir == OSMO_TRAU_DIR_UL) {
+		cbits_out[0] = 0;
+		cbits_out[1] = 0;
+		cbits_out[2] = 0;
+		cbits_out[3] = 1;
+		cbits_out[4] = 1;
+	} else {
+		cbits_out[0] = 0;
+		cbits_out[1] = 0;
+		cbits_out[2] = 0;
+		cbits_out[3] = fr->c_bits[3];
+		cbits_out[4] = compute_odd_parity(cbits_out, 4);
+	}
+
+	/* XC1 .. XC2 */
+	memcpy(trau_bits + 1 * 8 + 6, fr->xc_bits, 2);
+	/* XC3 .. XC6 */
+	memcpy(trau_bits + 2 * 8 + 2, fr->xc_bits, 4);
+	/* D1 ..  D2 */
+	memcpy(trau_bits + 2 * 8 + 6, fr->d_bits, 2);
+	d_idx += 2;
+
+	/* D1 .. D44 */
+	for (i = 3; i < 3 + 6; i++) {
+		int offset = i * 8;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	};
+
+	/* CRC0 .. CRC2 */
+	trau_bits[82] = fr->crc_bits[2];
+	trau_bits[83] = fr->crc_bits[1];
+	trau_bits[84] = fr->crc_bits[0];
+
+	/* D49 .. D111 */
+	for (i = 10; i < 10 + 10; i++) {
+		int offset = i * 8;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+	/* D112 */
+	trau_bits[19 * 8 + 1] = fr->d_bits[d_idx++];
+
+	/* C6 .. C9 */
+	memcpy(trau_bits + 19 * 8 + 2, fr->c_bits + 5, 4);
+
+	/* FIXME: handle timing adjustment */
+	/* T1 .. T2 */
+	trau_bits[19 * 8 + 6] = fr->t_bits[0];
+	trau_bits[19 * 8 + 7] = fr->t_bits[1];
+
+	return 20 * 8;
+}
+
+/* TS 08.61 Section 5.2.1.2.1 */
+static int decode8_amr_low(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			   enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* D1 .. D7 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 1, 7);
+	d_idx += 7;
+	/* C1 .. C5 */
+	memcpy(fr->c_bits, trau_bits + 2 * 8 + 1, 5);
+	/* D8 .. D9 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 6, 2);
+	d_idx += 2;
+	/* D10 .. D15 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 3 * 8 + 2, 6);
+	d_idx += 6;
+	/* D16 .. D120 */
+	for (i = 4; i < 19; i++) {
+		int offset = i * 8;
+		memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 7);
+		d_idx += 7;
+	}
+	/* D121 .. D126 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 6);
+	d_idx += 6;
+
+	/* FIXME: handle timing adjustment */
+	/* T1 */
+	fr->t_bits[0] = trau_bits[19 * 8 + 7];
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.2.1.2.1 */
+static int encode8_amr_low(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int i, d_idx = 0;
+
+	/* sync pattern */
+	memset(trau_bits, 0, 8);
+	trau_bits[8] = 1;
+	trau_bits[16] = 1;
+	trau_bits[24] = 0;
+	trau_bits[25] = 1;
+	for (i = 4; i < 20; i++)
+		trau_bits[i * 8] = 1;
+
+	/* D1 .. D7 */
+	memcpy(trau_bits + 1 * 8 + 1, fr->d_bits + d_idx, 7);
+	d_idx += 7;
+	/* C1 .. C5 */
+	memcpy(trau_bits + 2 * 8 + 1, fr->c_bits, 5);
+	/* D8 .. D9 */
+	memcpy(trau_bits + 2 * 8 + 6, fr->d_bits + d_idx, 2);
+	d_idx += 2;
+	/* D10 .. D15 */
+	memcpy(trau_bits + 3 * 8 + 2, fr->d_bits + d_idx, 6);
+	d_idx += 6;
+	/* D16 .. D120 */
+	for (i = 4; i < 19; i++) {
+		int offset = i * 8;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+	/* D121 .. D126 */
+	memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 6);
+	d_idx += 6;
+
+	/* T1 */
+	trau_bits[19 * 8 + 7] = fr->t_bits[0];
+
+	return 20 * 8;
+}
+
+/* TS 08.61 Section 5.2.1.2.2 */
+static int decode8_amr_67(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			  enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* D1 .. D7 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 1, 7);
+	d_idx += 7;
+	/* C1 .. C3 */
+	memcpy(fr->c_bits, trau_bits + 2 * 8 + 1, 3);
+	/* D8 .. D11 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 4, 4);
+	d_idx += 4;
+	/* D12 .. D39 */
+	for (i = 3; i < 7; i++) {
+		int offset = i * 8;
+		memcpy(fr->d_bits + d_idx, trau_bits + offset + 2, 7);
+		d_idx += 7;
+	}
+
+	/* D40 .. D137 */
+	for (i = 7; i < 20; i+= 2) {
+		int offset = i * 8;
+		memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 15);
+		d_idx += 15;
+	}
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.1.2.2 */
+static int encode8_amr_67(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int i, d_idx = 0;
+
+	/* sync pattern */
+	memset(trau_bits, 0, 8);
+	trau_bits[1 * 8] = 1;
+	trau_bits[2 * 8] = 1;
+	trau_bits[3 * 8] = 1;
+	trau_bits[4 * 8] = 1;
+	trau_bits[5 * 8] = 0;
+	for (i = 5; i < 20; i += 2)
+		trau_bits[i * 8] = 1;
+
+	/* D1 .. D7 */
+	memcpy(trau_bits + 1 * 8 + 1, fr->d_bits + d_idx, 7);
+	d_idx += 7;
+	/* C1 .. C3 */
+	memcpy(trau_bits + 2 * 8 + 1, fr->c_bits, 3);
+	/* D8 .. D11 */
+	memcpy(trau_bits + 2 * 8 + 4, fr->d_bits + d_idx, 4);
+	d_idx += 4;
+	/* D12 .. D39 */
+	for (i = 3; i < 7; i++) {
+		int offset = i * 8;
+		memcpy(trau_bits + offset + 2, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+
+	/* D40 .. D137 */
+	for (i = 7; i < 20; i+= 2) {
+		int offset = i * 8;
+		memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 15);
+		d_idx += 15;
+	}
+
+	return 20 * 8;
+}
+
+/* TS 08.61 Section 5.1.2.3 */
+static int decode8_amr_74(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			  enum osmo_trau_frame_direction dir)
+{
+	int d_idx = 0;
+
+	/* D1 .. D5 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 3, 5);
+	d_idx += 5;
+	/* D6 .. D12 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 1, 7);
+	d_idx += 7;
+	/* C1.. C3 */
+	memcpy(fr->c_bits, trau_bits + 2 * 8 + 1, 3);
+	/* D13 .. D16 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 4, 4);
+	d_idx += 4;
+	/* D17 .. D23 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 3 * 8 + 1, 7);
+	d_idx += 7;
+	/* D24 .. D151 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 4 * 8, 16 * 8);
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.1.2.3 */
+static int encode8_amr_74(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int d_idx = 0;
+
+	/* sync pattern */
+	trau_bits[0] = 0;
+	trau_bits[1] = 0;
+	trau_bits[2] = 1;
+	trau_bits[1*8] = 0;
+	trau_bits[2*8] = 1;
+	trau_bits[3*8] = 0;
+
+	/* D1 .. D5 */
+	memcpy(trau_bits + 3, fr->d_bits + d_idx, 5);
+	d_idx += 5;
+	/* D6 .. D12 */
+	memcpy(trau_bits + 1 * 8 + 1, fr->d_bits + d_idx, 7);
+	d_idx += 7;
+	/* C1.. C3 */
+	memcpy(trau_bits + 2 * 8 + 1, fr->c_bits, 3);
+	/* D13 .. D16 */
+	memcpy(trau_bits + 2 * 8 + 4, fr->d_bits + d_idx, 4);
+	d_idx += 4;
+	/* D17 .. D23 */
+	memcpy(trau_bits + 3 * 8 + 1, fr->d_bits + d_idx, 7);
+	d_idx += 7;
+	/* D24 .. D151 */
+	memcpy(trau_bits + 4 * 8, fr->d_bits + d_idx, 16 * 8);
+
+	return 20 * 8;
+}
+
+/* TS 08.61 Section 5.2.2 */
+static int decode8_data(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+			enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* C1 .. C5 */
+	memcpy(fr->c_bits, trau_bits + 1 * 8 + 1, 5);
+	/* D1 .. D2 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 6, 2);
+	d_idx += 2;
+	/* D3 .. D8 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 2, 6);
+	/* D9 .. D57 + D'1 .. D'57 */
+	for (i = 3; i < 20; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + i * 8 + 1, 7);
+		d_idx += 7;
+	}
+	/* D'58 .. D'62 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 6);
+	d_idx += 6;
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.2.2 */
+static int encode8_data(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int i, d_idx = 0;
+
+	/* sync pattern */
+	memset(trau_bits, 0, 8);
+	trau_bits[1 * 8] = 1;
+	trau_bits[2 * 8] = 0;
+	trau_bits[2 * 8 + 1] = 1;
+	for (i = 3; i < 19; i++)
+		trau_bits[i * 8] = 1;
+	trau_bits[19 * 8 + 7] = 1;
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 1 * 8 + 1, fr->c_bits, 5);
+	/* D1 .. D2 */
+	memcpy(trau_bits + 1 * 8 + 6, fr->d_bits + d_idx, 2);
+	d_idx += 2;
+	/* D3 .. D8 */
+	memcpy(trau_bits + 2 * 8 + 2, fr->d_bits + d_idx, 6);
+	/* D9 .. D57 + D'1 .. D'57 */
+	for (i = 3; i < 20; i++) {
+		memcpy(trau_bits + i * 8 + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+	/* D'58 .. D'62 */
+	memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 6);
+	d_idx += 6;
+
+	return 20 * 8;
+}
+
+/* TS 08.61 Section 5.2.3 */
+static int decode8_oam(struct osmo_trau_frame *fr, const ubit_t *trau_bits,
+		       enum osmo_trau_frame_direction dir)
+{
+	int i, d_idx = 0;
+
+	/* C1 .. C5 */
+	memcpy(fr->c_bits, trau_bits + 1 * 8 + 1, 5);
+	/* XC1 .. XC2 */
+	memcpy(fr->xc_bits, trau_bits + 1 * 8 + 6, 2);
+	/* XC3 .. XC6 */
+	memcpy(fr->xc_bits + 2, trau_bits + 2 * 8 + 2, 4);
+	/* D1 .. D2 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 6, 2);
+	d_idx += 2;
+	/* D3 .. D114 */
+	for (i = 3; i < 19; i++) {
+		memcpy(fr->d_bits + d_idx, trau_bits + i * 8 + 1, 7);
+		d_idx += 7;
+	}
+	/* D115 .. D120 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 6);
+	d_idx += 7;
+
+	return 0;
+}
+
+/* TS 08.61 Section 5.2.3 */
+static int encode8_oam(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
+{
+	int i, d_idx = 0;
+
+	/* sync pattern */
+	memset(trau_bits, 0, 8);
+	trau_bits[1 * 8 + 0] = 1;
+	trau_bits[2 * 8 + 0] = 0;
+	trau_bits[2 * 8 + 1] = 1;
+	for (i = 3; i < 20; i++)
+		trau_bits[i * 8] = 1;
+
+	/* C1 .. C5 */
+	memcpy(trau_bits + 1 * 8 + 1, fr->c_bits, 5);
+	/* XC1 .. XC2 */
+	memcpy(trau_bits + 1 * 8 + 6, fr->xc_bits, 2);
+	/* XC3 .. XC6 */
+	memcpy(trau_bits + 2 * 8 + 2, fr->xc_bits + 2, 4);
+	/* D1 .. D2 */
+	memcpy(trau_bits + 2 * 8 + 6, fr->d_bits + d_idx, 2);
+	d_idx += 2;
+	/* D3 .. D114 */
+	for (i = 3; i < 19; i++) {
+		memcpy(trau_bits + i * 8 + 1, fr->d_bits + d_idx, 7);
+		d_idx += 7;
+	}
+	/* D115 .. D120 */
+	memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 6);
+	d_idx += 7;
+
+	return 20 * 8;
+}
+
+
+/*! Encode a TRAU frame from its decoded representation to a sequence of unpacked bits.
+ *  \param[out] bits caller-allocated buffer for unpacked outpud bits
+ *  \param[in] n_bits size of 'bits' oputput buffer in number of unpacked bits
+ *  \param[in] fr decoded representation of TRAU frame to be encoded
+ *  \return 0 number of unpacked output bits generated; negative in case of error */
+int osmo_trau_frame_encode(ubit_t *bits, size_t n_bits, const struct osmo_trau_frame *fr)
+{
+	switch (fr->type) {
+	case OSMO_TRAU16_FT_FR:
+	case OSMO_TRAU16_FT_EFR:
+		return encode16_fr(bits, fr);
+	case OSMO_TRAU16_FT_HR:
+		return encode16_hr(bits, fr);
+	case OSMO_TRAU16_FT_AMR:
+		return encode16_amr(bits, fr);
+	case OSMO_TRAU16_FT_OAM:
+		return encode16_oam(bits, fr);
+	case OSMO_TRAU16_FT_IDLE:
+		return encode16_idle(bits, fr);
+	case OSMO_TRAU16_FT_DATA_HR:
+		return encode16_data_hr(bits, fr);
+	case OSMO_TRAU16_FT_DATA:
+		return encode16_data(bits, fr);
+	case OSMO_TRAU16_FT_D145_SYNC:
+	case OSMO_TRAU16_FT_EDATA:
+		return encode16_edata(bits, fr);
+	case OSMO_TRAU8_SPEECH:
+		return encode8_hr(bits, fr);
+	case OSMO_TRAU8_DATA:
+		return encode8_data(bits, fr);
+	case OSMO_TRAU8_OAM:
+		return encode8_oam(bits, fr);
+	case OSMO_TRAU8_AMR_LOW:
+		return encode8_amr_low(bits, fr);
+	case OSMO_TRAU8_AMR_6k7:
+		return encode8_amr_67(bits, fr);
+	case OSMO_TRAU8_AMR_7k4:
+		return encode8_amr_74(bits, fr);
+	case OSMO_TRAU16_FT_NONE:
+	default:
+		return -EINVAL;
+	}
+}
+
+/*! Decode/parse a 16k TRAU frame from unpacked bits to decoded format.
+ *  \param[out] fr caller-allocated output data structure
+ *  \param[in] bits unpacked bits containing raw TRAU frame; must be aligned
+ *  \param[in] dir direction (uplink/downlink)
+ *  \returns 0 in case of success; negative on error */
+int osmo_trau_frame_decode_16k(struct osmo_trau_frame *fr, const ubit_t *bits,
+			       enum osmo_trau_frame_direction dir)
+{
+	uint8_t cbits5 = get_bits(bits, 17, 5);
+
+	fr->type = OSMO_TRAU16_FT_NONE;
+	fr->dir = dir;
+
+	switch (cbits5) {
+	case TRAU_FT_FR_UP:
+	case TRAU_FT_FR_DOWN:
+		fr->type = OSMO_TRAU16_FT_FR;
+		return decode16_fr(fr, bits, dir);
+	case TRAU_FT_IDLE_UP:
+	case TRAU_FT_IDLE_DOWN:
+		fr->type = OSMO_TRAU16_FT_IDLE;
+		return decode16_fr(fr, bits, dir);
+	case TRAU_FT_EFR:
+		fr->type = OSMO_TRAU16_FT_EFR;
+		return decode16_fr(fr, bits, dir);
+	case TRAU_FT_AMR:
+		fr->type = OSMO_TRAU16_FT_AMR;
+		return decode16_amr(fr, bits, dir);
+	case TRAU_FT_OM_UP:
+	case TRAU_FT_OM_DOWN:
+		fr->type = OSMO_TRAU16_FT_OAM;
+		return decode16_oam(fr, bits, dir);
+	case TRAU_FT_DATA_UP:
+	case TRAU_FT_DATA_DOWN:
+		fr->type = OSMO_TRAU16_FT_DATA;
+		return decode16_data(fr, bits, dir);
+	case TRAU_FT_HR_UP:
+	case TRAU_FT_HR_DOWN:
+		fr->type = OSMO_TRAU16_FT_HR;
+		return decode16_hr(fr, bits, dir);
+	case TRAU_FT_DATA_UP_HR:
+	case TRAU_FT_DATA_DOWN_HR:
+		fr->type = OSMO_TRAU16_FT_DATA_HR;
+		return decode16_data_hr(fr, bits, dir);
+	case TRAU_FT_EDATA:
+		fr->type = OSMO_TRAU16_FT_EDATA;
+		return decode16_edata(fr, bits, dir);
+	case TRAU_FT_D145_SYNC:
+		fr->type = OSMO_TRAU16_FT_D145_SYNC;
+		return decode16_edata(fr, bits, dir);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+#define TRAU8_FT_AMR_NO_SPEECH_CMI	0x10	/* 1, 0, 0, 0, 0 */
+#define TRAU8_FT_AMR_NO_SPEECH_CMR	0x14	/* 1, 0, 1, 0, 0 */
+#define TRAU8_FT_AMR_475_515_590	0..7
+
+const uint8_t bit8_0[16] = { 0,  };
+
+/*!< check sync pattern for hr/data/oam */
+static bool is_hr(const ubit_t *bits)
+{
+	int i;
+
+	/* TS 08.61 Section 6.8.2.1.1 */
+	if (memcmp(bits, bit8_0, sizeof(bit8_0)))
+		return false;
+	if (bits[8] != 1)
+		return false;
+	if (bits[16] != 0 || bits[17] != 1)
+		return false;
+	for (i = 24; i < 20 * 8; i += 16) {
+		if (bits[i] != 1)
+			return false;
+	}
+	return true;
+}
+
+
+/*!< check sync pattern for AMR No_Speech + low bit rate */
+static bool is_amr_low(const ubit_t *bits)
+{
+	int i;
+
+	/* TS 08.61 Section 6.8.2.1.2 */
+	if (memcmp(bits, bit8_0, sizeof(bit8_0)))
+		return false;
+	if (bits[8] != 1)
+		return false;
+	if (bits[16] != 1)
+		return false;
+	if (bits[24] != 0 || bits[25] != 1)
+		return false;
+	for (i = 32; i < 20 * 8; i += 16) {
+		if (bits[i] != 1)
+			return false;
+	}
+	return true;
+}
+
+/*!< check sync pattern for AMR 6.7kBit/s */
+static bool is_amr_67(const ubit_t *bits)
+{
+	int i;
+
+	/* TS 08.61 Section 6.8.2.1.3 */
+	if (memcmp(bits, bit8_0, sizeof(bit8_0)))
+		return false;
+	if (bits[8] != 1)
+		return false;
+	if (bits[16] != 1)
+		return false;
+	if (bits[24] != 1)
+		return false;
+	if (bits[32] != 1)
+		return false;
+	if (bits[40] != 0)
+		return false;
+	for (i = 48; i < 20 * 8; i+= 16)
+		if (bits[i] != 1)
+			return false;
+
+	return true;
+}
+
+/*!< check sync pattern for AMR 7.4kBit/s */
+static bool is_amr_74(const ubit_t *bits)
+{
+	if (bits[0] != 0 || bits[1] != 0 || bits[2] != 0)
+		return false;
+	if (bits[8] != 0)
+		return false;
+	if (bits[16] != 1)
+		return false;
+	if (bits[24] != 0)
+		return false;
+
+	return true;
+}
+
+/*! Decode/parse a 8k TRAU frame from unpacked bits to decoded format.
+ *  \param[out] fr caller-allocated output data structure
+ *  \param[in] bits unpacked bits containing raw TRAU frame; must be aligned
+ *  \param[in] dir direction (uplink/downlink)
+ *  \returns 0 in case of success; negative on error */
+int osmo_trau_frame_decode_8k(struct osmo_trau_frame *fr, const ubit_t *bits,
+			      enum osmo_trau_frame_direction dir)
+{
+	fr->dir = dir;
+
+	if (is_hr(bits)) {
+		/* normal sync pattern */
+		uint8_t cbits5 = get_bits(bits, 9, 5);
+		if (dir == OSMO_TRAU_DIR_UL) {
+			switch (cbits5) {	/* Section 5.2.4.1.1 */
+			case 0x02:
+				return decode8_hr(fr, bits, dir);
+			case 0x07:
+				return decode8_data(fr, bits, dir);
+			case 0x13:
+				return decode8_oam(fr, bits, dir);
+			}
+		} else {
+			/* Downlink */
+			switch (cbits5 >> 2) {	/* Section 5.2.4.1.2 */
+			case 0:
+				return decode8_hr(fr, bits, dir);
+			case 1:
+				return decode8_data(fr, bits, dir);
+			case 2:
+				return decode8_oam(fr, bits, dir);
+			}
+		}
+	} else if (is_amr_low(bits)) {
+		return decode8_amr_low(fr, bits, dir);
+	} else if (is_amr_67(bits)) {
+		return decode8_amr_67(fr, bits, dir);
+	} else if (is_amr_74(bits)) {
+		return decode8_amr_74(fr, bits, dir);
+	}
+
+	return -EINVAL;
+}
+
 /* }@ */

-- 
To view, visit https://gerrit.osmocom.org/c/libosmo-abis/+/18249
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmo-abis
Gerrit-Branch: master
Gerrit-Change-Id: I5cf42e6c445d9224be18503cebc7584b3beba08c
Gerrit-Change-Number: 18249
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge at osmocom.org>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200514/3c055afe/attachment.htm>


More information about the gerrit-log mailing list