# HG changeset patch # User Mychaela Falconia # Date 1763657541 0 # Node ID 625be4b2922f4b6266cd83aed8fa978b2c317023 # Parent a1868d31ce7fd9dbf82aa3e0ff8b61358a7ee850 trau-decode: start framework for TRAU-AMR-8k decoding diff -r a1868d31ce7f -r 625be4b2922f .hgignore --- a/.hgignore Wed Mar 19 01:39:57 2025 +0000 +++ b/.hgignore Thu Nov 20 16:52:21 2025 +0000 @@ -19,6 +19,7 @@ ^trau-decode/dump-1bit$ ^trau-decode/tfo-parse-fr16$ +^trau-decode/trau-amr8-dump-hex$ ^trau-decode/trau-extr$ ^trau-decode/trau-hr-dump$ ^trau-decode/trau-hr-dump-hex$ diff -r a1868d31ce7f -r 625be4b2922f trau-decode/Makefile --- a/trau-decode/Makefile Wed Mar 19 01:39:57 2025 +0000 +++ b/trau-decode/Makefile Thu Nov 20 16:52:21 2025 +0000 @@ -1,7 +1,7 @@ CC= gcc CFLAGS= -O2 -PROGS= dump-1bit tfo-parse-fr16 trau-extr trau-hr-dump trau-hr-dump-hex \ - trau-parse trau-parse-hex trau-sync8 +PROGS= dump-1bit tfo-parse-fr16 trau-amr8-dump-hex trau-extr trau-hr-dump \ + trau-hr-dump-hex trau-parse trau-parse-hex trau-sync8 FR_OBJS=parse-fr.o parse-fr-common.o parse-efr.o HR_OBJS=gsmhr_unpack.o hr-guts.o @@ -16,6 +16,9 @@ tfo-parse-fr16: crc8gen.o parse-tfo16.o ${FR_OBJS} ${CC} ${CFLAGS} -o $@ $^ -lgsmfr2 -lgsmefr +trau-amr8-dump-hex: amr8-common.o amr8-readhex.o crc8gen.o + ${CC} ${CFLAGS} -o $@ $^ + trau-extr: extr-fr.o extr-efr.o extr-main.o ${CC} ${CFLAGS} -o $@ $^ -lgsmfr2 diff -r a1868d31ce7f -r 625be4b2922f trau-decode/amr8-common.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trau-decode/amr8-common.c Thu Nov 20 16:52:21 2025 +0000 @@ -0,0 +1,214 @@ +/* + * Here we implement parsing/decoding of TRAU-AMR-8k frames per 3GPP TS 48.061, + * striving to decode and display as much as we can. + */ + +#include +#include +#include +#include +#include +#include +#include "osmo_bits.h" + +/* + * AMR TRAU parity (same as EFR) + * + * g(x) = x^3 + x^1 + 1 + */ +static const struct osmo_crc8gen_code gsm0860_amr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +static int saved_mode, saved_mode_valid; + +static char *fclass_names[4] = { + "No_Speech", "Speech_Bad", "Speech_Degraded", "Speech_Good" +}; + +static char *nospch_class_names[8] = { + "No_Data", "invalid", "invalid", "invalid", + "Sid_Bad", "Sid_Update", "Onset", "Sid_First" +}; + +static const uint8_t params_lsf_475_515[] = {8, 8, 7, 0}; +static const uint8_t params_lsf_59_67_74[] = {8, 9, 9, 0}; + +static const uint8_t params_475_sf1[] = {8, 7, 2, 8, 0}; +static const uint8_t params_475_sf3[] = {4, 7, 2, 8, 0}; +static const uint8_t params_475_sf24[] = {4, 7, 2, 0}; + +static const uint8_t params_515_sf1[] = {8, 7, 2, 6, 0}; +static const uint8_t params_515_sf234[] = {4, 7, 2, 6, 0}; + +static const uint8_t params_59_sf13[] = {8, 9, 2, 6, 0}; +static const uint8_t params_59_sf24[] = {4, 9, 2, 6, 0}; + +static const uint8_t params_67_sf13[] = {8, 11, 3, 7, 0}; +static const uint8_t params_67_sf24[] = {4, 11, 3, 7, 0}; + +static const uint8_t params_74_sf13[] = {8, 13, 4, 7, 0}; +static const uint8_t params_74_sf24[] = {5, 13, 4, 7, 0}; + +static const uint8_t params_sid[] = {3, 8, 9, 9, 6, 0}; + +static const ubit_t bit8_0[8] = { 0, }; + +/*!< 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 += 8) { + 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] != 1) + return false; + if (bits[8] != 0) + return false; + if (bits[16] != 1) + return false; + if (bits[24] != 0) + return false; + + return true; +} + +static unsigned +bits_to_num(bits, nbits) + ubit_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 +print_generic_params(bits, ltab) + ubit_t *bits; + uint8_t *ltab; +{ + uint8_t *tp; + unsigned n, p; + + for (tp = ltab; n = *tp; tp++) { + p = bits_to_num(bits, n); + bits += n; + printf(" %u", p); + } +} + +static void +print_speech_params(bits, ltab) + ubit_t *bits; + uint8_t *ltab; +{ + fputs(" ", stdout); + print_generic_params(bits, ltab); + putchar('\n'); +} + +static void +check_spare_bits(bits, nbits, desc) + ubit_t *bits; + unsigned nbits; + char *desc; +{ + while (nbits) { + if (*bits == 0) + break; + bits++; + nbits--; + } + if (!nbits) + return; + printf(" Extra data in bits %s\n", desc); +} + +static void +handle_amr8_low(frame_bits) + ubit_t *frame_bits; +{ + puts(" TRAU-AMR-8k Low format, decoding to be implemented"); +} + +static void +handle_amr8_6k7(frame_bits) + ubit_t *frame_bits; +{ + puts(" TRAU-AMR-8k MR67 format, decoding to be implemented"); +} + +static void +handle_amr8_7k4(frame_bits) + ubit_t *frame_bits; +{ + puts(" TRAU-AMR-8k MR74 format, decoding to be implemented"); +} + +void +print_amr8_frame(frame_bits) + ubit_t *frame_bits; +{ + if (is_amr_low(frame_bits)) + handle_amr8_low(frame_bits); + else if (is_amr_67(frame_bits)) + handle_amr8_6k7(frame_bits); + else if (is_amr_74(frame_bits)) + handle_amr8_7k4(frame_bits); + else + puts(" Unrecognized format by sync pattern"); +} diff -r a1868d31ce7f -r 625be4b2922f trau-decode/amr8-readhex.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trau-decode/amr8-readhex.c Thu Nov 20 16:52:21 2025 +0000 @@ -0,0 +1,106 @@ +/* + * This program reads a line-based text file in an ad hoc hex format + * where each line represents a TRAU-AMR-8k frame. These TRAU frames + * are then decoded. + */ + +#include +#include +#include +#include +#include +#include + +static int +decode_hex_digit(c) +{ + if (isdigit(c)) + return c - '0'; + else if (isupper(c)) + return c - 'A' + 10; + else + return c - 'a' + 10; +} + +static void +hex2bits(hex, bits) + char *hex; + uint8_t *bits; +{ + unsigned n, m, x; + + for (n = 0; n < 40; n++) { + x = decode_hex_digit(hex[n]); + for (m = 8; m; m >>= 1) { + if (x & m) + *bits++ = 1; + else + *bits++ = 0; + } + } +} + +static void +process_record(hex_frame, lineno) + char *hex_frame; +{ + uint8_t frame_bits[160]; + + printf("line %d: %s\n", lineno, hex_frame); + hex2bits(hex_frame, frame_bits); + print_amr8_frame(frame_bits); +} + +static void +process_line(linebuf, filename, lineno) + char *linebuf, *filename; +{ + char *cp, *np; + unsigned n; + + for (cp = linebuf; isspace(*cp); cp++) + ; + if (*cp == '\0' || *cp == '#') + return; + np = cp; + for (n = 0; n < 40; n++) { + if (!isxdigit(*cp)) + goto inv; + cp++; + } + if (*cp) { + if (!isspace(*cp)) + goto inv; + *cp++ = '\0'; + } + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv; + process_record(np, lineno); + return; + +inv: fprintf(stderr, "%s line %d: invalid syntax\n", filename, lineno); + exit(1); +} + +main(argc, argv) + char **argv; +{ + FILE *inf; + char linebuf[128]; + int lineno; + + if (argc != 2) { + fprintf(stderr, "usage: %s hex-file\n", argv[0]); + exit(1); + } + inf = fopen(argv[1], "r"); + if (!inf) { + perror(argv[1]); + exit(1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) + process_line(linebuf, argv[1], lineno); + exit(0); +}