/*------------------------------------------------------------------------------- * dmr_block.c * DMR Data Header and Data Block Assembly/Handling * * LWVMOBILE * 2022-12 DSD-FME Florida Man Edition *-----------------------------------------------------------------------------*/ #include "dsd.h" void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors) { uint32_t i, j, k; uint8_t slot = state->currentslot; //clear out unified pdu 'superframe' slot for (short int i = 0; i < 288; i++) { state->dmr_pdu_sf[slot][i] = 0; } //TODO: ONLY RUN ON GOOD CRC? //collect relevant data header info, figure out format, and grab appropriate info from it if (state->data_p_head[slot] == 0) { fprintf (stderr, "%s ", KGRN); state->data_block_counter[slot] = 1; //reset block counter to 1 state->data_header_format[slot] = dheader[0]; //collect the entire byte, we can check group or individual, response requested as well on this state->data_header_sap[slot] = (dheader[1] & 0xF0) >> 4; if ( (state->data_header_format[slot] & 0xF) == 0x2) { state->data_conf_data[slot] = 0; //flag as unconfirmed data, no need for additional CRC/Serial checks state->data_header_padding[slot] = dheader[1] & 0xF; //number of padding octets on last block state->data_header_blocks[slot] = dheader[8] & 0x7F; //number of blocks to follow, most useful for when to poll the super array for data fprintf (stderr, "\n Unconfirmed Data Header: DPF %01X - SAP %02X - Block Count %02X - Padding Octets %02X", state->data_header_format[slot] & 0xF, state->data_header_sap[slot], state->data_header_blocks[slot], state->data_header_padding[slot] ); } if ( (state->data_header_format[slot] & 0xF) == 0x3) { state->data_conf_data[slot] = 1; //set flag to indicate the following blocks are confirmed data so we know to run additional CRC etc on it. state->data_header_padding[slot] = dheader[1] & 0xF; //number of padding octets on last block state->data_header_blocks[slot] = dheader[8] & 0x7F; //number of blocks to follow, most useful for when to poll the super array for data fprintf (stderr, "\n Confirmed Data Header: DPF %01X - SAP %02X - Block Count %02X - Padding Octets %02X", state->data_header_format[slot] & 0xF, state->data_header_sap[slot], state->data_header_blocks[slot], state->data_header_padding[slot] ); } if ( (state->data_header_format[slot] & 0xF) == 0xD) //DD_HEAD { state->data_conf_data[slot] = 0; state->data_header_padding[slot] = dheader[7] & 0xF; state->data_header_blocks[slot] = ( (dheader[0] & 0x30) | ((dheader[1] & 0xF) >> 0) ); //what a pain MSB | LSB fprintf (stderr, "\n Short Data - Defined: DPF %01X - SAP %02X - Appended Blocks %02X - Padding Bits %02X", state->data_header_format[slot] & 0xF, state->data_header_sap[slot], state->data_header_blocks[slot], state->data_header_padding[slot] ); } if ( (state->data_header_format[slot] & 0xF) == 0xE) //SP_HEAD or R_HEAD { state->data_conf_data[slot] = 0; state->data_header_padding[slot] = dheader[7] & 0xF; state->data_header_blocks[slot] = ( (dheader[0] & 0x30) | ((dheader[1] & 0xF) >> 0) ); fprintf (stderr, "\n Short Data - Raw or Precoded: DPF %01X - SAP %02X - Appended Blocks %02X - Padding Bits %02X", state->data_header_format[slot] & 0xF, state->data_header_sap[slot], state->data_header_blocks[slot], state->data_header_padding[slot] ); } } //do we still need this? since we have ample PDU storage if (state->data_header_blocks[slot] > 7) { state->data_header_blocks[slot] = 7; } fprintf (stderr, "%s ", KNRM); //end collecting data header info fprintf (stderr, "%s ", KYEL); if (state->data_header_sap[slot] == 0x4 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n IP Data - Source: "); fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] ); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] ); state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7]; //Remus! Uncomment these line // if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0) // sprintf (state->dmr_lrrp_gps[slot], "IP DATA SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]); } if (state->data_header_sap[slot] == 0x0 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n Unified Data Transport - Source: "); fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] ); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] ); state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7]; //Remus! Uncomment these line // if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0) // sprintf (state->dmr_lrrp_gps[slot], "UDT SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]); } if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n Proprietary Packet Data - Source: "); fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] ); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] ); state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7]; //Remus! Uncomment this line // sprintf (state->dmr_lrrp_gps[slot], "P_HEAD SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]); } if (state->data_header_sap[slot] == 0x2 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n TCP/IP Header Compression Data - Source: "); fprintf (stderr, "[%d]", dheader[3]); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", dheader[2]); state->dmr_lrrp_source[slot] = dheader[3]; //Remus! Uncomment these line // if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0) // sprintf (state->dmr_lrrp_gps[slot], "IPC SRC [%d] DST [%d] ", dheader[3], dheader[2]); } if (state->data_header_sap[slot] == 0x3 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n UDP/IP Header Compression Data - Source: "); fprintf (stderr, "[%d]", dheader[3]); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", dheader[2]); state->dmr_lrrp_source[slot] = dheader[3]; //Remus! Uncomment these line // if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0) // sprintf (state->dmr_lrrp_gps[slot], "UDP SRC [%d] DST [%d] ", dheader[3], dheader[2]); } if (state->data_header_sap[slot] == 0x5 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n ARP - Address Resolution Protocol "); } if ( (state->data_header_format[slot] & 0xF ) == 0xD && state->data_p_head[slot] == 0) { fprintf (stderr, "\n Short Data - Defined - Source: "); fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] ); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] ); //Remus! Uncomment this line // sprintf (state->dmr_lrrp_gps[slot], "SD-D SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]); } if ( (state->data_header_format[slot] & 0xF) == 0xE && state->data_p_head[slot] == 0) { fprintf (stderr, "\n Short Data - Raw or Status/Precoded - Source: "); fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] ); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] ); //Remus! Uncomment this line // sprintf (state->dmr_lrrp_gps[slot], "SD-R/P SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]); } if ( (state->data_header_format[slot] & 0xF ) == 0x1 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n Response Packet - Source: "); fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] ); fprintf (stderr, " Destination: "); fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] ); //Remus! Uncomment these lines // char rsp[90]; // sprintf (rsp, " - RSP SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]); // strcat (state->dmr_lrrp_gps[slot], rsp); } //Removed Dup UDT Message if ( (state->data_header_format[slot] & 0x40) == 0x40 && state->data_p_head[slot] == 0) //check response req bit { fprintf (stderr, "\n Response Requested - "); fprintf (stderr, " Source:"); fprintf (stderr, " [%d]", (dheader[2] <<16 ) + (dheader[3] << 8) + dheader[4] ); fprintf (stderr, " Destination:"); fprintf (stderr, " [%d]", (dheader[5] <<16 ) + (dheader[6] <<8 ) + dheader[7] ); //Remus! Uncomment these lines // char rsp[90]; // sprintf (rsp, "- RSP REQ "); // strcat (state->dmr_lrrp_gps[slot], rsp); } //if proprietary header was flagged by the last data header, then process data differently if (state->data_p_head[slot] == 1) { fprintf (stderr, "\n Proprietary Data Header: SAP %01X - Format %01X - MFID %02X", (dheader[0] & 0xF0) >> 4, dheader[0] & 0xF, dheader[1] ); //state->data_p_head[slot] = 0; //disabling this, we can flag it off after processing pdu instead state->data_header_sap[slot] = 0; //reset this to zero so we don't trip the condition below state->data_block_counter[slot]++; //increment the counter since this counts against the data blocks //set overarching manufacturer in use when non-standard feature id set is up //if (dheader[1] != 0) state->dmr_mfid = dheader[1]; //diable, will only set in link control or csbk for now (random manufacturers getting set, bad decode? or random feature?) //assign prop header pdu to 'superframe' uint8_t blocks = state->data_header_blocks[slot] - 1; for(i = 0; i < 12; i++) { state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i]; } } //only set this flag after everything else is processed if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 0) { fprintf (stderr, "\n Proprietary Packet Data Incoming "); state->data_p_head[slot] = 1; //flag that next data header will be a proprietary data header } fprintf (stderr, "%s ", KNRM); //let user know the data header may have errors fprintf (stderr, "%s", KRED); if (CRCCorrect == 1) ; //CRCCorrect 1 is good, else is bad CRC; no print on good else if(IrrecoverableErrors == 0) fprintf(stderr, " (FEC OK)"); else fprintf(stderr, " (FEC/CRC ERR)"); 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) { int i, j, k; uint8_t lb = 0; //mbc last block uint8_t pf = 0; //mbc protect flag uint8_t is_conf_data = 0; //internal flag to see if type 1 blocks contain confirmed data crcs and serials uint8_t slot = state->currentslot; int blocks; uint8_t blockcounter = state->data_block_counter[slot]; //crc specific variables uint8_t len = 24; //len of completed message needing crc32 number of blocks*block_len - 12? uint8_t crclen = 32; //len of the crc32 (obviously) uint8_t start = 12; //starting position (+12 offset loaded up data header) uint8_t n = 12; //number of bytes per block //TODO: CRC check on Header, Blocks and full frames as appropriate uint32_t CRCCorrect = 0; uint32_t CRCComputed = 0; uint32_t CRCExtracted = 0; uint32_t IrrecoverableErrors = 0; uint8_t dmr_pdu_sf_bits[18*8*8]; //8 blocks at 18 bytes at 8 bits //just a large value to have plenty storage space //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]; //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 > 8) blocks = 8; if (block_len == 0) block_len = 18; if (block_len > 24) block_len = 24; //TODO: add CRC/FEC validation to each data header and each confirmed data block as well - type 1 if (type == 1) { //type 1 data block, shuffle method for (j = 0; j < blocks; j++) //6 { for (i = 0; i < block_len; i++) //block_len { state->dmr_pdu_sf[slot][i+block_len*j] = state->dmr_pdu_sf[slot][i+block_len*(j+1)]; } } for (i = 0; i < block_len; i++) { state->dmr_pdu_sf[slot][i+(blocks*block_len)] = block_bytes[i]; } //time to send the completed 'superframe' to the DMR PDU message handler if (state->data_block_counter[slot] == state->data_header_blocks[slot]) { //TODO: CRC32 on completed messages dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]); } } //type 2 - MBC header and MBC continuation blocks if (type == 2) { //sanity check (marginal signal, bad decodes, etc) if (state->data_block_counter[slot] > 8) state->data_block_counter[slot] = 8; //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 (lb == 1) //last block arrived, time to send to cspdu decoder { for(i = 0, j = 0; i < 12*4; 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; } //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. <-- misleading statement, just like half the dmr manual 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*3]; 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++) { mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip mbc header } CRCComputed = ComputeCrcCCITT16d (mbc_block_bits, ((blocks+0)*96)-16 ); // fprintf (stderr, " \n CRC EXT %X CRC CMP %X", CRCExtracted, CRCComputed); 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; } //cspdu will only act on any fid/opcodes if good CRC to prevent falsing on control signalling dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors); } } //Full Super Frame Type 1 - Debug Output if (opts->payload == 1 && type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot]) { fprintf (stderr, "%s", KGRN); fprintf (stderr, "\n Multi Block PDU Superframe - Slot [%d]\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 ", KNRM); } //Full Super Frame MBC - Debug Output if (opts->payload == 1 && type == 2 && lb == 1) { fprintf (stderr, "%s", KGRN); fprintf (stderr, "\n MBC Multi Block PDU Superframe - Slot [%d]\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 Header CRC ERR "); if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC Blocks CRC ERR "); fprintf (stderr, "%s ", KNRM); } state->data_block_counter[slot]++; //increment block counter if (type == 2 && lb == 1) { state->data_block_counter[slot] = 0; //reset the block counter (MBCs) //clear out unified pdu 'superframe' slot for (short int i = 0; i < 288; i++) { state->dmr_pdu_sf[slot][i] = 0; } state->data_block_crc_valid[slot][0] = 0; //Zero Out MBC Header Block CRC Valid } } //failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures and we //can no longer verify accurate data block reporting void dmr_reset_blocks (dsd_opts * opts, dsd_state * state) { 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, 0, sizeof(state->data_block_counter)); memset (state->data_header_blocks, 0, 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)); }