diff --git a/src/ipsc_const.h b/src/ipsc_const.h index f6bc7dd..3ccbb0a 100644 --- a/src/ipsc_const.h +++ b/src/ipsc_const.h @@ -57,6 +57,13 @@ #define GV_BURST_TYPE_OFF 30 #define GV_PAYLOAD_OFF 31 #define GV_MIN_LEN 31 +#define GV_AMBE_OFF 33 /* 19-byte AMBE field: three 49-bit frames at bits 0, 50, 100 */ +/* Repeater-set "bad voice" flags, one per AMBE frame (uncorrectable on RF receive): + * frame 1 = data[32] bit 0; frames 2/3 = bits 49/99 of the AMBE field. */ +#define GV_BAD1_OFF 32 +#define GV_BAD1_MSK 0x01 +#define GV_BAD2_BIT 49 +#define GV_BAD3_BIT 99 #define GV_BE_FLAG 0x16 /* data[32] value identifying burst E (carries reassembled LC repeat) */ #define GV_BE_LC_FLCO_OFF 56 /* FLCO of the reassembled LC repeat carried only on burst E */ #define FLCO_GROUP 0x00 /* Group Voice Channel User (real call identity); 0x04-0x08 = TA/GPS */ diff --git a/src/translate.c b/src/translate.c index 8778c54..723b13c 100644 --- a/src/translate.c +++ b/src/translate.c @@ -102,6 +102,10 @@ static void rand4(uint8_t out[4]) if (getrandom(out, 4, 0) != 4) { out[0]=rand(); out[1]=rand(); out[2]=rand(); out[3]=rand(); } } +/* 72-bit AMBE silence frame from MMDVMHost DMR_SILENCE_DATA; replaces frames the + * repeater flagged bad (IPSC->HBP). */ +static const uint8_t AMBE_SILENCE_MMDVM[9] = {0xB9,0xE8,0x81,0x52,0x61,0x73,0x00,0x2A,0x6B}; + /* precompute 19-byte IPSC AMBE payload of three silence frames */ static void make_ambe_silence(uint8_t out[19]) { @@ -424,11 +428,16 @@ void translator_ipsc_voice_received(translator *tr, const uint8_t *data, int len * bytes on a TA superframe); all AMBE is forwarded under the locked identity. */ if (len < 52) { LOGW(LOGN, "SLOT_VOICE too short for AMBE: %d bytes", len); return; } - dmr_bit raw[152]; dmr_bytes_to_bits(data + 33, 19, raw); + dmr_bit raw[152]; dmr_bytes_to_bits(data + GV_AMBE_OFF, 19, raw); dmr_bit a1_72[72], a2_72[72], a3_72[72]; - dmr_ambe_49_to_72(raw + 0, a1_72); - dmr_ambe_49_to_72(raw + 50, a2_72); - dmr_ambe_49_to_72(raw + 100, a3_72); + dmr_bit *a72[3] = {a1_72, a2_72, a3_72}; + int bad[3] = {data[GV_BAD1_OFF] & GV_BAD1_MSK, raw[GV_BAD2_BIT], raw[GV_BAD3_BIT]}; + for (int i = 0; i < 3; i++) { + if (bad[i]) dmr_bytes_to_bits(AMBE_SILENCE_MMDVM, 9, a72[i]); + else dmr_ambe_49_to_72(raw + 50 * i, a72[i]); + } + if (bad[0] || bad[1] || bad[2]) + LOGD(LOGN, "IPSC bad AMBE ts=%d flags=%d%d%d — sent as silence", ts, bad[0], bad[1], bad[2]); int pos = tr->out_frame_pos[ts] % 6; dmr_bit embed[48]; build_embed(tr, ts, pos, embed); dmr_bit fb[264]; int at=0; diff --git a/tests/parity_in.txt b/tests/parity_in.txt index ff128c5..5e48fe3 100644 --- a/tests/parity_in.txt +++ b/tests/parity_in.txt @@ -1,16 +1,16 @@ 1 1 800004c317052f9b8100005b02000043e200000000000000100000000000010000000000000000000000000000000000000000000000 -1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a000000070e151c232a31383f464d545b626970777e0000 -1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a0000858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030000 -1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a00000a11181f262d343b424950575e656c737a81880000 +1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a000000070e151c232a31383f464d445b626970777e0000 +1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a0000858c939aa1a8afb6bdc4cbd2c9e0e7eef5fc030000 +1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a00000a11181f262d343b424950574e656c737a81880000 1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a00008f969da4abb2b9c0c7ced5dce3eaf1f8ff060d0000 1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a0016141b222930373e454c535a61686f767d848b920000 -1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a000099a0a7aeb5bcc3cad1d8dfe6edf4fb020910170000 +1 10 800004c317052f9b8100005b02000043e2000000000000001000000000000a000099a0a7aeb5bc83cad1d8dfe6edf4fb020910170000 1 2 800004c317052f9b8100005b02000043e200000000000000100000000000020000000000000000000000000000000000000000000000 1 1 800004c317102f9be500000902000043e200000000000000100000000000010000000000000000000000000000000000000000000000 -1 10 800004c317112f9be500000902000043e2000000000000001000000000000a000000050a0f14191e23282d32373c41464b50555a0000 +1 10 800004c317112f9be500000902000043e2000000000000001000000000000a000000050a0f14191e23282d32372c41464b50555a0000 1 10 800004c317112f9be500000902000043e2000000000000001000000000000a000073787d82878c91969ba0a5aaafb4b9bec3c8cd0000 1 10 800004c317122f9be500000902000043e2000000000000001000000000000a0000e6ebf0f5faff04090e13181d22272c31363b400000 -1 10 800004c317122f9be500000902000043e2000000000000001000000000000a0000595e63686d72777c81868b90959a9fa4a9aeb30000 -1 10 800004c317132f9be500000902000043e2000000000000001000000000000a0016ccd1d6dbe0e5eaeff4f9fe03080d12171c21260000 -1 10 800004c317132f9be500000902000043e2000000000000001000000000000a00003f44494e53585d62676c71767b80858a8f94990000 +1 10 800004c317122f9be500000902000043e2000000000000001000000000000a0000595e63686d72377c81868b90859a9fa4a9aeb30000 +1 10 800004c317132f9be500000902000043e2000000000000001000000000000a0016ccd1d6dbe0e5aaeff4f9fe03080d12171c21260000 +1 10 800004c317132f9be500000902000043e2000000000000001000000000000a00003f44494e53581d62676c71766b80858a8f94990000 1 2 800004c317132f9be500000902000043e200000000000000100000000000020000000000000000000000000000000000000000000000 diff --git a/tests/test_parity.c b/tests/test_parity.c index afb256e..6dbe036 100644 --- a/tests/test_parity.c +++ b/tests/test_parity.c @@ -8,6 +8,7 @@ #include "../src/translate.h" #include "../src/config.h" #include "../src/eventloop.h" +#include "../src/dmr/dmr.h" /* ---- captured DMRD output ---- */ #define MAXCAP 64 @@ -114,7 +115,47 @@ int main(void) } if (fi) fclose(fi); if (fr) fclose(fr); + translator_free(tr); + + /* ---- bad-voice flags (IPSC->HBP): flagged AMBE frames become MMDVMHost silence ---- */ + static const uint8_t SIL[9] = {0xB9,0xE8,0x81,0x52,0x61,0x73,0x00,0x2A,0x6B}; + static const int want[2][3] = {{1,1,1}, {0,1,0}}; /* per voice frame: flagged? */ + int bad_fails = 0; + tr = translator_new(&cfg, loop); + translator_set_protocols(tr, (struct ipsc *)1, (struct hbp *)1); + ncap = 0; + fi = fopen("tests/parity_in.txt", "r"); + for (int k = 0; k < 3 && fi && fgets(line, sizeof line, fi); k++) { + int ts, bt; char hex[400]; + if (sscanf(line, "%d %d %399s", &ts, &bt, hex) != 3) continue; + uint8_t raw[400]; int n = unhex(hex, raw); + if (k >= 1) { + const int *w = want[k - 1]; + if (w[0]) raw[32] |= 0x01; + if (w[1]) raw[33 + 49 / 8] |= (uint8_t)(0x80 >> (49 % 8)); + if (w[2]) raw[33 + 99 / 8] |= (uint8_t)(0x80 >> (99 % 8)); + } + translator_ipsc_voice_received(tr, raw, n, ts, bt); + } + if (fi) fclose(fi); + if (ncap != 3) { fprintf(stderr, "FAIL bad-voice: expected 3 DMRD, got %d\n", ncap); bad_fails++; } + for (int v = 0; v < 2 && v + 1 < ncap; v++) { + dmr_bit b[264]; dmr_bytes_to_bits(cap[v + 1] + 20, 33, b); + dmr_bit f[3][72]; + memcpy(f[0], b, 72); + memcpy(f[1], b + 72, 36); memcpy(f[1] + 36, b + 156, 36); + memcpy(f[2], b + 192, 72); + dmr_bit s[72]; dmr_bytes_to_bits(SIL, 9, s); + for (int i = 0; i < 3; i++) { + int is_sil = memcmp(f[i], s, sizeof s) == 0; + if (is_sil != want[v][i]) { + fprintf(stderr, "FAIL bad-voice: voice %d frame %d silence=%d, want %d\n", v, i + 1, is_sil, want[v][i]); + bad_fails++; + } + } + } + printf("Bad-voice flags (IPSC->HBP): %d failures\n", bad_fails); translator_free(tr); ev_free(loop); - return (fails || in_fails) ? 1 : 0; + return (fails || in_fails || bad_fails) ? 1 : 0; }