falconia has uploaded this change for review.

View Change

tests: add trau2rtp_gen program

This test program converts TRAU-16k or TRAU-8k frames into RTP payloads,
both represented in hex. (Hex representation of RTP payloads follows
TW-TS-005 spec; hex representation of TRAU frames is ad hoc.) It can
be used as a basis for unit tests, verifying that a given TRAU frame
input turns into corresponding RTP output, and it can also be used
(in conjuction with additional Themyscira utilities) to turn captured
TRAU frame streams into playable audio.

Change-Id: I3cc8502406e930317b680b0ace2fce479e495ccd
---
M .gitignore
M tests/Makefile.am
A tests/trau_conv/trau2rtp_gen.c
3 files changed, 220 insertions(+), 0 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/libosmo-abis refs/changes/58/39558/1
diff --git a/.gitignore b/.gitignore
index fd0974d..dbe50db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,7 @@
tests/raa_prime/test_enc
tests/trau_pcu_ericsson/trau_pcu_ericsson_test
tests/trau_conv/trau16_to_rtp
+tests/trau_conv/trau2rtp_gen
tests/trau_sync/trau_sync_test


diff --git a/tests/Makefile.am b/tests/Makefile.am
index 04ce529..d904241 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -9,6 +9,7 @@
raa_prime/test_dec \
raa_prime/test_enc \
trau_conv/trau16_to_rtp \
+ trau_conv/trau2rtp_gen \
trau_sync/trau_sync_test \
trau_pcu_ericsson/trau_pcu_ericsson_test

@@ -54,6 +55,9 @@
trau_conv_trau16_to_rtp_SOURCES = trau_conv/trau16_to_rtp.c
trau_conv_trau16_to_rtp_LDADD = $(TRAU_LA_LIBS)

+trau_conv_trau2rtp_gen_SOURCES = trau_conv/trau2rtp_gen.c
+trau_conv_trau2rtp_gen_LDADD = $(TRAU_LA_LIBS)
+
trau_sync_trau_sync_test_SOURCES = trau_sync/trau_sync_test.c
trau_sync_trau_sync_test_LDADD = $(TRAU_LA_LIBS)

diff --git a/tests/trau_conv/trau2rtp_gen.c b/tests/trau_conv/trau2rtp_gen.c
new file mode 100644
index 0000000..60d98c8
--- /dev/null
+++ b/tests/trau_conv/trau2rtp_gen.c
@@ -0,0 +1,215 @@
+/*
+ * This program is a generic exerciser for libosmotrau functions
+ * osmo_trau_frame_decode_16k() or osmo_trau_frame_decode_8k(),
+ * followed by osmo_trau2rtp(). Specifically, it reads input in the form
+ * of hex lines, with each line representing a TRAU-16k frame as a string
+ * of 80 hex digits or a TRAU-8k frame as a string of 40 hex digits,
+ * passes each TRAU frame thus read into libosmotrau functions under test,
+ * and emits the resulting RTP payload in another hex format defined in
+ * TW-TS-005. IOW, this program is a hex-to-hex converter where input
+ * hex lines represent TRAU frames and output hex lines represent RTP payloads.
+ *
+ * Spec reference for output hex format:
+ * https://www.freecalypso.org/specs/tw-ts-005-v010003.txt
+ *
+ * Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2025 - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/trau/trau_frame.h>
+#include <osmocom/trau/trau_rtp.h>
+#include <osmocom/gsm/rtp_extensions.h>
+
+static enum osmo_trau_frame_direction direction;
+static bool trau_8k_mode;
+static struct osmo_trau2rtp_state trau2rtp_st;
+static FILE *out_file;
+
+static void do_trau_frame_16k(struct osmo_trau_frame *tf, const char *hex_str)
+{
+ uint8_t input_bytes[40];
+ ubit_t trau_bits[320];
+ int rc;
+
+ osmo_hexparse(hex_str, input_bytes, sizeof(input_bytes));
+ osmo_pbit2ubit(trau_bits, input_bytes, sizeof(trau_bits));
+ rc = osmo_trau_frame_decode_16k(tf, trau_bits, direction);
+ if (rc < 0) {
+ fprintf(stderr,
+ "error: osmo_trau_frame_decode_16k() returned %d\n",
+ rc);
+ exit(1);
+ }
+}
+
+static void do_trau_frame_8k(struct osmo_trau_frame *tf, const char *hex_str)
+{
+ uint8_t input_bytes[20];
+ ubit_t trau_bits[160];
+ int rc;
+
+ osmo_hexparse(hex_str, input_bytes, sizeof(input_bytes));
+ osmo_pbit2ubit(trau_bits, input_bytes, sizeof(trau_bits));
+ rc = osmo_trau_frame_decode_8k(tf, trau_bits, direction);
+ if (rc < 0) {
+ fprintf(stderr,
+ "error: osmo_trau_frame_decode_8k() returned %d\n",
+ rc);
+ exit(1);
+ }
+}
+
+static void emit_hex_frame(const uint8_t *frame, unsigned nbytes)
+{
+ unsigned n;
+
+ for (n = 0; n < nbytes; n++)
+ fprintf(out_file, "%02X", frame[n]);
+ putc('\n', out_file);
+}
+
+static void process_record(const char *hex_input)
+{
+ struct osmo_trau_frame tf;
+ uint8_t rtp_pl[40]; /* maximum RTP payload length for TW-TS-005 */
+ int rc;
+
+ if (trau_8k_mode)
+ do_trau_frame_8k(&tf, hex_input);
+ else
+ do_trau_frame_16k(&tf, hex_input);
+ trau2rtp_st.type = tf.type;
+ rc = osmo_trau2rtp(rtp_pl, sizeof(rtp_pl), &tf, &trau2rtp_st);
+ if (rc < 0) {
+ fprintf(stderr, "error: osmo_trau2rtp() returned %d\n", rc);
+ exit(1);
+ }
+ if (rc == 0) {
+ /* TW-TS-005 section 4.3.1 */
+ fputs("NULL\n", out_file);
+ return;
+ }
+ emit_hex_frame(rtp_pl, rc);
+}
+
+static void process_line(char *linebuf, const char *infname, int lineno)
+{
+ char *cp = linebuf, *hex_str;
+ int ndig, expect_digits;
+
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0' || *cp == '#')
+ return;
+ /* expect string of 40 or 80 hex digits */
+ if (trau_8k_mode)
+ expect_digits = 40;
+ else
+ expect_digits = 80;
+ hex_str = cp;
+ for (ndig = 0; ndig < expect_digits; ndig++) {
+ if (!isxdigit(*cp))
+ goto inv;
+ cp++;
+ }
+ if (*cp) {
+ if (!isspace(*cp))
+ goto inv;
+ *cp++ = '\0';
+ }
+ /* must be end of non-comment line */
+ while (isspace(*cp))
+ cp++;
+ if (*cp != '\0' && *cp != '#')
+ goto inv;
+
+ process_record(hex_str);
+ return;
+
+inv: fprintf(stderr, "%s line %d: invalid syntax\n", infname, lineno);
+ exit(1);
+}
+
+static void process_file(const char *infname, const char *outfname)
+{
+ FILE *inf;
+ char linebuf[256];
+ int lineno;
+
+ inf = fopen(infname, "r");
+ if (!inf) {
+ perror(infname);
+ exit(1);
+ }
+ if (outfname) {
+ out_file = fopen(outfname, "w");
+ if (!out_file) {
+ perror(outfname);
+ exit(1);
+ }
+ } else {
+ out_file = stdout;
+ }
+ for (lineno = 1; fgets(linebuf, sizeof(linebuf), inf); lineno++)
+ process_line(linebuf, infname, lineno);
+ fclose(inf);
+ if (outfname) {
+ fclose(out_file);
+ out_file = NULL;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ extern int optind;
+
+ trau_8k_mode = false;
+ direction = OSMO_TRAU_DIR_UL;
+ trau2rtp_st.rtp_extensions = 0;
+ while ((opt = getopt(argc, argv, "8dx")) != EOF) {
+ switch (opt) {
+ case '8':
+ trau_8k_mode = true;
+ break;
+ case 'd':
+ direction = OSMO_TRAU_DIR_DL;
+ break;
+ case 'x':
+ trau2rtp_st.rtp_extensions =
+ OSMO_RTP_EXT_TWTS001 | OSMO_RTP_EXT_TWTS002;
+ break;
+ default:
+ goto usage;
+ }
+ }
+ if (argc < optind + 1 || argc > optind + 2)
+ goto usage;
+ process_file(argv[optind], argv[optind + 1]);
+ exit(0);
+
+usage: fprintf(stderr, "usage: %s [-8] [-d] [-x] input-file [output-file]\n",
+ argv[0]);
+ exit(1);
+}

To view, visit change 39558. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: libosmo-abis
Gerrit-Branch: master
Gerrit-Change-Id: I3cc8502406e930317b680b0ace2fce479e495ccd
Gerrit-Change-Number: 39558
Gerrit-PatchSet: 1
Gerrit-Owner: falconia <falcon@freecalypso.org>