blob: 9c9baa77a5f0912bb4c31ab688ea7bfc7993cc40 [file] [log] [blame]
/* New (2020) TRAU frame handling according to GSM TS 48.060 + 48.061 */
/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "internal.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <osmocom/trau/trau_frame.h>
#include <osmocom/core/logging.h>
/*! \addtogroup trau_frame
* @{
*
* \file trau_frame.c
*/
static uint32_t get_bits(const ubit_t *bitbuf, int offset, int num)
{
int i;
uint32_t ret = 0;
for (i = offset; i < offset + num; i++) {
ret = ret << 1;
if (bitbuf[i])
ret |= 1;
}
return ret;
}
const struct value_string osmo_trau_frame_type_names[] = {
{ OSMO_TRAU_FT_NONE, "NONE" },
{ OSMO_TRAU16_FT_FR, "FR" },
{ OSMO_TRAU16_FT_HR, "HR" },
{ OSMO_TRAU16_FT_EFR, "EFR" },
{ OSMO_TRAU16_FT_AMR, "AMR" },
{ OSMO_TRAU16_FT_OAM, "OAM" },
{ OSMO_TRAU16_FT_DATA, "DATA" },
{ OSMO_TRAU16_FT_EDATA, "EDATA" },
{ OSMO_TRAU16_FT_D145_SYNC, "D145_SYNC" },
{ OSMO_TRAU16_FT_DATA_HR, "DATA_HR" },
{ OSMO_TRAU16_FT_IDLE, "IDLE" },
{ OSMO_TRAU8_SPEECH, "8SPEECH" },
{ OSMO_TRAU8_DATA, "8DATA" },
{ OSMO_TRAU8_OAM, "8OAM" },
{ OSMO_TRAU8_AMR_LOW, "8AMR_LOW" },
{ OSMO_TRAU8_AMR_6k7, "8AMR_6k7" },
{ OSMO_TRAU8_AMR_7k4, "8AMR_7k4" },
{ 0, NULL }
};
/*********************************************************************************
* New API; introduced in 2020 for use by osmo-mgw
*********************************************************************************/
/* Bits C21..C22 (16k) or C4..C5 (8k) */
enum ts48060_amr_frame_classification {
TS48060_AMR_FC_SPEECH_GOOD = 0x3,
TS48060_AMR_FC_SPEECH_DEGRADED = 0x2,
TS48060_AMR_FC_SPEECH_BAD = 0x1,
TS48060_AMR_FC_NO_SPEECH = 0x0,
};
/* 16k sub-slots */
static const ubit_t ft_fr_up_bits[5] = { 0, 0, 0, 1, 0 };
static const ubit_t ft_fr_down_bits[5] = { 1, 1, 1, 0, 0 };
static const ubit_t ft_data_up_bits[5] = { 0, 1, 0, 0, 0 };
static const ubit_t ft_data_down_bits[5] = { 1, 0, 1, 1, 0 };
static const ubit_t ft_idle_up_bits[5] = { 1, 0, 0, 0, 0 };
static const ubit_t ft_idle_down_bits[5] = { 0, 1, 1, 1, 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;
}
#define T16_500us 8 /* 500 us = 8 bits */
#define T16_250us 4 /* 250 us = 4 bits */
#define T8_250us 2 /* 250 us = 2 bits */
#define T8_125us 1 /* 125 us = 1 bits */
/* How many 16k bits to delay / advance (TS 48.060 Section 5.5.1.1.1) */
static int trau_frame_16_ta_us(uint8_t c6_11)
{
if (c6_11 <= 0x27)
return c6_11 * 500; /* delay frame N x 500us */
else if (c6_11 == 0x3e)
return 250; /* delay frame 250us */
else if (c6_11 == 0x3f)
return -250; /* advance frame 250us */
else
return 0;
}
/* How many 8k bits to delay / advance (TS 48.061 Table 6.1) */
static int trau_frame_8_ta_us(uint8_t c6_8)
{
/* TA2..TA1..TA0 == C6..C7..C8 */
switch (c6_8) {
case 0x7:
return 0;
case 0x6:
return -250;
case 0x5:
return 250;
case 0x3:
return 500;
case 0x4:
return 1000;
case 0x2:
return 2000;
case 0x1:
return 6000;
case 0:
return 9000;
default:
return 0;
}
}
/*! Determine the time alignment in us requested by CCU in a UL frame */
int osmo_trau_frame_dl_ta_us(const struct osmo_trau_frame *fr)
{
uint8_t c6_11, c6_8, fc;
/* timing alignment is only communicated from CCU to TRAU in UL */
if (fr->dir == OSMO_TRAU_DIR_DL)
return 0;
switch (fr->type) {
case OSMO_TRAU16_FT_FR:
case OSMO_TRAU16_FT_EFR:
case OSMO_TRAU16_FT_HR:
case OSMO_TRAU16_FT_AMR:
c6_11 = (fr->c_bits[5] << 5) | (fr->c_bits[6] << 4) | (fr->c_bits[7] << 3) |
(fr->c_bits[8] << 2) | (fr->c_bits[9] << 1) | (fr->c_bits[10] << 0);
return trau_frame_16_ta_us(c6_11);
break;
case OSMO_TRAU8_SPEECH:
c6_8 = (fr->c_bits[5] << 2) | (fr->c_bits[6] << 1) | (fr->c_bits[7] << 0);
return trau_frame_8_ta_us(c6_8);
break;
case OSMO_TRAU8_AMR_LOW:
fc = (fr->c_bits[3] << 1) | (fr->c_bits[4] << 0);
if (fc == TS48060_AMR_FC_NO_SPEECH) {
c6_11 = (fr->c_bits[5] << 5) | (fr->c_bits[6] << 4) | (fr->c_bits[7] << 3) |
(fr->c_bits[8] << 2) | (fr->c_bits[9] << 1) | (fr->c_bits[10] << 0);
/* For AMR speech on 8 kBit/s submultiplexing the same procedures as for AMR
* speech on 16 kBit/s submultiplexing shall be applied, see 3GPP TS 48.060 */
return trau_frame_16_ta_us(c6_11);
} else
return 0;
break;
default:
return 0;
}
}
static int encode16_handle_ta(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
{
if (fr->dir == OSMO_TRAU_DIR_DL) {
int num_bits = fr->dl_ta_usec * T16_250us / 250;
if (num_bits > 0) {
if (num_bits > 39 * 8)
num_bits = 39;
memset(trau_bits + 40 * 8, 1, num_bits);
return 40 * 8 + num_bits;
} else if (num_bits < 0) {
if (num_bits < -1 * T16_250us)
num_bits = -1 * T16_250us;
return 40 * 8 + num_bits;
}
}
return 40 * 8;
}
static int encode8_handle_ta(ubit_t *trau_bits, const struct osmo_trau_frame *fr)
{
if (fr->dir == OSMO_TRAU_DIR_DL) {
int num_bits = fr->dl_ta_usec * T8_250us / 250;
if (num_bits > 0) {
if (num_bits > 20 * 8 - 2)
num_bits = 20 * 8 - 2;
memset(trau_bits + 20 * 8, 1, num_bits);
return 20 * 8 + num_bits;
} else if (num_bits < 0) {
if (num_bits < -1 * T8_250us)
num_bits = -2;
return 20 * 8 + num_bits;
}
}
return 20 * 8;
}
/* 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);
/* T1 .. T4 */
memcpy(trau_bits + 316, fr->t_bits+0, 4);
/* handle timing adjustment */
return encode16_handle_ta(trau_bits, fr);
}
/* 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);
}
/* T1 .. T4 */
memcpy(trau_bits + 316, fr->t_bits + 0, 4);
return encode16_handle_ta(trau_bits, fr);
/* FIXME: handle TAE (Timing Alignment Extension) */
}
/* 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);
/* T1 .. T4 */
memcpy(trau_bits + 39 * 8 + 4, fr->t_bits, 4);
/* handle timing adjustment */
return encode16_handle_ta(trau_bits, fr);
}
/* 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;
}
static struct osmo_trau_frame fr_idle_frame = {
.type = OSMO_TRAU16_FT_IDLE,
.dir = OSMO_TRAU_DIR_DL,
.t_bits = { 1, 1, 1, 1 },
};
#define TRAU_FRAME_BITS (40*8)
static ubit_t encoded_idle_frame[TRAU_FRAME_BITS];
static int dbits_initted = 0;
/*! \brief return pointer to global buffer containing a TRAU idle frame */
static ubit_t *trau_idle_frame(void)
{
/* only initialize during the first call */
if (!dbits_initted) {
/* set all D-bits to 1 */
memset(&fr_idle_frame.d_bits, 0x01, 260);
memset(&fr_idle_frame.c_bits, 0x01, 25); /* spare are set to 1 */
/* set Downlink Idle Speech Frame pattern */
fr_idle_frame.c_bits[0] = 0; /* C1 */
fr_idle_frame.c_bits[1] = 1; /* C2 */
fr_idle_frame.c_bits[2] = 1; /* C3 */
fr_idle_frame.c_bits[3] = 1; /* C4 */
fr_idle_frame.c_bits[4] = 0; /* C5 */
/* set no Time Alignment pattern */
fr_idle_frame.c_bits[5] = 0; /* C6 */
fr_idle_frame.c_bits[6] = 0; /* C7 */
fr_idle_frame.c_bits[7] = 0; /* C8 */
fr_idle_frame.c_bits[8] = 0; /* C9 */
fr_idle_frame.c_bits[9] = 0; /* C10 */
fr_idle_frame.c_bits[10] = 0; /* C11 */
/* already set to 1, but maybe we need to modify it in the future */
fr_idle_frame.c_bits[11] = 1; /* C12 (UFE), good frame */
fr_idle_frame.c_bits[15] = 1; /* C16 (SP), no DTX */
encode16_fr(encoded_idle_frame, &fr_idle_frame);
dbits_initted = 1; /* set it to 1 to not call it again */
}
return encoded_idle_frame;
}
/* 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);
/* 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);
/* T1 .. T2 */
trau_bits[19 * 8 + 6] = fr->t_bits[0];
trau_bits[19 * 8 + 7] = fr->t_bits[1];
/* handle timing adjustment */
return encode8_handle_ta(trau_bits, fr);
}
/* 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;
/* 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 encode8_handle_ta(trau_bits, fr);
}
/* 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 encode8_handle_ta(trau_bits, fr);
}
/* 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 encode8_handle_ta(trau_bits, fr);
}
/* 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)
{
/* check for sufficient space provided by caller in output buffer */
switch (fr->type) {
case OSMO_TRAU16_FT_FR:
case OSMO_TRAU16_FT_EFR:
case OSMO_TRAU16_FT_HR:
case OSMO_TRAU16_FT_AMR:
/* timing alignment may happen: increased space requirement */
if (n_bits < 2 * 40 * 8 - 1)
return -ENOSPC;
break;
case OSMO_TRAU16_FT_OAM:
case OSMO_TRAU16_FT_IDLE:
case OSMO_TRAU16_FT_DATA_HR:
case OSMO_TRAU16_FT_DATA:
case OSMO_TRAU16_FT_D145_SYNC:
case OSMO_TRAU16_FT_EDATA:
if (n_bits < 1 * 40 * 8)
return -ENOSPC;
break;
case OSMO_TRAU8_SPEECH:
case OSMO_TRAU8_AMR_LOW:
case OSMO_TRAU8_AMR_6k7:
case OSMO_TRAU8_AMR_7k4:
/* timing alignment may happen: increased space requirement */
if (n_bits < 2 * 20 * 8 - 1)
return -ENOSPC;
break;
case OSMO_TRAU8_DATA:
case OSMO_TRAU8_OAM:
if (n_bits < 1 * 20 * 8)
return -ENOSPC;
break;
case OSMO_TRAU_FT_NONE:
break;
default:
return -EINVAL;
}
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_TRAU_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_TRAU_FT_NONE;
fr->dir = dir;
fr->dl_ta_usec = 0;
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->type = OSMO_TRAU_FT_NONE;
fr->dir = dir;
fr->dl_ta_usec = 0;
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;
}
/* }@ */