view trau-decode/parse-new16.c @ 109:382a80f91149

trau-parse-n: add support for 125 us advance
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 22 Dec 2025 05:25:15 +0000
parents f2009a354444
children
line wrap: on
line source

/*
 * This program reads a 64 kbit/s timeslot recording file, examines one
 * of the four 16 kbit/s subslots (selected), looks for GSM 08.60 TRAU
 * frames, and dumps whatever it finds.  The present version is a new one,
 * put together for the purpose of reverse-engineering Abis output from
 * Nokia BTS, which exhibits more complex behaviour than what our original
 * trau-parse program can analyze properly.
 */

#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>

static uint8_t *filebuf;
static unsigned total_size;

static enum {
	NOT_D144,
	D144_SYNC,
	D144_EDATA,
} d144_state;

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 > 3) {
		fprintf(stderr, "error: invalid subslot argument\n");
		exit(1);
	}
	right_shift = (3 - subslot) * 2;
	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) & 3;
	}
	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] & 2))
		return 0;
	if (cand[8] == 3 && cand[9] == 3 && cand[10] == 3 &&
	    (d144_state == D144_SYNC || d144_state == D144_EDATA))
		return 1;
	for (n = 2; n < 20; n++) {
		if (!(cand[n * 8] & 2))
			return 0;
	}
	return 1;
}

static void
unpack_dibits(in_frame_2bit, frame_bits)
	uint8_t *in_frame_2bit, *frame_bits;
{
	int i, inb;
	uint8_t *op;

	op = frame_bits;
	for (i = 0; i < 160; i++) {
		inb = in_frame_2bit[i];
		if (inb & 2)
			*op++ = 1;
		else
			*op++ = 0;
		if (inb & 1)
			*op++ = 1;
		else
			*op++ = 0;
	}
}

static unsigned
bits_to_num(bits, nbits)
	uint8_t *bits;
	unsigned nbits;
{
	unsigned accum;
	unsigned n;

	accum = 0;
	for (n = 0; n < nbits; n++) {
		accum <<= 1;
		if (*bits)
			accum |= 1;
		bits++;
	}
	return accum;
}

static void
dump_raw_nibbles(dibits, num_dibits)
	uint8_t *dibits;
	unsigned num_dibits;
{
	uint8_t *sp = dibits;
	unsigned remain = num_dibits;

	if (remain >= 2) {
		while (remain >= 2) {
			printf("%x", (sp[0] << 2) | sp[1]);
			sp += 2;
			remain -= 2;
		}
		putchar('\n');
	}
	if (remain)
		printf("dribble %u%u\n", (*sp & 2) >> 1, (*sp & 1) >> 0);
}

static int
process_frame(pos)
	unsigned pos;
{
	uint8_t *frame_2b = filebuf + pos;
	uint8_t frame_bits[320];
	unsigned c1_5, c6_11;

	printf("Frame at 0x%x:\n", pos);
	dump_raw_nibbles(frame_2b, 160);
	unpack_dibits(frame_2b, frame_bits);
	printf("  C1-C5: %u%u%u%u%u", frame_bits[17], frame_bits[18],
		frame_bits[19], frame_bits[20], frame_bits[21]);
	c1_5 = bits_to_num(frame_bits + 17, 5);
	switch (c1_5) {
	case 0x02:
		fputs(" (FR UL)", stdout);
		break;
	case 0x1C:
		fputs(" (FR DL)", stdout);
		break;
	case 0x1A:
		fputs(" (EFR)", stdout);
		break;
	case 0x10:
		fputs(" (idle UL)", stdout);
		break;
	case 0x0E:
		fputs(" (idle DL)", stdout);
		break;
	case 0x08:
		fputs(" (data UL)", stdout);
		break;
	case 0x16:
		fputs(" (data DL)", stdout);
		break;
	case 0x09:
		fputs(" (HR data UL)", stdout);
		break;
	case 0x17:
		fputs(" (HR data DL)", stdout);
		break;
	case 0x14:
		fputs(" (D144 sync)", stdout);
		break;
	case 0x1F:
		fputs(" (E-TRAU)", stdout);
		break;
	case 0x06:
		fputs(" (AMR)", stdout);
		break;
	}
	putchar('\n');
	if (c1_5 == 0x14)
		d144_state = D144_SYNC;
	else if (c1_5 == 0x1F &&
		 (d144_state == D144_SYNC || d144_state == D144_EDATA))
		d144_state = D144_EDATA;
	else
		d144_state = NOT_D144;
	switch (c1_5) {
	case 0x02:
	case 0x1C:
	case 0x1A:
	case 0x10:
	case 0x0E:
		c6_11 = bits_to_num(frame_bits + 22, 6);
		printf("  C6-C11: %u\n", c6_11);
		printf("  C12=%u C13=%u C14=%u C15=%u\n", frame_bits[28],
			frame_bits[29], frame_bits[30], frame_bits[31]);
		print_fr_efr_frame(frame_bits, c1_5);
		printf("  C16=%u C17=%u C18=%u C19=%u C20=%u C21=%u\n",
			frame_bits[310], frame_bits[311], frame_bits[312],
			frame_bits[313], frame_bits[314], frame_bits[315]);
		printf("  T1=%u T2=%u T3=%u T4=%u\n", frame_bits[316],
			frame_bits[317], frame_bits[318], frame_bits[319]);
		return 1;
	case 0x08:
	case 0x09:
	case 0x16:
	case 0x17:
	case 0x14:
		print_data_frame(frame_bits);
		return 0;
	case 0x1F:
		print_edata_frame(frame_bits);
		return 0;
	case 0x06:
		print_amr_frame(frame_bits);
		return 1;
	default:
		printf("  C6-C15: %u%u%u%u%u%u%u%u%u%u\n", frame_bits[22],
			frame_bits[23], frame_bits[24], frame_bits[25],
			frame_bits[26], frame_bits[27], frame_bits[28],
			frame_bits[29], frame_bits[30], frame_bits[31]);
		printf("  C16=%u C17=%u C18=%u C19=%u C20=%u C21=%u\n",
			frame_bits[310], frame_bits[311], frame_bits[312],
			frame_bits[313], frame_bits[314], frame_bits[315]);
		printf("  T1=%u T2=%u T3=%u T4=%u\n", frame_bits[316],
			frame_bits[317], frame_bits[318], frame_bits[319]);
		return 0;
	}
}

static void
process_filebuf()
{
	unsigned p, nf_pos = 0, nf_accum = 0;
	int match, have_t_bits;

	d144_state = NOT_D144;
	for (p = 0; p < total_size; ) {
		if ((total_size - p) >= 160)
			match = check_sync(p);
		else
			match = 0;
		if (match) {
			if (nf_accum) {
				printf("Non-frame at 0x%x:\n", nf_pos);
				dump_raw_nibbles(filebuf + nf_pos, nf_accum);
			}
			have_t_bits = process_frame(p);
			if (have_t_bits && filebuf[p+158] == 0 &&
			    filebuf[p+159] == 0) {
				puts("advance of 250 us");
				p += 158;
			} else if (have_t_bits && filebuf[p+158] == 3 &&
				   filebuf[p+159] == 0) {
				puts("advance of 125 us");
				p += 159;
			} else
				p += 160;
			nf_pos = p;
			nf_accum = 0;
		} else {
			p++;
			nf_accum++;
			if (nf_accum >= 160) {
				printf("Non-frame at 0x%x:\n", nf_pos);
				dump_raw_nibbles(filebuf + nf_pos, nf_accum);
				nf_pos = p;
				nf_accum = 0;
			}
			d144_state = NOT_D144;
		}
	}
	if (nf_accum) {
		printf("Non-frame at 0x%x:\n", nf_pos);
		dump_raw_nibbles(filebuf + nf_pos, nf_accum);
	}
}

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);
}