Files
dsd-fme/src/dmr_block.c
2025-02-22 22:50:53 -05:00

1135 lines
48 KiB
C

/*-------------------------------------------------------------------------------
* dmr_block.c
* DMR Data Header and Data Block Assembly/Handling
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//hopefully a more simplified (or logical) version...once you get past all the variables
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{
uint8_t slot = state->currentslot;
//clear out unified pdu 'superframe' slot
for (int i = 0; i < 24*127; i++) state->dmr_pdu_sf[slot][i] = 0;
//reset block counter to 1
state->data_block_counter[slot] = 1;
if (IrrecoverableErrors == 0 && CRCCorrect == 1) //&&CRCCorrect == 1
{
uint8_t gi = dheader_bits[0]; //group or individual data
uint8_t a = dheader_bits[1]; //response requested flag
uint8_t ab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended blocks
uint8_t dpf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[4], 4); //data packet format
uint8_t sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //service access point
uint8_t mpoc = dheader_bits[3]; //most significant bit of the 5-bit Pad Octet Count
uint8_t poc = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4) + (mpoc << 4); //padding octets
UNUSED(ab);
uint32_t target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[16], 24); //destination llid
uint32_t source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[40], 24); //source llid
//extra tgt/src handling for XPT
uint8_t target_hash[24];
uint8_t tg_hash = 0;
uint8_t is_xpt = 0;
//set flag if XPT
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
//truncate tgt/src to 16-bit values if XPT
if (is_xpt == 1)
{
target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16); //destination llid
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16); //source llid
if (gi == 0)
{
for (int i = 0; i < 16; i++) target_hash[i] = dheader_bits[24+i];
tg_hash = crc8 (target_hash, 16);
}
}
uint8_t is_cap = 0;
if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0) is_cap = 1;
if (is_cap)
{
//truncate tg on group? or just on private/individual data?
if (gi == 0) target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16);
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16);
}
//store source for dmr pdu packet handling (lrrp) when not available in completed message
if (dpf != 15) state->dmr_lrrp_source[slot] = source;
//store number of padding octets in a header to be used
if (dpf != 15) state->data_block_poc[slot] = poc;
//set dpf to storage for later use (UDT, SD, etc)
state->data_header_format[slot] = dpf;
//Type Strings
char sap_string[20];
char mfid_string[20];
char udtf_string[20];
char sddd_string[20];
sprintf (sap_string, "%s", "");
sprintf (mfid_string, "%s", "");
sprintf (udtf_string, "%s", "");
sprintf (sddd_string, "%s", "");
//see 9.3 - ETSI TS 102 361-1 V2.5.1 (2017-10) for more info
uint8_t f = dheader_bits[64]; //F -- Full message flag (F)
uint8_t bf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[65], 7); //Blocks to Follow (BF)
//confirmed data header
uint8_t s = dheader_bits[72]; //S -- Re-Synchronize Flag
uint8_t ns = (uint8_t)ConvertBitIntoBytes(&dheader_bits[73], 3); //N(S) -- send sequence number
uint8_t fsn = (uint8_t)ConvertBitIntoBytes(&dheader_bits[76], 4); //Fragment Sequence Number (FSN)
//response header
uint8_t r_class = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 2);
uint8_t r_type = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 3);
uint8_t r_status = (uint8_t)ConvertBitIntoBytes(&dheader_bits[77], 3);
//short data - status/precoded
uint8_t s_ab_msb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended block, msb
uint8_t s_ab_lsb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);//appended block, lsb
uint8_t s_ab_fin = (s_ab_msb << 2) | s_ab_lsb; //appended blocks, final value
uint8_t s_source_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 3);
uint8_t s_dest_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[67], 3);
uint8_t s_status_precoded = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 10);
//short data - raw
uint8_t sd_sarq = dheader_bits[70]; //Selective Automatic Repeat reQuest
uint8_t sd_f = dheader_bits[71]; //full message flag
uint8_t sd_bp = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 8); //bit padding
//short data - defined
uint8_t dd_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 6);
//Unified Data Transport (UDT)
uint8_t udt_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);
uint8_t udt_padnib = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 5);
uint8_t udt_uab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 2); //udt appended blocks
uint8_t udt_sf = dheader_bits[72];
uint8_t udt_pf = dheader_bits[73];
uint8_t udt_op = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 6);
//ETSI TS 102 361-4 V1.12.1 (2023-07) p281
udt_uab += 1; //add 1 internally, up to 4 appended blocks are carried, min is 1
//NMEA Specific Fix for unspecified MFID format w/ 2 appended blocks (UAB 2) p291
if (udt_uab == 0x5 && udt_uab == 3)
udt_uab = 2; //set to two if long unspecified format
//Note: NMEA Reserved value UAB 3 is not referenced in ETSI, so unknown number of
//appended blocks but we will assume it is supposed to be 4 appended blocks
//p_head
uint8_t p_sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[0], 4);
uint8_t p_mfid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8);
fprintf (stderr, "%s ", KGRN);
fprintf (stderr, "\n");
fprintf (stderr, " Slot %d Data Header - ", slot+1);
if (gi == 1 && dpf != 15) fprintf(stderr, "Group - ");
if (gi == 0 && dpf != 15) fprintf(stderr, "Indiv - ");
if (dpf == 0) fprintf (stderr, "Unified Data Transport (UDT) ");
else if (dpf == 1) fprintf (stderr, "Response Packet ");
else if (dpf == 2) fprintf (stderr, "Unconfirmed Delivery ");
else if (dpf == 3) fprintf (stderr, "Confirmed Delivery ");
else if (dpf == 13) fprintf (stderr, "Short Data: Defined ");
else if (dpf == 14) fprintf (stderr, "Short Data: Raw or S/P ");
// else if (dpf == 15) fprintf (stderr, "Proprietary Packet Data");
else if (dpf == 15) fprintf (stderr, "Extended"); //
else fprintf (stderr, "Reserved/Unknown DPF %X ", dpf);
if (a == 1 && dpf != 15) fprintf(stderr, "- Response Requested ");
if (dpf != 15) fprintf (stderr, "- Source: %d Target: %d ", source, target);
//include the hash value if this is an XPT and if its IND data
if (dpf != 15 && is_xpt == 1 && gi == 0) fprintf (stderr, "Hash: %d ", tg_hash);
//sap string handling
if (dpf == 15) sap = p_sap;
if (sap == 0) sprintf (sap_string, "%s", "UDT Data"); //apparently, both dpf and sap for UDT is 0
else if (sap == 2) sprintf (sap_string, "%s", "TCP Comp"); //TCP/IP header compression
else if (sap == 3) sprintf (sap_string, "%s", "UDP Comp"); //UDP/IP header compression
else if (sap == 4) sprintf (sap_string, "%s", "IP Based"); //IP based Packet Data
else if (sap == 5) sprintf (sap_string, "%s", "ARP Prot"); //Address Resoution Protocol (ARP)
else if (sap == 9) sprintf (sap_string, "%s", "EXTD HDR"); //Extended Header (Proprietary)
else if (sap == 10) sprintf (sap_string, "%s", "Short DT"); //Short Data
else if (sap == 1 && p_mfid == 0x10)
sprintf (sap_string, "%s", "Moto EXT"); //motorola extended format
else sprintf (sap_string, "%s", "Reserved"); //reserved, or err/unk
//mfid string handling
if (dpf == 15)
{
if (p_mfid == 0x10) sprintf (mfid_string, "%s", "Moto"); //could just also be a generic catch all for DMRA
else if (p_mfid == 0x58) sprintf (mfid_string, "%s", "Tait");
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
else sprintf (mfid_string, "%s", "Other");
}
//udt format string handling
if (dpf == 0)
{
if (udt_format == 0x00) sprintf (udtf_string, "%s", "Binary");
else if (udt_format == 0x01) sprintf (udtf_string, "%s", "MS/TG Adr");
else if (udt_format == 0x02) sprintf (udtf_string, "%s", "4-bit BCD");
else if (udt_format == 0x03) sprintf (udtf_string, "%s", "ISO7 Char");
else if (udt_format == 0x04) sprintf (udtf_string, "%s", "ISO8 Char");
else if (udt_format == 0x05) sprintf (udtf_string, "%s", "NMEA LOCN");
else if (udt_format == 0x06) sprintf (udtf_string, "%s", "IP Addr");
else if (udt_format == 0x07) sprintf (udtf_string, "%s", "UTF-16"); //16-bit Unicode Chars
else if (udt_format == 0x08) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
else if (udt_format == 0x09) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
else if (udt_format == 0x0A) sprintf (udtf_string, "%s", "Mixed UTF"); //Appended block contains addr and 16-bit UTF-16BE
else if (udt_format == 0x0B) sprintf (udtf_string, "%s", "LIP LOCN");
else sprintf (udtf_string, "%s", "Reserved");
}
//short data dd_head format string
if (dpf == 13)
{
if (dd_format == 0x00) sprintf (sddd_string, "%s", "Binary");
else if (dd_format == 0x01) sprintf (sddd_string, "%s", "BCD ");
else if (dd_format == 0x02) sprintf (sddd_string, "%s", "7-bit Char");
else if (dd_format == 0x03) sprintf (sddd_string, "%s", "IEC 8859-1");
else if (dd_format == 0x04) sprintf (sddd_string, "%s", "IEC 8859-2");
else if (dd_format == 0x05) sprintf (sddd_string, "%s", "IEC 8859-3");
else if (dd_format == 0x06) sprintf (sddd_string, "%s", "IEC 8859-4");
else if (dd_format == 0x07) sprintf (sddd_string, "%s", "IEC 8859-5");
else if (dd_format == 0x08) sprintf (sddd_string, "%s", "IEC 8859-6");
else if (dd_format == 0x09) sprintf (sddd_string, "%s", "IEC 8859-7");
else if (dd_format == 0x0A) sprintf (sddd_string, "%s", "IEC 8859-8");
else if (dd_format == 0x0B) sprintf (sddd_string, "%s", "IEC 8859-9");
else if (dd_format == 0x0C) sprintf (sddd_string, "%s", "IEC 8859-10");
else if (dd_format == 0x0D) sprintf (sddd_string, "%s", "IEC 8859-11");
else if (dd_format == 0x0E) sprintf (sddd_string, "%s", "IEC 8859-13"); //there is no 8059-12
else if (dd_format == 0x0F) sprintf (sddd_string, "%s", "IEC 8859-14");
else if (dd_format == 0x10) sprintf (sddd_string, "%s", "IEC 8859-15");
else if (dd_format == 0x11) sprintf (sddd_string, "%s", "IEC 8859-16");
else if (dd_format == 0x12) sprintf (sddd_string, "%s", "UTF-8 ");
else if (dd_format == 0x13) sprintf (sddd_string, "%s", "UTF-16 ");
else if (dd_format == 0x14) sprintf (sddd_string, "%s", "UTF-16BE");
else if (dd_format == 0x15) sprintf (sddd_string, "%s", "UTF-16LE");
else if (dd_format == 0x16) sprintf (sddd_string, "%s", "UTF-32 ");
else if (dd_format == 0x17) sprintf (sddd_string, "%s", "UTF-32BE");
else if (dd_format == 0x18) sprintf (sddd_string, "%s", "UTF-32LE");
else sprintf (sddd_string, "%s", "Reserved");
}
if (dpf == 0) //UDT
{
//UDT packet info -- samples needed for testing
//NOTE: This format's completed message has a CRC16 - like MBC - but has number of blocks (appended blocks) like R 1/2, etc.
fprintf (stderr, "\n SAP %02X [%s] - FMT %d [%s] - PDn %d - BLOCKS %d SF %d - PF %d OP %02X", sap, sap_string, udt_format, udtf_string, udt_padnib, udt_uab, udt_sf, udt_pf, udt_op);
//set number of blocks to follow (appended blocks) for block assembler
state->data_header_blocks[slot] = udt_uab;
//set data header to valid
state->data_header_valid[slot] = 1;
//reset block counter to zero
state->data_block_counter[slot] = 0;
//send to assembler as type 3, rearrange into CSBK type PDU (to verify), and send to dmr_cspdu
dmr_block_assembler (opts, state, dheader, 12, 0x0B, 3);
}
if (dpf == 1) //response data packet header
{
//mostly fleshed out response packet info
fprintf (stderr, "\n SAP %02X [%s] - Class %d - Type %0d - ", sap, sap_string, r_class, r_type);
if (r_class == 0 && r_type == 1) fprintf (stderr, "ACK - Success");
if (r_class == 1)
{
fprintf (stderr, "NACK - ");
if (r_type == 0) fprintf (stderr, "Illegal Format");
if (r_type == 1) fprintf (stderr, "Illegal Format");
if (r_type == 2) fprintf (stderr, "Packet CRC ERR");
if (r_type == 3) fprintf (stderr, "Memory Full");
if (r_type == 4) fprintf (stderr, "FSN Out of Seq");
if (r_type == 5) fprintf (stderr, "Undeliverable");
if (r_type == 6) fprintf (stderr, "PKT Out of Seq");
if (r_type == 7) fprintf (stderr, "Invalid User");
}
if (r_class == 2) fprintf (stderr, "SACK - Retry");
if (r_status) fprintf (stderr, " - %d", r_status); //the object/value of the ack/nack/sack
}
//Confirmed or Unconfirmed Data Packets Header
if (dpf == 2 || dpf == 3)
{
if (dpf == 2) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
if (dpf == 3) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
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.
}
//Short Data DD_Head (13), and R_Head or SP_Head (14)
if (dpf == 13 || dpf == 14)
{
//only set if not all zeroes
if (s_ab_fin) state->data_header_blocks[slot] = s_ab_fin;
//Short Data: Defined
if (dpf == 13) fprintf (stderr, "\n SD:D [DD_HEAD] - SAP %02d [%s] - BLOCKS %02d - DD %02X - PADb %d - FMT %02X [%s]", sap, sap_string, s_ab_fin, dd_format, sd_bp, dd_format, sddd_string);
//Short Data: Raw or S/P
if (dpf == 14)
{
//S/P has all appended block bits set to zero -- any other way to tell difference?
if (s_ab_fin == 0) fprintf (stderr, "\n SD:S/P [SP_HEAD] - SAP %02d [%s] - SP %02d - DP %02d - S/P %02X", sap, sap_string, s_source_port, s_dest_port, s_status_precoded);
//Raw
else fprintf (stderr, "\n SD:RAW [R_HEAD] - SAP %02d [%s] - BLOCKS %02d - SP %02d - DP %02d - SARQ %d - FMF %d - PDb %d", sap, sap_string, s_ab_fin, s_source_port, s_dest_port, sd_sarq, sd_f, sd_bp);
}
//6.2.2 The Response Requested (A) information element of the header shall be set to 0
//for unconfirmed data and shall be set to 1 for confirmed data. DD_HEAD, R_HEAD, or SP_HEAD (double check)
if (a == 1)
{
state->data_conf_data[slot] = 1;
fprintf (stderr, " - Confirmed Data");
}
}
//Proprietary Data Header
if (dpf == 15)
{
//The SAP found here is the actual SAP of the message (like a P25 ndary SAP, and can chain together according to ETSI)
fprintf (stderr, " - SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
//p_sap 1 on mfid 10 (moto) has been observed as the first block of LRRP data on a Moto system but doesn't pass the CRC for some reason (included or not included)
if (p_mfid == 0x10 && p_sap == 1)
{
//add the header to the first 12 bytes of the storage
for (uint8_t i = 0; i < 12; i++)
state->dmr_pdu_sf[slot][i] = dheader[i];
state->data_block_counter[slot]++;
state->data_byte_ctr[slot] += 12; //set current byte ptr to 12
state->data_p_head[slot] = 1;
}
else //if (p_sap != 1) //anything else
{
//sanity check to prevent segfault (this happened when the regular header was not received beforehand)
if (state->data_header_blocks[slot] > 1)
state->data_header_blocks[slot]--;
}
//Start Setting DMR Data Packet Encryption Variables
if (p_sap != 1 && p_mfid == 0x10)
{
//check ENC bit, assuming this is an ENC bit, or SVC OPT like thing (or could be an opcode for the rest of the extended header)
if ((uint8_t)ConvertBitIntoBytes(&dheader_bits[20], 4) == 1)
{
//set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later
if (state->currentslot == 0) state->dmr_so = 0x100;
else state->dmr_soR = 0x100;
}
fprintf (stderr, "\n PDU ENC Header:");
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
fprintf (stderr, " ENC: %X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[20], 4));
if (state->currentslot == 0)
state->payload_keyid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[24], 8);
else state->payload_keyidR = (uint8_t)ConvertBitIntoBytes(&dheader_bits[24], 8);
fprintf (stderr, " Key ID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[24], 8));
//this uses the same 3 bit method found in the 'late entry' alg
if (state->currentslot == 0) //could be 17,3
state->payload_algid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3);
else state->payload_algidR = (uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3);
fprintf (stderr, " ALG: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3));
if ((uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3) == 0) fprintf (stderr, " BP;");
if ((uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3) == 1) fprintf (stderr, " RC4;");
if ((uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3) == 2) fprintf (stderr, " DES56;");
if ((uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3) == 3) fprintf (stderr, " AES128;");
if ((uint8_t)ConvertBitIntoBytes(&dheader_bits[17], 3) == 4) fprintf (stderr, " AES256;");
if (state->currentslot == 0)
state->payload_mi = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 32);
else state->payload_miR = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 32);
//print MI only if this is not Moto BP (no MI on those)
if ((uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 32) != 0)
fprintf (stderr, " MI(32): %08X", (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 32));
}
else if (p_sap == 1 && p_mfid == 0x10)
{
//This can be an LRRP packet first block (but slightly different configuration)
fprintf (stderr, "\n Motorola Alternate Function Header; ");
}
else //if (p_mfid == 0x10)
{
fprintf (stderr, "\n Unknown Extended Header: ");
for (uint8_t i = 2; i < 10; i++)
fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&dheader_bits[0+(i*8)], 8));
}
//End Setting DMR Data Packet Encryption Variables
}
else //if (dpf != 15) //normal data header, we want to reset the enc states in case of any record needle drop playback occurrences
{
//reset alg/keyid/mi
if (state->currentslot == 0)
{
state->payload_mi = 0;
state->payload_algid = 0;
state->payload_keyid = 0;
state->dmr_so = 0;
}
else
{
state->payload_miR = 0;
state->payload_algidR = 0;
state->payload_keyidR = 0;
state->dmr_soR = 0;
}
}
//block storage sanity
if (state->data_header_blocks[slot] > 127) state->data_header_blocks[slot] = 127;
//assuming we didn't receive the initial data header block on a p_head and then decremented it
//3 or 4 seems to be the average safe value
if (state->data_header_blocks[slot] < 1) state->data_header_blocks[slot] = 1; //
//set data header validity unless its a p_head (should be set prior, if received)
if (dpf != 15) state->data_header_valid[slot] = 1;
// if (dpf != 15 && dpf != 1)
{
sprintf (state->dmr_lrrp_gps[slot], "Data Call - %s TGT: %d SRC: %d ", sap_string, target, source);
if (a == 1) strcat (state->dmr_lrrp_gps[slot], "- RSP REQ ");
}
//store SAP value
state->data_header_sap[slot] = sap;
} //End Irrecoverable Errors
if (IrrecoverableErrors != 0)
{
state->data_header_valid[slot] = 0;
sprintf (state->dmr_lrrp_gps[slot], "%s", "");
state->data_p_head[slot] = 0;
state->data_conf_data[slot] = 0;
state->data_block_counter[slot] = 1;
state->data_header_blocks[slot] = 1;
state->data_header_format[slot] = 7;
}
fprintf (stderr, "%s", KNRM);
}
void dmr_udt_decoder (dsd_opts * opts, dsd_state * state, uint8_t * block_bytes, uint32_t CRCCorrect)
{
//TODO: double check end rep values to Text Format Modes vs padnibs/uab values
//TODO: Make Text Format Modes seperate function to be used more generically by other things
//TODO: LIP Format (make seperate functions, LIP also used by USBD)
int i, j;
UNUSED(CRCCorrect);
UNUSED(opts);
int slot = state->currentslot;
uint8_t cs_bits[8*12*5]; //maximum of 1 header and 4 blocks at 96 bits
UNUSED(cs_bits);
//bytes to bits
for(i = 0, j = 0; i < 36; i++, j+=8)
{
cs_bits[j + 0] = (block_bytes[i] >> 7) & 0x01;
cs_bits[j + 1] = (block_bytes[i] >> 6) & 0x01;
cs_bits[j + 2] = (block_bytes[i] >> 5) & 0x01;
cs_bits[j + 3] = (block_bytes[i] >> 4) & 0x01;
cs_bits[j + 4] = (block_bytes[i] >> 3) & 0x01;
cs_bits[j + 5] = (block_bytes[i] >> 2) & 0x01;
cs_bits[j + 6] = (block_bytes[i] >> 1) & 0x01;
cs_bits[j + 7] = (block_bytes[i] >> 0) & 0x01;
}
//Unified Data Transport (UDT) -- already checked, but may need a few of these here as well
uint8_t udt_ig = cs_bits[0]; //group or individual data
uint8_t udt_a = cs_bits[1]; //response required
uint8_t udt_res = (uint8_t)ConvertBitIntoBytes(&cs_bits[2], 2);
uint8_t udt_format1 = (uint8_t)ConvertBitIntoBytes(&cs_bits[4], 4); //header format of UDT
uint8_t udt_sap = (uint8_t)ConvertBitIntoBytes(&cs_bits[8], 4);
uint8_t udt_format2 = (uint8_t)ConvertBitIntoBytes(&cs_bits[12], 4); //UDT Format referenced below
uint32_t udt_target = (uint32_t)ConvertBitIntoBytes(&cs_bits[16], 24);
uint32_t udt_source = (uint32_t)ConvertBitIntoBytes(&cs_bits[40], 24);
uint8_t udt_padnib = (uint8_t)ConvertBitIntoBytes(&cs_bits[64], 5);
uint8_t udt_zero = cs_bits[69]; //should always be zero?
uint8_t udt_uab = (uint8_t)ConvertBitIntoBytes(&cs_bits[70], 2) + 1; //udt appended blocks
uint8_t udt_sf = cs_bits[72];
uint8_t udt_pf = cs_bits[73];
uint8_t udt_op = (uint8_t)ConvertBitIntoBytes(&cs_bits[74], 6);
UNUSED4(udt_ig, udt_a, udt_res, udt_format1);
UNUSED4(udt_sap, udt_format2, udt_target, udt_source);
UNUSED4(udt_padnib, udt_zero, udt_sf, udt_pf);
UNUSED(udt_op);
//number of repititions required in various bit grabs
int end = 3; UNUSED(end);
//char strings
uint8_t iso7c;
uint8_t iso8c;
uint16_t utf16c;
//appended addresses -- max is 15 across 4 blocks
uint32_t address[16]; memset (address, 0, sizeof(address)); UNUSED(address);
uint8_t add_res = (uint8_t)ConvertBitIntoBytes(&cs_bits[96], 7); UNUSED(add_res);
uint8_t add_ok = cs_bits[103];
//BCD Format (Dialer Digits) -- max is 92 digits
uint8_t bcd_digits[93]; memset (bcd_digits, 0, sizeof(bcd_digits)); UNUSED(bcd_digits);
//NMEA Debug Testing (need real world samples)
// udt_format2 = 0x5;
// for (int i = 0; i < 8; i++) cs_bits[184+i] = 0; //spare bits to zero
// cs_bits[96] = 0; //enc bit to zero
// uint8_t test[96*4]; memset (test, 1, sizeof(test)); // all ones test vector
// nmea_iec_61162_1 (opts, state, test, udt_source, 1);
// nmea_iec_61162_1 (opts, state, cs_bits+96, udt_source, 2);
//LIP Debug Testing (need real world samples)
// udt_format2 = 0x0B;
//initial linebreak
fprintf (stderr, "%s", KCYN);
fprintf (stderr, "\n ");
fprintf (stderr, "Slot %d - SRC: %d; TGT: %d; UDT ", slot+1, udt_source, udt_target);
if (udt_format2 == 0x00)
{
fprintf (stderr, "Binary Data;");
}
else if (udt_format2 == 0x01) //appended addresses
{
fprintf (stderr, "Appended Addressing;\n ");
if (udt_uab == 1) end = 3;
if (udt_uab == 2) end = 7;
if (udt_uab == 3) end = 11;
if (udt_uab == 4) end = 15;
if (add_res) fprintf (stderr, "RES: %d; ", add_res);
fprintf (stderr, "OK: %d; ", add_ok);
fprintf (stderr, "ADDR:");
for (i = 0; i < end; i++)
{
fprintf (stderr, " %d;", (uint32_t)ConvertBitIntoBytes(&cs_bits[(i*24)+104], 24));
}
}
else if (udt_format2 == 0x02) //BCD 4-bit Dialer Digits
{
if (udt_uab == 1) end = 20;
if (udt_uab == 2) end = 44;
if (udt_uab == 3) end = 95;
if (udt_uab == 4) end = 92;
end -= udt_padnib; //subtract padnib since its also 4 bits
fprintf (stderr, "Dialer BCD: ");
for (i = 0; i < end; i++)
{
//dialer digits 7.2.9
int digit = (int)ConvertBitIntoBytes(&cs_bits[(i*4)+96], 4);
if (digit < 10) fprintf (stderr, "%d", digit); //numbers 0-9
else if (digit == 10) fprintf (stderr, "*"); //asterisk/star
else if (digit == 11) fprintf (stderr, "#"); //pound/hash
else if (digit == 15) fprintf (stderr, " "); //null character
else fprintf (stderr, "R:%X", digit); //reserved values on 12,13, and 14
}
}
else if (udt_format2 == 0x03) //ISO7 format
{
if (udt_uab == 1) end = 11;
if (udt_uab == 2) end = 25;
if (udt_uab == 3) end = 38;
if (udt_uab == 4) end = 52;
end -= udt_padnib/7; //is this correct?
fprintf (stderr, "ISO7 Text: " );
for (i = 0; i < end; i++) //max 368/7 = 52 character max?
{
iso7c = (uint8_t)ConvertBitIntoBytes(&cs_bits[(i*7)+96], 7);
if (iso7c >= 0x20 && iso7c <= 0x7E) //Standard ASCII Set
fprintf (stderr, "%c", iso7c);
else fprintf (stderr, " ");
}
}
else if (udt_format2 == 0x04) //ISO8 format
{
fprintf (stderr, "ISO8 Text: " );
if (udt_uab == 1) end = 10;
if (udt_uab == 2) end = 22;
if (udt_uab == 3) end = 34;
if (udt_uab == 4) end = 46;
end -= udt_padnib/8; //is this correct?
for (i = 0; i < end; i++)
{
iso8c = (uint8_t)ConvertBitIntoBytes(&cs_bits[(i*8)+96], 8);
if (iso8c >= 0x20 && iso8c <= 0x7E) //Standard ASCII Set
fprintf (stderr, "%c", iso8c);
// else if (iso8c >= 0x81 && iso8c <= 0xFE) //Extended ASCII Set
// fprintf (stderr, "%c", iso8c);
else fprintf (stderr, " ");
}
}
else if (udt_format2 == 0x07) //UTF-16BE format
{
if (udt_uab == 1) end = 5;
if (udt_uab == 2) end = 11;
if (udt_uab == 3) end = 17;
if (udt_uab == 4) end = 23;
end -= udt_padnib/4; //example, 4 blocks sets 23 - (20nibs/4bits) = 18 chars, may need to check this again
fprintf (stderr, "UTF16 Text: " );
for (i = 0; i < end; i++) //368/16 = 23 character max?
{
utf16c = (uint16_t)ConvertBitIntoBytes(&cs_bits[(i*16)+96], 16);
if (utf16c >= 0x20 && utf16c != 0x7F) //avoid control chars
fprintf (stderr, "%lc", utf16c); //will using lc work here? May depend on console locale settings?
else fprintf (stderr, " ");
}
}
else if (udt_format2 == 0x06) //IP
{
if (udt_uab == 1) //IP4
{
fprintf (stderr, "IP4: ");
fprintf (stderr, "%d.",(uint8_t)ConvertBitIntoBytes(&cs_bits[96+0], 8));
fprintf (stderr, "%d.",(uint8_t)ConvertBitIntoBytes(&cs_bits[96+8], 8));
fprintf (stderr, "%d.",(uint8_t)ConvertBitIntoBytes(&cs_bits[96+16], 8));
fprintf (stderr, "%d", (uint8_t)ConvertBitIntoBytes(&cs_bits[96+24], 8));
}
else //IP6
{
fprintf (stderr, "IP6: ");
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+0], 16));
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+16], 16));
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+32], 16));
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+48], 16));
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+56], 16));
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+72], 16));
fprintf (stderr, "%04X:",(uint16_t)ConvertBitIntoBytes(&cs_bits[96+88], 16));
fprintf (stderr, "%04X", (uint16_t)ConvertBitIntoBytes(&cs_bits[96+104], 16));
}
}
else if (udt_format2 == 0x0A) //Mixed Address/UTF-16BE
{
if (udt_uab == 1) end = 3;
if (udt_uab == 2) end = 9;
if (udt_uab == 3) end = 15;
if (udt_uab == 4) end = 21;
end -= udt_padnib/4; //is this correct?
fprintf (stderr, "Address: %d", (uint32_t)ConvertBitIntoBytes(&cs_bits[96+8], 24));
fprintf (stderr, "Text: " );
for (i = 0; i < end; i++) //368/16 = 21 character max
{
utf16c = (uint16_t)ConvertBitIntoBytes(&cs_bits[(i*16)+96], 16);
if (utf16c >= 0x20 && utf16c != 0x7F) //avoid control chars
fprintf (stderr, "%lc", utf16c);
else fprintf (stderr, " ");
}
}
else if (udt_format2 == 0x05)
{
//Would be nice to be able to test these all out to make sure the conditions are okay, etc
fprintf (stderr, "NMEA" );
if (cs_bits[96] == 1) //check if its encrypted first
fprintf (stderr, " Encrypted Format :(" ); //sad face
else if (udt_uab == 1)
nmea_iec_61162_1 (opts, state, cs_bits+96, udt_source, 1); //short format w/ 1 appended block
else if (udt_uab == 2)
nmea_iec_61162_1 (opts, state, cs_bits+96, udt_source, 2); //standard long format w/ 2 appended blocks
//check the 'spare' bits from 184 to 192 to see if this is a manufacturer specific nmea format
else if (udt_uab == 3) //mfid format w/ 2 appended blocks -- format unspecified
fprintf (stderr, " Unspecified MFID Format: %02X;", (uint8_t)ConvertBitIntoBytes(&cs_bits[184], 8));
else
fprintf (stderr, " Reserved Format; ");
}
else if (udt_format2 == 0x0B)
{
//unsure of how this is structured for UDT Blocks, would assume one appended block of same format
//but could also be full blown LIP protocol that is also found in tetra that would require the PDU
//type bit to be read and then to decode accordingly, this assumes its the modified Short PDU that USBD uses
fprintf (stderr, "\n");
lip_protocol_decoder (opts, state, cs_bits+96); //start on first appended block, and not header
}
else if (udt_format2 == 0x08 || udt_format2 == 0x09)
{
fprintf (stderr, "MFID SPEC %02X: ", udt_format2);
//use -Z to expose this
}
else
{
fprintf (stderr, "Reserved %02X: ", udt_format2);
//use -Z to expose this
}
fprintf (stderr, "%s", KNRM);
}
//assemble the blocks as they come in, shuffle them into the unified dmr_pdu_sf
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type)
{
UNUSED(databurst);
int i, j;
uint8_t lb = 0; //mbc last block
uint8_t pf = 0; //mbc protect flag
uint8_t slot = state->currentslot;
int blocks;
uint8_t blockcounter = state->data_block_counter[slot];
uint8_t block_num = state->data_header_blocks[slot];
uint32_t CRCCorrect = 0;
uint32_t CRCComputed = 0;
uint32_t CRCExtracted = 0;
uint32_t IrrecoverableErrors = 0;
uint8_t dmr_pdu_sf_bits[8*24*129]; //give plenty of space so we don't go oob
//MBC Header and Block CRC
uint8_t mbc_crc_good[2]; //header and blocks crc pass/fail local storage
memset (mbc_crc_good, 0, sizeof (mbc_crc_good)); //init on 0 - bad crc
if (type == 1) blocks = state->data_header_blocks[slot] - 1;
if (type == 2) blocks = state->data_block_counter[slot];
//UDT Header and Block Check
uint8_t is_udt = 0;
if (type == 3)
{
blocks = state->data_header_blocks[slot];
is_udt = 1;
type = 2;
}
//sanity check, setting block_len and block values to sane numbers in case of missing header, else could overload array (crash) or print out
if (blocks < 1) blocks = 1; //changed blocks from uint8_t to int
if (blocks > 127) blocks = 127;
if (block_len == 0) block_len = 18;
if (block_len > 24) block_len = 24;
if (type == 1)
{
//type 1 data block, append current block_bytes to end of ctr location
uint16_t ctr = state->data_byte_ctr[slot];
for (i = 0; i < block_len; i++)
state->dmr_pdu_sf[slot][ctr++] = block_bytes[i];
//add block_len to current byte counter
state->data_byte_ctr[slot] += block_len;
//time to send the completed 'superframe' to the DMR PDU message handler
if (state->data_block_counter[slot] == state->data_header_blocks[slot] && state->data_header_valid[slot] == 1)
{
//CRC32 on completed messages
for(i = 0, j = 0; i < ctr; i++, j+=8)
{
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
}
//sanity check to prevent a negative or zero block_num or sending a negative value into the bit array
if (block_num < 1 || block_num == 0) block_num = 1;
CRCExtracted = (state->dmr_pdu_sf[slot][ctr-4] << 24) | (state->dmr_pdu_sf[slot][ctr-3] << 16) |
(state->dmr_pdu_sf[slot][ctr-2] << 8) | (state->dmr_pdu_sf[slot][ctr-1] << 0);
int offset = 0;
if (state->data_p_head[slot] == 1)
offset = 12;
//rearrage for ridiculously stupid CRC32 LSO/MSO ordering
for(i = 0, j = 0; i < ctr; i+=2, j+=16)
{
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01;
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01;
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01;
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01;
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01;
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01;
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01;
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01;
dmr_pdu_sf_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
dmr_pdu_sf_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
dmr_pdu_sf_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
dmr_pdu_sf_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
dmr_pdu_sf_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
dmr_pdu_sf_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
dmr_pdu_sf_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
dmr_pdu_sf_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
//increment extra 2 bytes on each nth byte (depends on data type 1/2, 3/4, 1) if conf data
if ( i == (block_len - 1 + offset) )
{
if (state->data_conf_data[slot] == 1) i+=2; //should this be 0, or 1?
}
}
//confirmed working now!
CRCComputed = (uint32_t)ComputeCrc32Bit(dmr_pdu_sf_bits, ( block_num * block_len * 8) - 32);
//if the CRC32 is correct, I think its fair to assume we don't need to worry about if the
//individual CRC9s are correct on confirmed data blocks (but we can confirm now that they are all good)
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
//check for encryption on PDU
uint8_t enc_check = 0;
uint8_t decrypted_pdu = 1;
if (slot == 0 && state->dmr_so == 0x100)
{
enc_check = 1;
decrypted_pdu = 0;
}
else if (slot == 1 && state->dmr_soR == 0x100)
{
enc_check = 1;
decrypted_pdu = 0;
}
//decode PDU
if (enc_check == 1 && decrypted_pdu == 0) //check for encryption and if it was decrypted first or not
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "\n Slot %d - Encrypted Data Packet;", slot+1);
fprintf (stderr, "%s", KNRM);
}
else if (CRCCorrect || opts->aggressive_framesync == 0)
{
//may need to make adjustments for various compressed headers for starting point, etc?
if (state->data_header_sap[slot] == 4) //IP based
{
uint16_t len = ((blocks+1)*block_len)-4; //total number of bytes in PDU minus 4 CRC32 bytes
dmr_ip_pdu (opts, state, len, state->dmr_pdu_sf[slot]);
}
else if (state->data_header_sap[slot] == 10) //short data, may also need to check for SD:D [DD_HEAD]
{
uint16_t len = ((blocks+1)*block_len)-4; //total number of bytes in PDU minus 4 CRC32 bytes
dmr_sd_pdu (opts, state, len, state->dmr_pdu_sf[slot]);
}
else if (state->data_header_sap[slot] == 2 || state->data_header_sap[slot] == 3) //TCP and UDP Compression (may only be 3 UDP compression, unknown)
{
uint16_t len = ((blocks+1)*block_len)-4; //total number of bytes in PDU minus 4 CRC32 bytes
dmr_udp_comp_pdu (opts, state, len, state->dmr_pdu_sf[slot]);
}
else if (state->data_header_sap[slot] == 1 && state->dmr_pdu_sf[slot][1] == 0x10) //test SAP 1 MFID 10 (with -F) as a potential LRRP message (header inclusive)
{
uint16_t len = ((blocks+1)*block_len)-4;
//sanity check
if (len > 150)
len = 150;
uint8_t PDU[150]; memset (PDU, 0, sizeof(PDU)); //arbitrary size (should be okay)
//manipulate to match expected format alignment
PDU[0] = 0; PDU[1] = state->dmr_pdu_sf[slot][0];
//speculation: the other 4 bytes are hashed values for dst and src, or something similar
uint16_t mprt = 0xFFFF;
uint32_t msrc = state->dmr_lrrp_source[slot];
uint32_t mdst = 0xFFFFFF; //TODO: Get the real DST or TG indicated in the header
//copy rest, starting at offset 5 of the reserved extended header, to offset 2 to match expected formatting
memcpy(PDU+2, state->dmr_pdu_sf[slot]+5, (sizeof(PDU)-5));
dmr_lrrp (opts, state, len-4, msrc, mdst, mprt, mprt, PDU);
}
}
//debug
fprintf (stderr, " CRC32: %08X / %08X", CRCExtracted, CRCComputed);
if (CRCCorrect) ; //print nothing
else
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "\n Slot %d - Multi Block PDU Message CRC32 ERR", slot+1);
fprintf (stderr, "%s", KNRM);
}
//Full Super Frame Type 1 - Payload Output
if (opts->payload == 1)
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr, "\n Slot %d - Multi Block PDU Message\n ", slot+1);
for (i = 0; i < ((blocks+1)*block_len); i++)
{
if ( (i != 0) && (i % 12 == 0) ) fprintf (stderr, "\n ");
fprintf (stderr, "%02X", state->dmr_pdu_sf[slot][i]);
}
fprintf (stderr, "%s ", KNRM);
}
//reset data header format storage
state->data_header_format[slot] = 7;
//flag off data header validity
state->data_header_valid[slot] = 0;
//flag off conf data flag
state->data_conf_data[slot] = 0;
//reset padding
state->data_block_poc[slot] = 0;
//reset byte counter
state->data_byte_ctr[slot] = 0;
} //end completed sf
} //end type 1 blocks
//type 2 - MBC and UDT header, MBC and UDT continuation blocks
if (type == 2)
{
//sanity check (marginal signal, bad decodes, etc) -- may go a little lower (find out max number of MBC blocks supported)
if (state->data_block_counter[slot] > 4) state->data_block_counter[slot] = 4;
//Type 2 data block, additive method
for (i = 0; i < block_len; i++)
{
state->dmr_pdu_sf[slot][i+(blockcounter*block_len)] = block_bytes[i];
}
memset (dmr_pdu_sf_bits, 0, sizeof(dmr_pdu_sf_bits));
lb = block_bytes[0] >> 7; //last block flag
pf = (block_bytes[0] >> 6) & 1;
if (is_udt)
{
lb = 0; //set to zero, data header may erroneously the lb flag check above (IG)
pf = 0; //set to zero, data header may erroneously the pf flag check above (A)
if (blocks == blockcounter) lb = 1; //seems to work now with the +1 on udt_uab
//debug -- evaluate current block count vs the number of expected blocks
// fprintf (stderr, " BL: %d; BC: %d; ", blocks, blockcounter);
}
//last block arrived and we have a valid data header, time to send to cspdu decoder
if (lb == 1 && state->data_header_valid[slot] == 1)
{
for(i = 0, j = 0; i < 12*6; i++, j+=8) //4 blocks max?
{
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
}
//check UDT PF
if (is_udt) pf = dmr_pdu_sf_bits[73];
//CRC check on Header and full frames as appropriate (header crc already stored)
//The 16 bit CRC in the header shall include the data carried by the header.
//The 16 bit CRC in the last block shall be performed on
//all MBC blocks (conmbined) except the header block.
mbc_crc_good[0] = state->data_block_crc_valid[slot][0];
CRCExtracted = 0;
//extract crc from last block, apply to completed 'superframe' minus header
for(i = 0; i < 16; i++)
{
CRCExtracted = CRCExtracted << 1;
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + 96*(1+blocks) - 16] & 1);
}
uint8_t mbc_block_bits[12*8*6]; //needed more room
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
//shift continuation blocks and last block into seperate array for crc check
for (i = 0; i < 12*8*3; i++) //only doing 3 blocks (4 minus the header), probably need to re-evalutate this
{
mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip mbc header
}
if (is_udt)
{
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
for (i = 0; i < 12*8*blocks; i++)
{
mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip udt header
}
}
CRCComputed = ComputeCrcCCITT16d (mbc_block_bits, ((blocks+0)*96)-16 );
if (CRCComputed == CRCExtracted) mbc_crc_good[1] = 1;
CRCCorrect = 0;
IrrecoverableErrors = 1;
//set good on good header and good blocks
if (mbc_crc_good[0] == 1 && mbc_crc_good[1] == 1)
{
CRCCorrect = 1;
IrrecoverableErrors = 0;
}
else
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "\n Slot %d - Multi Block Control Message CRC16 ERR", slot+1);
//debug print
fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
fprintf (stderr, "%s", KNRM);
}
//cspdu will only act on any fid/opcodes if good CRC to prevent falsing on control signalling
if (!is_udt && !pf) dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors);
//send to udt decoder for handling
if (is_udt && !pf) dmr_udt_decoder (opts, state, state->dmr_pdu_sf[slot], CRCCorrect);
//Full Super Frame MBC/UDT - Debug Output
if (opts->payload == 1)
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr, "\n Slot %d - Multi Block Control Message\n ", slot+1);
for (i = 0; i < ((blocks+1)*block_len); i++)
{
fprintf (stderr, "%02X", state->dmr_pdu_sf[slot][i]);
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
{
fprintf (stderr, "\n ");
}
}
fprintf (stderr, "%s", KRED);
if (mbc_crc_good[0] == 0) fprintf (stderr, "MBC/UDT Header CRC ERR ");
if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC/UDT Blocks CRC ERR ");
if (pf) fprintf (stderr, "MBC/UDT Header/Blocks Protected ");
fprintf (stderr, "%s ", KNRM);
}
} //end last block flag on MBC
} //end type 2 (MBC Header and Continuation)
//leave this seperate so we can reset/zero stuff in case the data header isn't valid, etc
//switched to an if-elseif-else so we could get the block counter increment on the end
//without having it in an akward position
//if the end of normal data header and blocks
if (type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot])
{
//clear out unified pdu 'superframe' slot
for (int i = 0; i < 24*127; i++)
{
state->dmr_pdu_sf[slot][i] = 0;
}
//Zero Out MBC Header Block CRC Valid
state->data_block_crc_valid[slot][0] = 0;
//reset the block counter (data blocks)
state->data_block_counter[slot] = 1;
//reset data header format storage
state->data_header_format[slot] = 7;
//flag off data header validity
state->data_header_valid[slot] = 0;
//flag off conf data flag
state->data_conf_data[slot] = 0;
//reset padding
state->data_block_poc[slot] = 0;
//reset byte counter
state->data_byte_ctr[slot] = 0;
}
//else if the end of MBC Header and Blocks
else if (type == 2 && lb == 1)
{
//clear out unified pdu 'superframe' slot
for (int i = 0; i < 24*127; i++)
{
state->dmr_pdu_sf[slot][i] = 0;
}
//Zero Out MBC Header Block CRC Valid
state->data_block_crc_valid[slot][0] = 0;
//reset the block counter (data blocks)
state->data_block_counter[slot] = 1;
//reset data header format storage
state->data_header_format[slot] = 7;
//flag off data header validity
state->data_header_valid[slot] = 0;
//flag off conf data flag
state->data_conf_data[slot] = 0;
//flag off p_head
state->data_p_head[slot] = 0;
}
//else increment block counter after sorting/shuffling blocks
else state->data_block_counter[slot]++;
}
//failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures
//or tuning away and we can no longer verify accurate data block reporting
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
{
UNUSED(opts);
memset (state->data_p_head, 0, sizeof(state->data_p_head));
memset (state->data_conf_data, 0, sizeof(state->data_conf_data));
memset (state->dmr_pdu_sf, 0, sizeof(state->dmr_pdu_sf));
memset (state->data_block_counter, 1, sizeof(state->data_block_counter));
memset (state->data_block_poc, 0, sizeof(state->data_block_poc));
memset (state->data_byte_ctr, 0, sizeof(state->data_byte_ctr));
memset (state->data_header_blocks, 1, sizeof(state->data_header_blocks));
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
memset (state->dmr_lrrp_source, 0, sizeof(state->dmr_lrrp_source));
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
memset (state->cap_plus_block_num, 0, sizeof(state->cap_plus_block_num));
memset (state->data_header_valid, 0, sizeof(state->data_header_valid));
memset (state->data_header_format, 7, sizeof(state->data_header_format));
//reset some strings -- resetting call string here causes random blink on ncurses terminal (cap+)
// sprintf (state->call_string[0], "%s", " "); //21 spaces
// sprintf (state->call_string[1], "%s", " "); //21 spaces
sprintf (state->dmr_lrrp_gps[0], "%s", "");
sprintf (state->dmr_lrrp_gps[1], "%s", "");
}