changeset 28:9bcdb091c24d

trau-decode: new program trau-hr-dump
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 18 Aug 2024 08:37:02 +0000
parents f508dacf2cf9
children fec87477e60b
files .hgignore trau-decode/Makefile trau-decode/gsmhr_unpack.c trau-decode/gsmhr_unpack.h trau-decode/trau-hr-dump.c
diffstat 5 files changed, 312 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sun Aug 18 06:26:23 2024 +0000
+++ b/.hgignore	Sun Aug 18 08:37:02 2024 +0000
@@ -6,6 +6,7 @@
 
 ^trau-decode/dump-1bit$
 ^trau-decode/trau-extr$
+^trau-decode/trau-hr-dump$
 ^trau-decode/trau-parse$
 ^trau-decode/trau-sync8$
 
--- a/trau-decode/Makefile	Sun Aug 18 06:26:23 2024 +0000
+++ b/trau-decode/Makefile	Sun Aug 18 08:37:02 2024 +0000
@@ -1,6 +1,6 @@
 CC=	gcc
 CFLAGS=	-O2
-PROGS=	dump-1bit trau-extr trau-parse trau-sync8
+PROGS=	dump-1bit trau-extr trau-hr-dump trau-parse trau-sync8
 
 all:	${PROGS}
 
@@ -10,6 +10,9 @@
 trau-extr:	extr-fr.o extr-efr.o extr-main.o
 	${CC} ${CFLAGS} -o $@ $^ -lgsmfr2
 
+trau-hr-dump:	crc8gen.o gsmhr_unpack.o trau-hr-dump.o
+	${CC} ${CFLAGS} -o $@ $^
+
 trau-parse:	crc8gen.o parse-fr.o parse-efr.o parse-main.o
 	${CC} ${CFLAGS} -o $@ $^ -lgsmfr2 -lgsmefr
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trau-decode/gsmhr_unpack.c	Sun Aug 18 08:37:02 2024 +0000
@@ -0,0 +1,68 @@
+/*
+ * This module has been copied from WIP libgsmhr1: it holds the function that
+ * unpacks a single GSM-HR codec frame from TS 101 318 packed format into
+ * the codec's native array of parameters.
+ */
+
+#include <stdint.h>
+#include "gsmhr_unpack.h"
+
+void gsmhr_unpack_ts101318(const uint8_t *payload, int16_t *params)
+{
+	const uint8_t *c = payload;
+
+	params[0]  = (*c >> 3) & 0x1F;
+	params[1]  = (*c++ & 0x7) << 8;
+	params[1] |= *c++;
+	params[2]  = (*c++ & 0xFF) << 1;
+	params[2] |= (*c >> 7) & 0x1;
+	params[3]  = (*c++ & 0x7F) << 1;
+	params[3] |= (*c >> 7) & 0x1;
+	params[4]  = (*c >> 6) & 0x1;
+	params[5]  = (*c >> 4) & 0x3;
+	if (params[5]) {
+		/* voiced modes */
+		params[6]  = (*c++ & 0xF) << 4;
+		params[6] |= (*c >> 4) & 0xF;
+		params[7]  = (*c++ & 0xF) << 5;
+		params[7] |= (*c >> 3) & 0x1F;
+		params[8]  = (*c++ & 0x7) << 2;
+		params[8] |= (*c >> 6) & 0x3;
+		params[9]  = (*c >> 2) & 0xF;
+		params[10]  = (*c++ & 0x3) << 7;
+		params[10] |= (*c >> 1) & 0x7F;
+		params[11]  = (*c++ & 0x1) << 4;
+		params[11] |= (*c >> 4) & 0xF;
+		params[12]  = *c++ & 0xF;
+		params[13]  = (*c++ & 0xFF) << 1;
+		params[13] |= (*c >> 7) & 0x1;
+		params[14]  = (*c >> 2) & 0x1F;
+		params[15]  = (*c++ & 0x3) << 2;
+		params[15] |= (*c >> 6) & 0x3;
+		params[16]  = (*c++ & 0x3F) << 3;
+		params[16] |= (*c >> 5) & 0x7;
+		params[17]  = *c++ & 0x1F;
+	} else {
+		/* unvoiced mode */
+		params[6]  = (*c++ & 0xF) << 3;
+		params[6] |= (*c >> 5) & 0x7;
+		params[7]  = (*c++ & 0x1F) << 2;
+		params[7] |= (*c >> 6) & 0x3;
+		params[8]  = (*c >> 1) & 0x1F;
+		params[9]  = (*c++ & 0x1) << 6;
+		params[9] |= (*c >> 2) & 0x3F;
+		params[10]  = (*c++ & 0x3) << 5;
+		params[10] |= (*c >> 3) & 0x1F;
+		params[11]  = (*c++ & 0x7) << 2;
+		params[11] |= (*c >> 6) & 0x3;
+		params[12]  = (*c++ & 0x3F) << 1;
+		params[12] |= (*c >> 7) & 0x1;
+		params[13]  = *c++ & 0x7F;
+		params[14]  = (*c >> 3) & 0x1F;
+		params[15]  = (*c++ & 0x7) << 4;
+		params[15] |= (*c >> 4) & 0xF;
+		params[16]  = (*c++ & 0xF) << 3;
+		params[16] |= (*c >> 5) & 0x7;
+		params[17]  = *c++ & 0x1F;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trau-decode/gsmhr_unpack.h	Sun Aug 18 08:37:02 2024 +0000
@@ -0,0 +1,6 @@
+/*
+ * Function prototype for our local copy (outside of libgsmhr1)
+ * of gsmhr_unpack_ts101318() function.
+ */
+
+void gsmhr_unpack_ts101318(const uint8_t *payload, int16_t *params);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trau-decode/trau-hr-dump.c	Sun Aug 18 08:37:02 2024 +0000
@@ -0,0 +1,233 @@
+/*
+ * This program reads a 64 kbit/s timeslot recording file, examines one
+ * of the eight 8 kbit/s subslots (selected), looks for the sync pattern of
+ * GSM 08.61, decodes each frame as HRv1 speech and dumps everything it
+ * decodes.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "gsmhr_unpack.h"
+#include "osmo_bits.h"
+
+/*
+ * EFR TRAU parity (also used for HR)
+ *
+ * g(x) = x^3 + x^1 + 1
+ */
+static const struct osmo_crc8gen_code gsm0860_efr_crc3 = {
+	.bits = 3,
+	.poly = 0x3,
+	.init = 0x0,
+	.remainder = 0x7,
+};
+
+static uint8_t *filebuf;
+static unsigned total_size;
+
+static void
+read_ts_file(filename, subslot_arg)
+	char *filename, *subslot_arg;
+{
+	FILE *inf;
+	struct stat st;
+	int subslot, right_shift;
+	unsigned n;
+	uint8_t *dp;
+	int b;
+
+	inf = fopen(filename, "r");
+	if (!inf) {
+		perror(filename);
+		exit(1);
+	}
+	fstat(fileno(inf), &st);
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr, "error: %s is not a regular file\n", filename);
+		exit(1);
+	}
+	total_size = st.st_size;
+	if (total_size < 160) {
+		fprintf(stderr, "error: %s is too short\n", filename);
+		exit(1);
+	}
+	filebuf = malloc(total_size);
+	if (!filebuf) {
+		perror("malloc of file size");
+		exit(1);
+	}
+	subslot = atoi(subslot_arg);
+	if (subslot < 0 || subslot > 7) {
+		fprintf(stderr, "error: invalid subslot argument\n");
+		exit(1);
+	}
+	right_shift = 7 - subslot;
+	dp = filebuf;
+	for (n = 0; n < total_size; n++) {
+		b = getc(inf);
+		if (b < 0) {
+			fprintf(stderr,
+			"error: getc() returned EOF contrary to st_size\n");
+			exit(1);
+		}
+		*dp++ = (b >> right_shift) & 1;
+	}
+	fclose(inf);
+}
+
+static int
+check_sync(pos)
+	unsigned pos;
+{
+	uint8_t *cand = filebuf + pos;
+	unsigned n;
+
+	for (n = 0; n < 8; n++) {
+		if (cand[n])
+			return 0;
+	}
+	if (!cand[8])
+		return 0;
+	if (cand[16])
+		return 0;
+	if (!cand[17])
+		return 0;
+	for (n = 3; n < 20; n++) {
+		if (!cand[n * 8])
+			return 0;
+	}
+	return 1;
+}
+
+static int
+bit_parity(bits, nbits)
+	ubit_t *bits;
+	unsigned nbits;
+{
+	unsigned n, sum;
+
+	sum = 0;
+	for (n = 0; n < nbits; n++)
+		sum += bits[n];
+	return sum & 1;
+}
+
+static void
+bits2bytes(bits, bytes, nbytes)
+	ubit_t *bits;
+	uint8_t *bytes;
+	unsigned nbytes;
+{
+	unsigned n, m, acc;
+
+	for (n = 0; n < nbytes; n++) {
+		acc = 0;
+		for (m = 0; m < 8; m++) {
+			acc <<= 1;
+			acc |= *bits++;
+		}
+		*bytes++ = acc;
+	}
+}
+
+static void
+process_frame(pos)
+	unsigned pos;
+{
+	ubit_t *frame = filebuf + pos;
+	ubit_t xc_bits[6], dbits[112];
+	uint8_t hr_bytes[14];
+	int16_t params[18];
+	int crc_stat;
+
+	printf("Frame at 0x%x:\n", pos);
+	printf("  C1-C4: %u%u%u%u OP %s\n", frame[9], frame[10], frame[11],
+		frame[12], bit_parity(frame + 9, 5) ? "good" : "bad");
+	bcopy(frame + 14, xc_bits, 2);
+	bcopy(frame + 18, xc_bits + 2, 4);
+	printf("  XC1-XC5: %u%u%u%u%u OP %s\n", xc_bits[0], xc_bits[1],
+		xc_bits[2], xc_bits[3], xc_bits[4],
+		bit_parity(xc_bits, 6) ? "good" : "bad");
+	bcopy(frame + 22, dbits, 2);
+	bcopy(frame + 25, dbits + 2, 7);
+	bcopy(frame + 33, dbits + 9, 7);
+	bcopy(frame + 41, dbits + 16, 7);
+	bcopy(frame + 49, dbits + 23, 7);
+	bcopy(frame + 57, dbits + 30, 7);
+	bcopy(frame + 65, dbits + 37, 7);
+	bcopy(frame + 76, dbits + 44, 4);
+	bcopy(frame + 81, dbits + 48, 7);
+	bcopy(frame + 89, dbits + 55, 7);
+	bcopy(frame + 97, dbits + 62, 7);
+	bcopy(frame + 105, dbits + 69, 7);
+	bcopy(frame + 113, dbits + 76, 7);
+	bcopy(frame + 121, dbits + 83, 7);
+	bcopy(frame + 129, dbits + 90, 7);
+	bcopy(frame + 137, dbits + 97, 7);
+	bcopy(frame + 145, dbits + 104, 7);
+	dbits[111] = frame[153];
+	crc_stat = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, dbits, 44,
+					   frame + 73);
+	printf("  CRC %s\n", crc_stat ? "bad" : "good");
+	bits2bytes(dbits, hr_bytes, 14);
+	gsmhr_unpack_ts101318(hr_bytes, params);
+	printf("  R0=%02x LPC=%03x,%03x,%02x Int=%x Mode=%x\n", params[0],
+		params[1], params[2], params[3], params[4], params[5]);
+	printf(params[5] ? "  s1=%02x,%03x,%02x" : "  s1=%02x,%02x,%02x",
+		params[6], params[7], params[8]);
+	printf(params[5] ? " s2=%x,%03x,%02x" : " s2=%02x,%02x,%02x",
+		params[9], params[10], params[11]);
+	printf(params[5] ? " s3=%x,%03x,%02x" : " s3=%02x,%02x,%02x",
+		params[12], params[13], params[14]);
+	printf(params[5] ? " s4=%x,%03x,%02x\n" : " s4=%02x,%02x,%02x\n",
+		params[15], params[16], params[17]);
+	printf("  C6-C9: %u%u%u%u\n", frame[154], frame[155], frame[156],
+		frame[157]);
+	printf("  T1=%u T2=%u\n", frame[158], frame[159]);
+}
+
+static void
+process_filebuf()
+{
+	unsigned p, endp;
+	int sync = 0, match;
+
+	endp = total_size - 160;
+	for (p = 0; p < endp; ) {
+		match = check_sync(p);
+		if (match != sync) {
+			printf("# %s frame sync at file offset 0x%x\n",
+				match ? "Acquired" : "Lost", p);
+		}
+		if (match) {
+			process_frame(p);
+			if (!filebuf[p+158] && !filebuf[p+159]) {
+				printf(
+			  "# both T bits equal 0, shifting frame alignment\n");
+				p += 158;
+			} else
+				p += 160;
+		} else
+			p++;
+		sync = match;
+	}
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s binfile subslot\n", argv[0]);
+		exit(1);
+	}
+	read_ts_file(argv[1], argv[2]);
+	process_filebuf();
+	exit(0);
+}