MBE: Support for SDRTrunk NXDN MBE files (WIP); #298

DMR: Clarify and Decode FSN Value on Data Headers; #350

NXDN: Fix errors on KS creation on AES discard bytes on voice application;

GCC: Fix Misc unused-but-set-variable warnings (GCC Version GNU 16.1.1);
This commit is contained in:
lwvmobile
2026-06-11 17:52:12 -04:00
parent f443063d3b
commit 8c3ae61b1a
7 changed files with 178 additions and 8 deletions

View File

@@ -293,8 +293,19 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
//Confirmed or Unconfirmed Data Packets Header
if (dpf == 2 || dpf == 3)
{
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
uint8_t fsn_number = (fsn & 7) + 1;
char fsn_string[50];
memset(fsn_string, 0, sizeof(fsn_string));
if (fsn == 0) sprintf(fsn_string, "%s", "Unconfirmed data single fragment");
else if (fsn == 8) sprintf(fsn_string, "%s", "Confirmed data single fragment");
else if (fsn >= 9) sprintf(fsn_string, "%s", "Last confirmed data fragment with number");
else sprintf(fsn_string, "%s", "Subsequent confirmed data fragment with number");
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, fsn, fsn_string, fsn_number);
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, s, ns, fsn, fsn_string, fsn_number);
state->data_header_blocks[slot] = bf;
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.

View File

@@ -2194,6 +2194,8 @@ void agsm (dsd_opts * opts, dsd_state * state, short * input, int len)
state->aout_gainA = coeff; //store for internal use
UNUSED(avg);
}
//until analog agc is fixed, going to use a manual gain control on this

View File

@@ -1345,6 +1345,18 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
state->lastsynctype = 10;
}
if (strncmp ("NXDN", str_buffer, 4) == 0)
{
//set AMBE+2 protocol here
protocol = 3;
//disable dmra (+7 on ks_idx)
is_dmra = 0;
state->synctype = 28;
state->lastsynctype = 28;
}
//open .imb or .amb file, if desired, but only after setting a synctype
if (state->synctype != -1)
{
@@ -1355,6 +1367,57 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
if (strncmp ("tag", str_buffer, 3) == 0)
{
str_buffer = strtok(NULL, "\n"); //copies to End of Line
//debug
// fprintf (stderr, " TAG: %s", str_buffer);
//NOTE: Nothing coded for SDRTrunk MBE files provides information regarding
//possible FACCH1 steals on voice frames, so keystrem index position here can
//become unsynced on some voice frames if trunking or other does FACCH1 steals
//when the system is really busy, or radio drops out. Writing code to read ahead
//would still not tell us if the FACCH1 steal was in vocoder socket 1 or 2
//NOTE: Above may become even more noticeable on DES or AES as those keystreams
//are generated for two voice superframes, and the decryption may become unsynced
//until the next VCALL_IV is read in from the MBE file.
//Check NXDN tag value, set the ks_idx to proper position
//by looking for SACCH_1, SACCH_2, SACCH_3, or SACCH_4 for scrambler
if (alg_id == 1 && protocol == 3)
{
if (strncmp ("SACCH 1", str_buffer+4, 7) == 0)
{
ks_idx = 0;
if (opts->payload == 1)
fprintf (stderr, "\n PF 1/4");
}
else if (strncmp ("SACCH 2", str_buffer+4, 7) == 0)
{
ks_idx = 49*4;
if (opts->payload == 1)
fprintf (stderr, "\n PF 2/4");
}
else if (strncmp ("SACCH 3", str_buffer+4, 7) == 0)
{
ks_idx = 49*8;
if (opts->payload == 1)
fprintf (stderr, "\n PF 3/4");
}
else if (strncmp ("SACCH 4", str_buffer+4, 7) == 0)
{
ks_idx = 49*12;
if (opts->payload == 1)
fprintf (stderr, "\n PF 4/4");
}
}
}
if (strncmp ("call_type", str_buffer, 9) == 0)
{
str_buffer = strtok(NULL, " : \""); //next value after any : "" string
@@ -1388,6 +1451,29 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
key_id = 0;
iv_hex = 0;
//NXDN Forced Scrambler
if (is_enc == 1 && state->forced_alg_id == 1 && protocol == 3)
{
alg_id = state->forced_alg_id;
//TEST: Key Load Forced Scrambler Key by TGT or SRC value?
if (state->forced_alg_id == 1 && state->keyloader == 1)
{
if (state->rkey_array[target] != 0)
state->R = target;
else if (state->rkey_array[source] != 0)
state->R = source;
}
if (alg_id == 1 && state->R != 0)
{
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
ks_available = 1;
}
//DES and AES still require the IV for KS creation
}
//debug set value
// fprintf (stderr, " ENC: %d;", is_enc);
@@ -1523,6 +1609,23 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
key_id = strtol (str_buffer, NULL, 10);
//NXDN Scrambler Setup
if (alg_id == 1 && protocol == 3)
{
state->payload_keyid = key_id;
//Scrambler Key Loading
if (state->keyloader == 1)
keyring(opts, state);
if (state->R != 0)
{
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
ks_available = 1;
}
}
//debug set value
if (opts->payload == 1)
fprintf (stderr, "\n Key ID: %04X;", key_id);
@@ -1708,6 +1811,29 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
//WIP: Add handling for NXDN DES and AES256
//NOTE: This hinges on where SDRTrunk places the IV for the call,
//ideally needs to be after the SACCH4 voice frames
else if (protocol == 3 && alg_id == 2 && state->R != 0)
{
//double check discards (seems same as in dsd_mbe)
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32);
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8);
ambe2_counter = 0;
ks_idx = 0;
ks_available = 1;
}
else if (protocol == 3 && alg_id == 3 && state->aes_key_loaded[0] == 1)
{
//TODO: All of This
// ambe2_counter = 0;
// ks_idx = 0;
}
//Pull request: https://github.com/DSheirer/sdrtrunk/pull/2273
//Merged into SDRTrunk nightly so should be able to handle this better now
@@ -1776,7 +1902,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
// fprintf (stderr, " Enc Play;");
}
else if (protocol == 2) //P25p2 AMBE
else if (protocol == 2) //P25p2 or DMR AMBE+2
{
//debug print current str_buffer
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
@@ -1801,6 +1927,37 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
}
else if (protocol == 3) //NXDN AMBE+2 (EHR)
{
//debug print current str_buffer
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
//18 hex characters on 'hex' which is the AMBE interleaved C codewords
ks_idx = ambe2_str_to_decode(opts, state, str_buffer, ks, ks_idx, is_dmra, is_enc, ks_available);
//debug
// if (is_enc == 1 && ks_available == 0)
// fprintf (stderr, " Enc Mute;");
// else if (is_enc == 1 && ks_available == 1)
// fprintf (stderr, " Enc Play;");
//increment AMBE+2 counter
ambe2_counter++;
//Really actually need the Structure Field (PF values here)
//reset if over for NXDN 16 AMBE+2 frames for Scrambler
if (alg_id == 1 && ambe2_counter == 16)
{
ambe2_counter = 0;
ks_idx = 0;
}
else if (alg_id > 1 && ambe2_counter == 32)
{
ambe2_counter = 0;
ks_idx = 0;
}
}
}
if (strncmp ("time", str_buffer, 4) == 0)

View File

@@ -535,7 +535,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->bit_counterL = 0;
//unpack the octets into a bit-wise keystream
unpack_byte_array_into_bit_array(state->ks_octetL+8, state->ks_bitstreamL, 15*8);
unpack_byte_array_into_bit_array(state->ks_octetL+16, state->ks_bitstreamL, 14*16);
//reset flag to 0
state->nxdn_new_iv = 0;
@@ -546,7 +546,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->bit_counterL > (1568-49))
state->bit_counterL = (1568-49);
//Keystream creation is currently inside of the NXDN_decode_VCALL_IV function
//Keystream Application (no +7 discard like DMRA)
for (i = 0; i < 49; i++)
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];

View File

@@ -1479,7 +1479,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits)
unsigned int syndromeI = 0; // syndrome index
int is = 0;
int i = 0;
int corrections = 0;
int corrections = 0; UNUSED(corrections);
for (is = 0; is < 9; is++)
{
syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0])

View File

@@ -111,7 +111,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//debug look at the "off bits" of the encoded lich, should be all 1's (8)
//disble this code if sync issues arise, this may not be ideal of marginal signal
uint8_t lich_off_hex = 0;
uint8_t lich_off_hex = 0; UNUSED(lich_off_hex);
for (int i=0; i<8; i++)
lich_off_hex += lich_bits[(i*2)+1];
#ifdef NXDN_LICH_OFFBITS

View File

@@ -57,7 +57,7 @@ int count_bits(uint8_t b, int slen)
int p25_12(uint8_t * input, uint8_t treturn[12])
{
int i, t;
int total_err = 0;
int total_err = 0; UNUSED(total_err);
uint8_t deinterleaved_dibits[98];
memset(deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));