l1sap: proper rate adaptation for CSD (RFC4040 'clearmode')
Since 95407f3f
osmo-bts-trx supports scheduling all CSD specific
channel rates, however the rate adaptation was missing.
On the radio interface we deal with CSD-modified V.110 frames, which
need to be converted to normal 80-bit V.110 frames (RA1'/RA1), which
in turn need to be batched and sent in RFC4040 "clearmode" 160 octet
RTP payloads (RA1/RA2 as per I.460).
Note that this patch comments out TCH/F14.4 in bts_supports_cm_data(),
so that all channel allocations for this mode would be NACKed. The
reason for this is that the rate adaptation functions for TCH/F14.4
are different than the RA1'/RA1 and the RA1/RA2.
For more information, see:
* 3GPP TS 44.021, section 8 (functions RA1'/RA1)
* ITU-T I.460, section 1.1 "Rate adaption of 8, 16 and 32 kbit/s streams"
Change-Id: I5e3701ad52d5d428fd02caff037881045f2d0a02
Related: OS#1572
This commit is contained in:
parent
97d3bd3e62
commit
d1f8f3429c
|
@ -55,6 +55,7 @@ src/osmo-bts-oc2g/misc/.dirstamp
|
|||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/amr/amr_test
|
||||
tests/csd/csd_test
|
||||
tests/agch/agch_test
|
||||
tests/paging/paging_test
|
||||
tests/cipher/cipher_test
|
||||
|
|
|
@ -445,6 +445,7 @@ AC_OUTPUT(
|
|||
tests/power/Makefile
|
||||
tests/meas/Makefile
|
||||
tests/amr/Makefile
|
||||
tests/csd/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
|
|
|
@ -24,6 +24,7 @@ noinst_HEADERS = \
|
|||
tx_power.h \
|
||||
control_if.h \
|
||||
cbch.h \
|
||||
csd_v110.h \
|
||||
l1sap.h \
|
||||
lchan.h \
|
||||
power_control.h \
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
/* RFC4040 "clearmode" RTP payload length */
|
||||
#define RFC4040_RTP_PLEN 160
|
||||
|
||||
struct gsm_lchan;
|
||||
|
||||
struct csd_v110_frame_desc {
|
||||
uint16_t num_blocks;
|
||||
uint16_t num_bits;
|
||||
};
|
||||
|
||||
struct csd_v110_lchan_desc {
|
||||
struct csd_v110_frame_desc fr;
|
||||
struct csd_v110_frame_desc hr;
|
||||
};
|
||||
|
||||
extern const struct csd_v110_lchan_desc csd_v110_lchan_desc[256];
|
||||
|
||||
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
|
||||
const uint8_t *data, size_t data_len);
|
||||
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
|
||||
const uint8_t *rtp, size_t rtp_len);
|
|
@ -51,6 +51,7 @@ libbts_a_SOURCES = \
|
|||
bts_ctrl_commands.c \
|
||||
bts_ctrl_lookup.c \
|
||||
bts_shutdown_fsm.c \
|
||||
csd_v110.c \
|
||||
l1sap.c \
|
||||
cbch.c \
|
||||
power_control.c \
|
||||
|
|
|
@ -875,7 +875,7 @@ static bool bts_supports_cm_data(const struct gsm_bts *bts,
|
|||
switch (bts->variant) {
|
||||
case BTS_OSMO_TRX:
|
||||
switch (cm->chan_rate) {
|
||||
case RSL_CMOD_CSD_T_14k4:
|
||||
/* TODO: RSL_CMOD_CSD_T_14k4 */
|
||||
case RSL_CMOD_CSD_T_9k6:
|
||||
if (cm->chan_rt != RSL_CMOD_CRT_TCH_Bm)
|
||||
return false; /* invalid */
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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 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 <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm44021.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/isdn/v110.h>
|
||||
|
||||
#include <osmo-bts/csd_v110.h>
|
||||
#include <osmo-bts/lchan.h>
|
||||
|
||||
/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */
|
||||
const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = {
|
||||
#if 0
|
||||
[GSM48_CMODE_DATA_14k5] = {
|
||||
/* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */
|
||||
.fr = { .num_blocks = 1, .num_bits = 290 },
|
||||
},
|
||||
#endif
|
||||
[GSM48_CMODE_DATA_12k0] = {
|
||||
/* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */
|
||||
.fr = { .num_blocks = 4, .num_bits = 60 },
|
||||
},
|
||||
[GSM48_CMODE_DATA_6k0] = {
|
||||
/* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */
|
||||
.fr = { .num_blocks = 2, .num_bits = 60 },
|
||||
/* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */
|
||||
.hr = { .num_blocks = 4, .num_bits = 60 },
|
||||
},
|
||||
[GSM48_CMODE_DATA_3k6] = {
|
||||
/* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */
|
||||
.fr = { .num_blocks = 2, .num_bits = 36 },
|
||||
/* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */
|
||||
.hr = { .num_blocks = 4, .num_bits = 36 },
|
||||
},
|
||||
};
|
||||
|
||||
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
|
||||
const uint8_t *data, size_t data_len)
|
||||
{
|
||||
const struct csd_v110_frame_desc *desc;
|
||||
ubit_t ra_bits[80 * 4];
|
||||
|
||||
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
||||
if (lchan->type == GSM_LCHAN_TCH_F)
|
||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].fr;
|
||||
else
|
||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].hr;
|
||||
if (OSMO_UNLIKELY(desc->num_blocks == 0))
|
||||
return -ENOTSUP;
|
||||
|
||||
/* handle empty/incomplete frames gracefully */
|
||||
if (OSMO_UNLIKELY(data_len < (desc->num_blocks * desc->num_bits)))
|
||||
return -ENODATA;
|
||||
|
||||
/* RA1'/RA1: convert to an intermediate data rate */
|
||||
for (unsigned int i = 0; i < desc->num_blocks; i++) {
|
||||
struct osmo_v110_decoded_frame df;
|
||||
|
||||
/* convert modified V.110 frames to normal V.110 frames */
|
||||
if (desc->num_bits == 60)
|
||||
osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60);
|
||||
else /* desc->num_bits == 36 */
|
||||
osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36);
|
||||
|
||||
/* FIXME: E1 .. E3 must be set by out-of-band knowledge! */
|
||||
memset(&df.e_bits[0], 0, 3);
|
||||
|
||||
osmo_v110_encode_frame(&ra_bits[i * 80], 80, &df);
|
||||
}
|
||||
|
||||
/* RA1/RA2: convert from an intermediate rate to 64 kbit/s */
|
||||
if (desc->num_blocks == 4) {
|
||||
/* 4 * 80 bits (16 kbit/s) => 2 bits per octet */
|
||||
for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) {
|
||||
rtp[i] = (0xff >> 2);
|
||||
rtp[i] |= (ra_bits[j++] << 7);
|
||||
rtp[i] |= (ra_bits[j++] << 6);
|
||||
}
|
||||
} else {
|
||||
/* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
|
||||
for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) {
|
||||
rtp[i] = (0xff >> 1);
|
||||
rtp[i] |= (ra_bits[i] << 7);
|
||||
}
|
||||
}
|
||||
|
||||
return RFC4040_RTP_PLEN;
|
||||
}
|
||||
|
||||
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
|
||||
const uint8_t *rtp, size_t rtp_len)
|
||||
{
|
||||
const struct csd_v110_frame_desc *desc;
|
||||
ubit_t ra_bits[80 * 4];
|
||||
|
||||
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
||||
if (lchan->type == GSM_LCHAN_TCH_F)
|
||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].fr;
|
||||
else
|
||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].hr;
|
||||
if (OSMO_UNLIKELY(desc->num_blocks == 0))
|
||||
return -ENOTSUP;
|
||||
|
||||
if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN))
|
||||
return -EINVAL;
|
||||
|
||||
/* RA1/RA2: convert from 64 kbit/s to an intermediate rate */
|
||||
if (desc->num_blocks == 4) {
|
||||
/* 4 * 80 bits (16 kbit/s) => 2 bits per octet */
|
||||
for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) {
|
||||
ra_bits[j++] = (rtp[i] >> 7);
|
||||
ra_bits[j++] = (rtp[i] >> 6) & 0x01;
|
||||
}
|
||||
} else {
|
||||
/* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
|
||||
for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++)
|
||||
ra_bits[i] = (rtp[i] >> 7);
|
||||
}
|
||||
|
||||
/* RA1'/RA1: convert to an intermediate data rate */
|
||||
for (unsigned int i = 0; i < desc->num_blocks; i++) {
|
||||
struct osmo_v110_decoded_frame df;
|
||||
|
||||
/* convert modified V.110 frames to normal V.110 frames */
|
||||
osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80);
|
||||
if (desc->num_bits == 60)
|
||||
osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df);
|
||||
else /* desc->num_bits == 36 */
|
||||
osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df);
|
||||
}
|
||||
|
||||
return desc->num_blocks * desc->num_bits;
|
||||
}
|
|
@ -58,6 +58,7 @@
|
|||
#include <osmo-bts/pcuif_proto.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
#include <osmo-bts/asci.h>
|
||||
#include <osmo-bts/csd_v110.h>
|
||||
|
||||
/* determine the CCCH block number based on the frame number */
|
||||
unsigned int l1sap_fn2ccch_block(uint32_t fn)
|
||||
|
@ -1829,9 +1830,27 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void send_ul_rtp_packet_data(struct gsm_lchan *lchan, uint32_t fn,
|
||||
const uint8_t *data, uint16_t data_len)
|
||||
{
|
||||
uint8_t rtp_pl[RFC4040_RTP_PLEN];
|
||||
int rc;
|
||||
|
||||
rc = csd_v110_rtp_encode(lchan, &rtp_pl[0], data, data_len);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
|
||||
&rtp_pl[0], sizeof(rtp_pl),
|
||||
fn_ms_adj(fn, lchan),
|
||||
lchan->rtp_tx_marker);
|
||||
/* Only clear the marker bit once we have sent a RTP packet with it */
|
||||
lchan->rtp_tx_marker = false;
|
||||
}
|
||||
|
||||
/* a helper function for the logic in l1sap_tch_ind() */
|
||||
static void send_ul_rtp_packet(struct gsm_lchan *lchan, uint32_t fn,
|
||||
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
|
||||
static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn,
|
||||
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
|
||||
{
|
||||
if (lchan->abis_ip.osmux.use) {
|
||||
lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len,
|
||||
|
@ -1859,7 +1878,7 @@ static void send_rtp_rfc5993(struct gsm_lchan *lchan, uint32_t fn,
|
|||
else
|
||||
toc = 0x00;
|
||||
msgb_push_u8(msg, toc);
|
||||
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
||||
}
|
||||
|
||||
/* A helper function for l1sap_tch_ind(): handling BFI
|
||||
|
@ -1884,7 +1903,7 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
|||
/* did it actually give us some output? */
|
||||
if (rc > 0) {
|
||||
/* yes, send it out in RTP */
|
||||
send_ul_rtp_packet(lchan, fn, ecu_out, rc);
|
||||
send_ul_rtp_packet_speech(lchan, fn, ecu_out, rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1892,7 +1911,7 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
|||
/* Are we in rtp continuous-streaming special mode? If so, send out
|
||||
* a BFI packet as zero-length RTP payload. */
|
||||
if (lchan->ts->trx->bts->rtp_nogaps_mode) {
|
||||
send_ul_rtp_packet(lchan, fn, NULL, 0);
|
||||
send_ul_rtp_packet_speech(lchan, fn, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1948,11 +1967,21 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
|||
if (lchan->ecu_state)
|
||||
osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len);
|
||||
/* hand msg to RTP code for transmission */
|
||||
if (bts->emit_hr_rfc5993 && lchan->type == GSM_LCHAN_TCH_H &&
|
||||
lchan->tch_mode == GSM48_CMODE_SPEECH_V1)
|
||||
send_rtp_rfc5993(lchan, fn, msg);
|
||||
else
|
||||
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||
switch (lchan->rsl_cmode) {
|
||||
case RSL_CMOD_SPD_SPEECH:
|
||||
if (bts->emit_hr_rfc5993 && lchan->type == GSM_LCHAN_TCH_H &&
|
||||
lchan->tch_mode == GSM48_CMODE_SPEECH_V1)
|
||||
send_rtp_rfc5993(lchan, fn, msg);
|
||||
else
|
||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
||||
break;
|
||||
case RSL_CMOD_SPD_DATA:
|
||||
send_ul_rtp_packet_data(lchan, fn, msg->data, msg->len);
|
||||
break;
|
||||
case RSL_CMOD_SPD_SIGN:
|
||||
default: /* shall not happen */
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
/* if loopback is enabled, also queue received RTP data */
|
||||
if (lchan->loopback) {
|
||||
/* add new frame to queue, make sure the queue doesn't get too long */
|
||||
|
@ -2247,10 +2276,23 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
|||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
msg = l1sap_msgb_alloc(rtp_pl_len);
|
||||
msg = l1sap_msgb_alloc(512);
|
||||
if (!msg)
|
||||
return;
|
||||
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
|
||||
|
||||
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
|
||||
int rc = csd_v110_rtp_decode(lchan, msg->tail,
|
||||
rtp_pl, rtp_pl_len);
|
||||
if (rc > 0) {
|
||||
msgb_put(msg, rc);
|
||||
} else {
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
|
||||
}
|
||||
|
||||
msgb_pull(msg, sizeof(struct osmo_phsap_prim));
|
||||
|
||||
/* Store RTP header Marker bit in control buffer */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SUBDIRS = paging cipher agch misc handover tx_power power meas ta_control amr
|
||||
SUBDIRS = paging cipher agch misc handover tx_power power meas ta_control amr csd
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
SUBDIRS += sysmobts
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOCODEC_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOTRAU_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(NULL)
|
||||
AM_LDFLAGS = -no-install
|
||||
LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCODEC_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
check_PROGRAMS = csd_test
|
||||
EXTRA_DIST = csd_test.err
|
||||
|
||||
csd_test_SOURCES = csd_test.c
|
||||
csd_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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 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 <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/isdn/v110.h>
|
||||
|
||||
#include <osmo-bts/csd_v110.h>
|
||||
#include <osmo-bts/lchan.h>
|
||||
|
||||
#define BBUF_MAX 290
|
||||
|
||||
struct test_case {
|
||||
const char *name;
|
||||
enum gsm_chan_t lchan_type;
|
||||
enum gsm48_chan_mode tch_mode;
|
||||
};
|
||||
|
||||
static const struct test_case tests[] = {
|
||||
{
|
||||
.name = "TCH/F14.4",
|
||||
.lchan_type = GSM_LCHAN_TCH_F,
|
||||
.tch_mode = GSM48_CMODE_DATA_14k5,
|
||||
},
|
||||
{
|
||||
.name = "TCH/F9.6",
|
||||
.lchan_type = GSM_LCHAN_TCH_F,
|
||||
.tch_mode = GSM48_CMODE_DATA_12k0,
|
||||
},
|
||||
{
|
||||
.name = "TCH/F4.8",
|
||||
.lchan_type = GSM_LCHAN_TCH_F,
|
||||
.tch_mode = GSM48_CMODE_DATA_6k0,
|
||||
},
|
||||
{
|
||||
.name = "TCH/H4.8",
|
||||
.lchan_type = GSM_LCHAN_TCH_H,
|
||||
.tch_mode = GSM48_CMODE_DATA_6k0,
|
||||
},
|
||||
{
|
||||
.name = "TCH/F2.4",
|
||||
.lchan_type = GSM_LCHAN_TCH_F,
|
||||
.tch_mode = GSM48_CMODE_DATA_3k6,
|
||||
},
|
||||
{
|
||||
.name = "TCH/H2.4",
|
||||
.lchan_type = GSM_LCHAN_TCH_H,
|
||||
.tch_mode = GSM48_CMODE_DATA_3k6,
|
||||
},
|
||||
};
|
||||
|
||||
static void exec_test_case(const struct test_case *tc)
|
||||
{
|
||||
const struct csd_v110_frame_desc *desc;
|
||||
uint8_t rtp[RFC4040_RTP_PLEN] = { 0 };
|
||||
ubit_t data_enc[BBUF_MAX];
|
||||
ubit_t data_dec[BBUF_MAX];
|
||||
int rc;
|
||||
|
||||
/* obtain a V.110 frame description for the given channel type/rate */
|
||||
OSMO_ASSERT(tc->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
||||
if (tc->lchan_type == GSM_LCHAN_TCH_F)
|
||||
desc = &csd_v110_lchan_desc[tc->tch_mode].fr;
|
||||
else
|
||||
desc = &csd_v110_lchan_desc[tc->tch_mode].hr;
|
||||
|
||||
/* total number of bits carried by a radio interface block */
|
||||
const unsigned int bit_num = desc->num_bits * desc->num_blocks;
|
||||
if (bit_num == 0) {
|
||||
fprintf(stderr, "[i] Skipping '%s' (not implemented)\n", tc->name);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[i] Testing '%s' (bitnum=%u)\n", tc->name, bit_num);
|
||||
|
||||
struct gsm_lchan lchan = {
|
||||
.type = tc->lchan_type,
|
||||
.tch_mode = tc->tch_mode,
|
||||
};
|
||||
|
||||
/* populate the data_enc[] buffer with some bits */
|
||||
OSMO_ASSERT(bit_num <= BBUF_MAX);
|
||||
for (unsigned int i = 0; i < bit_num; i++)
|
||||
data_enc[i] = i & 0x01;
|
||||
|
||||
/* encode an RTP frame and print it */
|
||||
rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], bit_num);
|
||||
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
|
||||
if (rc != RFC4040_RTP_PLEN)
|
||||
return;
|
||||
/* print the encoded RTP frame (16 bytes per row) */
|
||||
for (unsigned int i = 0; i < sizeof(rtp) / 16; i++)
|
||||
fprintf(stderr, " %s\n", osmo_hexdump(&rtp[i * 16], 16));
|
||||
|
||||
/* decode the encoded RTP frame */
|
||||
rc = csd_v110_rtp_decode(&lchan, &data_dec[0], &rtp[0], sizeof(rtp));
|
||||
fprintf(stderr, "[i] csd_v110_rtp_decode() returns %d\n", rc);
|
||||
if (rc != bit_num)
|
||||
return;
|
||||
/* compare data_dec[] vs data_enc[] */
|
||||
for (unsigned int i = 0; i < bit_num; i++) {
|
||||
if (data_dec[i] == data_enc[i])
|
||||
continue;
|
||||
fprintf(stderr, "[!] Data mismatch @ %03u: D%u vs E%u\n",
|
||||
i, data_dec[i], data_enc[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(tests); i++)
|
||||
exec_test_case(&tests[i]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
[i] Skipping 'TCH/F14.4' (not implemented)
|
||||
[i] Testing 'TCH/F9.6' (bitnum=240)
|
||||
[i] csd_v110_rtp_encode() returns 160
|
||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||
ff 7f 7f 7f bf 3f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
||||
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf 3f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||
ff 7f 7f 7f bf 3f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
||||
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf 3f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
[i] csd_v110_rtp_decode() returns 240
|
||||
[i] Testing 'TCH/F4.8' (bitnum=120)
|
||||
[i] csd_v110_rtp_encode() returns 160
|
||||
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
|
||||
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
|
||||
ff ff 7f ff 7f ff 7f ff ff 7f 7f 7f 7f ff 7f ff
|
||||
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
|
||||
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
|
||||
ff ff 7f ff 7f ff 7f ff ff 7f 7f 7f 7f ff 7f ff
|
||||
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||
[i] csd_v110_rtp_decode() returns 120
|
||||
[i] Testing 'TCH/H4.8' (bitnum=240)
|
||||
[i] csd_v110_rtp_encode() returns 160
|
||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||
ff 7f 7f 7f bf 3f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
||||
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf 3f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||
ff 7f 7f 7f bf 3f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
||||
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf 3f 7f 7f
|
||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||
[i] csd_v110_rtp_decode() returns 240
|
||||
[i] Testing 'TCH/F2.4' (bitnum=72)
|
||||
[i] csd_v110_rtp_encode() returns 160
|
||||
7f 7f 7f 7f 7f 7f 7f 7f ff 7f 7f ff ff 7f 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f 7f 7f ff 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||
7f 7f 7f 7f 7f 7f 7f 7f ff 7f 7f ff ff 7f 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f 7f 7f ff 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||
[i] csd_v110_rtp_decode() returns 72
|
||||
[i] Testing 'TCH/H2.4' (bitnum=144)
|
||||
[i] csd_v110_rtp_encode() returns 160
|
||||
3f 3f 3f 3f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
||||
bf 7f bf 7f bf 3f 7f 7f bf 7f bf 7f bf 7f bf 7f
|
||||
bf 7f bf 7f bf 7f bf 7f 3f 3f 3f 3f bf 7f bf 7f
|
||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 3f 7f 7f
|
||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
||||
3f 3f 3f 3f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
||||
bf 7f bf 7f bf 3f 7f 7f bf 7f bf 7f bf 7f bf 7f
|
||||
bf 7f bf 7f bf 7f bf 7f 3f 3f 3f 3f bf 7f bf 7f
|
||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 3f 7f 7f
|
||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
||||
[i] csd_v110_rtp_decode() returns 144
|
|
@ -70,3 +70,9 @@ cat $abs_srcdir/amr/amr_test.ok > expout
|
|||
cat $abs_srcdir/amr/amr_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/amr/amr_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([csd])
|
||||
AT_KEYWORDS([csd])
|
||||
cat $abs_srcdir/csd/csd_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/csd/csd_test], [], [ignore], [experr])
|
||||
AT_CLEANUP
|
||||
|
|
Loading…
Reference in New Issue