mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
DMR: Tweak FLCO to remove 0x23 as a Cap+ Private Call when it is only a RAS indicator on a normal Private Call; Clarify Comments; DMR: Tweak RAS MD5 loading to allow for a correction against a flat value (if needed in future based on observation); DMR: Misc Tweaks to Vertex Standard Key Loading; #316
680 lines
23 KiB
C
680 lines
23 KiB
C
/*-------------------------------------------------------------------------------
|
|
* dmr_dburst.c
|
|
* DMR Data Burst Handling and related BPTC/FEC/CRC Functions
|
|
*
|
|
* Portions of BPTC/FEC/CRC code from LouisErigHerve
|
|
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
|
*
|
|
* LWVMOBILE
|
|
* 2026-09 DSD-FME Florida Man Edition
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
|
|
//TODO: Test UDT NMEA and LIP Decoders with Real World Samples (if/when available)
|
|
|
|
#include "dsd.h"
|
|
|
|
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst)
|
|
{
|
|
|
|
uint32_t i, j, k;
|
|
uint32_t CRCExtracted = 0;
|
|
uint32_t CRCComputed = 0;
|
|
uint32_t CRCCorrect = 0;
|
|
uint32_t IrrecoverableErrors = 0;
|
|
uint8_t slot = state->currentslot;
|
|
|
|
//confirmed data
|
|
uint8_t dbsn = 0; //data block serial number for confirmed data blocks
|
|
uint8_t blockcounter = 0; //local block count
|
|
uint8_t confdatabits[250]; //array to reshuffle conf data block bits into sequence for crc9 check
|
|
memset (confdatabits, 0, sizeof(confdatabits));
|
|
UNUSED(dbsn);
|
|
|
|
//BPTC 196x96 Specific
|
|
uint8_t BPTCDeInteleavedData[196];
|
|
uint8_t BPTCDmrDataBit[96];
|
|
uint8_t BPTCDmrDataByte[12];
|
|
|
|
//Embedded Signalling Specific
|
|
uint8_t BptcDataMatrix[8][16];
|
|
uint8_t LC_DataBit[77];
|
|
uint8_t LC_DataBytes[10];
|
|
int Burst = -1;
|
|
|
|
memset (BPTCDeInteleavedData, 0, sizeof(BPTCDeInteleavedData));
|
|
memset (BPTCDmrDataBit, 0, sizeof(BPTCDmrDataBit));
|
|
memset (BPTCDmrDataByte, 0, sizeof(BPTCDmrDataByte));
|
|
memset (BptcDataMatrix, 0, sizeof(BptcDataMatrix));
|
|
memset (LC_DataBit, 0, sizeof(LC_DataBit));
|
|
memset (LC_DataBytes, 0, sizeof(LC_DataBytes));
|
|
|
|
//PDU Bytes and Bits
|
|
uint8_t DMR_PDU[25];
|
|
uint8_t DMR_PDU_bits[200];
|
|
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
|
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
|
|
|
uint8_t is_ras = 0;
|
|
uint8_t bptc_res_bits[3];
|
|
memset (bptc_res_bits, 0, sizeof(bptc_res_bits));
|
|
uint8_t crc_original_validity = 0;
|
|
|
|
uint32_t crcmask = 0;
|
|
uint8_t crclen = 0;
|
|
|
|
uint8_t is_bptc = 0;
|
|
uint8_t is_trellis = 0;
|
|
uint8_t is_emb = 0;
|
|
uint8_t is_lc = 0;
|
|
uint8_t is_full = 0;
|
|
uint8_t is_udt = 0;
|
|
uint8_t pdu_len = 0;
|
|
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
|
|
|
uint8_t usbd_st = 0; //usbd service type
|
|
|
|
switch(databurst){
|
|
case 0x00: //PI
|
|
is_bptc = 1;
|
|
crclen = 16;
|
|
crcmask = 0x6969; //insert pi and 69 jokes here
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " PI ");
|
|
break;
|
|
case 0x01: //VLC
|
|
is_bptc = 1;
|
|
is_lc = 1;
|
|
crclen = 24;
|
|
crcmask = 0x969696;
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " VLC ");
|
|
break;
|
|
case 0x02: //TLC
|
|
is_bptc = 1;
|
|
is_lc = 1;
|
|
crcmask = 0x999999;
|
|
crclen = 24;
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " TLC ");
|
|
break;
|
|
case 0x03: //CSBK
|
|
is_bptc = 1;
|
|
crclen = 16;
|
|
crcmask = 0xA5A5;
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " CSBK");
|
|
break;
|
|
case 0x04: //MBC Header
|
|
is_bptc = 1;
|
|
crclen = 16;
|
|
crcmask = 0xAAAA;
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " MBCH");
|
|
break;
|
|
case 0x05: //MBC Continuation
|
|
is_bptc = 1;
|
|
//let block assembler carry out crc on the completed message
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " MBCC");
|
|
break;
|
|
case 0x06: //Data Header
|
|
is_bptc = 1;
|
|
crclen = 16;
|
|
crcmask = 0xCCCC;
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " DATA");
|
|
break;
|
|
case 0x07: //1/2 Rate Data
|
|
is_bptc = 1;
|
|
crclen = 9; //confirmed data only
|
|
crcmask = 0x0F0;
|
|
pdu_len = 12; //12 bytes unconfirmed
|
|
sprintf(state->fsubtype, " R12U ");
|
|
if (state->data_conf_data[slot] == 1)
|
|
{
|
|
pdu_len = 10;
|
|
pdu_start = 2; //start at plus two when assembling
|
|
sprintf(state->fsubtype, " R12C ");
|
|
}
|
|
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
|
|
{
|
|
is_udt = 1;
|
|
if (state->data_conf_data[slot] == 1)
|
|
sprintf(state->fsubtype, " UDTC "); //confirmed data
|
|
else sprintf(state->fsubtype, " UDTU "); //unconfirmed data
|
|
}
|
|
break;
|
|
case 0x08: //3/4 Rate Data
|
|
is_trellis = 1;
|
|
crclen = 9; //confirmed data only
|
|
crcmask = 0x1FF;
|
|
pdu_len = 18; //18 bytes unconfirmed
|
|
sprintf(state->fsubtype, " R34U ");
|
|
if (state->data_conf_data[slot] == 1)
|
|
{
|
|
pdu_len = 16;
|
|
pdu_start = 2; //start at plus two when assembling
|
|
sprintf(state->fsubtype, " R34C ");
|
|
}
|
|
break;
|
|
case 0x09: //Idle
|
|
//pseudo-random data fill, no need to do anything with this
|
|
sprintf(state->fsubtype, " IDLE ");
|
|
break;
|
|
case 0x0A: //1 Rate Data
|
|
crclen = 9; //confirmed data only
|
|
crcmask = 0x10F;
|
|
is_full = 1;
|
|
pdu_len = 24; //192 bits 24 bytes + 4 pad bits
|
|
sprintf(state->fsubtype, " R_1U ");
|
|
if (state->data_conf_data[slot] == 1)
|
|
{
|
|
pdu_len = 22; //start at plus two when assembling
|
|
pdu_start = 2; //start at plus two when assembling
|
|
sprintf(state->fsubtype, " R_1C ");
|
|
}
|
|
break;
|
|
case 0x0B: //Unified Single Block Data USBD
|
|
is_bptc = 1;
|
|
crclen = 16;
|
|
crcmask = 0x3333;
|
|
pdu_len = 12; //12 bytes
|
|
sprintf(state->fsubtype, " USBD ");
|
|
break;
|
|
|
|
//special types (not real data 'sync' bursts)
|
|
case 0xEB: //Embedded Signalling
|
|
crclen = 5;
|
|
is_emb = 1;
|
|
pdu_len = 9;
|
|
break;
|
|
|
|
default:
|
|
//Slot Type FEC should catch this so we never see it,
|
|
//but if it doesn't, then we can still dump the entire 'packet'
|
|
//treat like rate 1 unconfirmed data
|
|
is_full = 1;
|
|
pdu_len = 25; //196 bits - 24.5 bytes
|
|
sprintf(state->fsubtype, " _UNK ");
|
|
break;
|
|
|
|
}
|
|
|
|
//flag off prop head when not looking at data blocks
|
|
if (databurst != 0x6 && databurst != 0x7 && databurst != 0x8 && databurst != 0xA && databurst != 0xB) state->data_p_head[slot] = 0;
|
|
|
|
if (databurst != 0xEB)
|
|
{
|
|
if (state->dmr_ms_mode == 0)
|
|
{
|
|
if (state->dmr_color_code != 16)
|
|
fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
|
else fprintf(stderr, "| Color Code=XX ");
|
|
}
|
|
fprintf(stderr, "|%s", state->fsubtype);
|
|
|
|
//'DSP' output to file
|
|
if (opts->use_dsp_output == 1)
|
|
{
|
|
FILE * pFile; //file pointer
|
|
pFile = fopen (opts->dsp_out_file, "a");
|
|
fprintf (pFile, "\n%d 98 ", slot+1); //'98' is CACH designation value
|
|
for (i = 0; i < 6; i++) //3 byte CACH
|
|
{
|
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
|
fprintf (pFile, "%X", cach_byte); //nibble, not a byte, next time I look at this and wonder why its not %02X
|
|
}
|
|
fprintf (pFile, "\n%d %02X ", slot+1, databurst); //use hex value of current data burst type
|
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
|
{
|
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
|
fprintf (pFile, "%X", dsp_byte);
|
|
}
|
|
fclose (pFile);
|
|
}
|
|
}
|
|
|
|
//Most Data Sync Burst types will use the bptc 196x96
|
|
if (is_bptc)
|
|
{
|
|
CRCExtracted = 0;
|
|
CRCComputed = 0;
|
|
IrrecoverableErrors = 0;
|
|
|
|
/* Deinterleave DMR data */
|
|
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
|
|
|
|
/* Extract the BPTC 196,96 DMR data */
|
|
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, bptc_res_bits, &is_ras);
|
|
|
|
#ifdef DEBUG_RAS
|
|
//debug print
|
|
if (bptc_res_bits[0] != 0 || bptc_res_bits[1] != 0 || bptc_res_bits[2] != 0)
|
|
fprintf (stderr, " BPTC R BITS: %d %d %d", bptc_res_bits[0], bptc_res_bits[1], bptc_res_bits[2]);
|
|
#endif
|
|
|
|
/* Convert the 96 bits BPTC data into 12 bytes */
|
|
pack_bit_array_into_byte_array(BPTCDmrDataBit, BPTCDmrDataByte, 12);
|
|
|
|
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
|
|
if (IrrecoverableErrors == 0 && BPTCDmrDataByte[1] == 0x68) is_ras = 0;
|
|
|
|
//make sure if this is a PI header, the FID is set to 0x10
|
|
if (IrrecoverableErrors == 0 && databurst == 0x00 && BPTCDmrDataByte[1] != 0x10) is_ras = 0;
|
|
|
|
/* Fill the CRC extracted */
|
|
CRCExtracted = 0;
|
|
for(i = 0; i < crclen; i++)
|
|
{
|
|
CRCExtracted = CRCExtracted << 1;
|
|
CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1);
|
|
}
|
|
|
|
/* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */
|
|
CRCExtracted = CRCExtracted ^ crcmask;
|
|
|
|
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
|
if (is_lc)
|
|
{
|
|
if (is_ras)
|
|
{
|
|
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 9, 1);
|
|
if (CRCComputed == CRCExtracted)
|
|
CRCCorrect = 1;
|
|
|
|
#ifdef DEBUG_RAS
|
|
fprintf (stderr, " EXT: %06X; MAC: %06X;", CRCExtracted, CRCComputed);
|
|
#endif
|
|
}
|
|
else CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
|
}
|
|
|
|
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
|
|
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
|
|
{
|
|
CRCComputed = 0;
|
|
CRCExtracted = 0;
|
|
CRCCorrect = 1;
|
|
}
|
|
|
|
//run CRC9 on intermediate and last 1/2 confirmed data blocks
|
|
else if (state->data_conf_data[slot] == 1 && databurst == 0x7)
|
|
{
|
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
|
dbsn = (uint32_t)ConvertBitIntoBytes(&BPTCDmrDataBit[0], 7) + 1; //recover data block serial number
|
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&BPTCDmrDataBit[7], 9); //extract CRC from data
|
|
CRCExtracted = CRCExtracted ^ crcmask;
|
|
|
|
//reorganize the BPTCDmrDataBit array into confdatabits, just for CRC9 check
|
|
//need to find a sample to confirm this
|
|
for(i = 0; i < 80; i++) confdatabits[i] = BPTCDmrDataBit[i + 16];
|
|
for(i = 0; i < 7; i++) confdatabits[i + 80] = BPTCDmrDataBit[i];
|
|
|
|
CRCComputed = ComputeCrc9Bit(confdatabits, 87);
|
|
if (CRCExtracted == CRCComputed)
|
|
{
|
|
CRCCorrect = 1;
|
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
|
}
|
|
else state->data_block_crc_valid[slot][blockcounter] = 0;
|
|
|
|
}
|
|
|
|
//run CCITT on other data forms
|
|
else
|
|
{
|
|
if (is_ras == 1)
|
|
{
|
|
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 10, 0);
|
|
|
|
#ifdef DEBUG_RAS
|
|
fprintf (stderr, " EXT: %04X; MAC: %04X;", CRCExtracted, CRCComputed);
|
|
#endif
|
|
}
|
|
else CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
|
|
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
|
else CRCCorrect = 0;
|
|
}
|
|
|
|
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay
|
|
//if we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
|
|
if (is_ras == 1 && state->ras_effective_key == 0)
|
|
{
|
|
crc_original_validity = CRCCorrect;
|
|
CRCCorrect = 1;
|
|
}
|
|
|
|
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
|
{
|
|
if (CRCCorrect)
|
|
{
|
|
state->data_block_crc_valid[slot][0] = 1;
|
|
}
|
|
else state->data_block_crc_valid[slot][0] = 0;
|
|
}
|
|
|
|
/* Convert corrected x bytes into x*8 bits */
|
|
unpack_byte_array_into_bit_array(BPTCDmrDataByte, BPTCDmrDataBit, pdu_len);
|
|
|
|
//convert to DMR_PDU and DMR_PDU_bits
|
|
for (i = 0; i < pdu_len; i++)
|
|
{
|
|
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
|
}
|
|
|
|
for (i = 0; i < pdu_len * 8; i++)
|
|
{
|
|
DMR_PDU_bits[i] = BPTCDmrDataBit[i];
|
|
}
|
|
|
|
}
|
|
|
|
//Embedded Signalling will use BPTC 128x77
|
|
if (is_emb)
|
|
{
|
|
|
|
CRCExtracted = 0;
|
|
CRCComputed = 0;
|
|
IrrecoverableErrors = 0;
|
|
|
|
/* First step : Reconstitute the BPTC 16x8 matrix */
|
|
Burst = 1; /* Burst B to E contains embedded signaling data */
|
|
k = 0;
|
|
for(i = 0; i < 16; i++)
|
|
{
|
|
for(j = 0; j < 8; j++)
|
|
{
|
|
/* Only the LSBit of the byte is stored */
|
|
BptcDataMatrix[j][i] = state->dmr_embedded_signalling[slot][Burst][k+8];
|
|
k++;
|
|
|
|
/* Go on to the next burst once 32 bit
|
|
* of the SNYC have been stored */
|
|
if(k >= 32)
|
|
{
|
|
k = 0;
|
|
Burst++;
|
|
}
|
|
} /* End for(j = 0; j < 8; j++) */
|
|
} /* End for(i = 0; i < 16; i++) */
|
|
|
|
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
|
|
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
|
|
|
/* Reconstitute the 5 bit CRC */
|
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
|
|
|
|
/* Compute the 5 bit CRC */
|
|
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
|
|
|
//read ahead first two bytes, look for 1X10 or 2X10 pattern
|
|
uint16_t ras_test = (uint16_t)(ConvertBitIntoBytes(&LC_DataBit[0], 16) & 0x30FF);
|
|
if ((IrrecoverableErrors == 0) && (ras_test == 0x2010 || ras_test == 0x1010))
|
|
{
|
|
is_ras = 1;
|
|
if (state->ras_effective_key != 0)
|
|
CRCComputed = ras_mac_calculator(state, LC_DataBit, 9, 2);
|
|
}
|
|
else is_ras = 0;
|
|
|
|
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
|
|
else CRCCorrect = 0;
|
|
|
|
for (i = 0; i < 72; i++)
|
|
{
|
|
DMR_PDU_bits[i] = LC_DataBit[i];
|
|
}
|
|
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&LC_DataBit[i*8], 8);
|
|
}
|
|
|
|
}
|
|
|
|
//the sexy one
|
|
if (is_trellis)
|
|
{
|
|
CRCExtracted = 0;
|
|
CRCComputed = 0;
|
|
IrrecoverableErrors = 1;
|
|
|
|
uint8_t tdibits[98];
|
|
memset (tdibits, 0, sizeof(tdibits));
|
|
|
|
//reconstitute info bits into dibits for the trellis decoder
|
|
for (i = 0; i < 98; i++)
|
|
tdibits[i] = (info[i*2] << 1) | info[i*2+1];
|
|
|
|
uint8_t TrellisReturn[18];
|
|
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
|
IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn);
|
|
|
|
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
|
|
//they may have been successfully corrected, the CRC will reveal as much
|
|
|
|
for (i = 0; i < pdu_len; i++)
|
|
DMR_PDU[i] = TrellisReturn[i+pdu_start];
|
|
|
|
for(i = 0, j = 0; i < 18; i++, j+=8)
|
|
{
|
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i] >> 7) & 0x01;
|
|
DMR_PDU_bits[j + 1] = (TrellisReturn[i] >> 6) & 0x01;
|
|
DMR_PDU_bits[j + 2] = (TrellisReturn[i] >> 5) & 0x01;
|
|
DMR_PDU_bits[j + 3] = (TrellisReturn[i] >> 4) & 0x01;
|
|
DMR_PDU_bits[j + 4] = (TrellisReturn[i] >> 3) & 0x01;
|
|
DMR_PDU_bits[j + 5] = (TrellisReturn[i] >> 2) & 0x01;
|
|
DMR_PDU_bits[j + 6] = (TrellisReturn[i] >> 1) & 0x01;
|
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i] >> 0) & 0x01;
|
|
}
|
|
|
|
//set CRC to correct on unconfirmed 3/4 data blocks (for reporting due to no CRC available on these)
|
|
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
|
|
|
|
//run CRC9 on intermediate and last 3/4 data blocks
|
|
else if (state->data_conf_data[slot] == 1)
|
|
{
|
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
|
dbsn = (uint32_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 7) + 1; //recover data block serial number
|
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&DMR_PDU_bits[7], 9); //extract CRC from data
|
|
CRCExtracted = CRCExtracted ^ crcmask;
|
|
|
|
//reorganize the DMR_PDU_bits array into confdatabits, just for CRC9 check
|
|
for(i = 0; i < 128; i++) confdatabits[i] = DMR_PDU_bits[i + 16];
|
|
for(i = 0; i < 7; i++) confdatabits[i + 128] = DMR_PDU_bits[i];
|
|
|
|
CRCComputed = ComputeCrc9Bit(confdatabits, 135);
|
|
if (CRCExtracted == CRCComputed)
|
|
{
|
|
CRCCorrect = 1;
|
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
|
}
|
|
else
|
|
state->data_block_crc_valid[slot][blockcounter] = 0;
|
|
|
|
|
|
}
|
|
|
|
//reorganize the DMR_PDU_bits into PDU friendly format (minus the dbsn and crc9)
|
|
memset (DMR_PDU_bits, 0, sizeof(DMR_PDU_bits));
|
|
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
|
{
|
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
|
DMR_PDU_bits[j + 1] = (TrellisReturn[i+pdu_start] >> 6) & 0x01;
|
|
DMR_PDU_bits[j + 2] = (TrellisReturn[i+pdu_start] >> 5) & 0x01;
|
|
DMR_PDU_bits[j + 3] = (TrellisReturn[i+pdu_start] >> 4) & 0x01;
|
|
DMR_PDU_bits[j + 4] = (TrellisReturn[i+pdu_start] >> 3) & 0x01;
|
|
DMR_PDU_bits[j + 5] = (TrellisReturn[i+pdu_start] >> 2) & 0x01;
|
|
DMR_PDU_bits[j + 6] = (TrellisReturn[i+pdu_start] >> 1) & 0x01;
|
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
|
}
|
|
|
|
}
|
|
|
|
if (is_full) //Rate 1 Data
|
|
{
|
|
//assembly (w/ confirmed data) on a working Tier III Tait System
|
|
|
|
CRCExtracted = 0;
|
|
CRCComputed = 0;
|
|
IrrecoverableErrors = 0; //implicit, since there is no encoding
|
|
|
|
//pack rate 1 data for a total of up to 24 bytes, (22 if Confirmed Data)
|
|
//skipping the 4 padding bits located at 96,97,98,99 using the ptr values
|
|
//as offsets for confirmed data, len, and continue points for packing
|
|
int bit_ptr = pdu_start * 8;
|
|
int byte_ptr = 0;
|
|
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12-pdu_start);
|
|
bit_ptr = 100; byte_ptr = 12-pdu_start;
|
|
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12);
|
|
|
|
//set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these)
|
|
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
|
|
|
|
//run CRC9 on intermediate and last 1 rate data blocks
|
|
else if (state->data_conf_data[slot] == 1)
|
|
{
|
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
|
dbsn = (uint32_t)ConvertBitIntoBytes(&info[0], 7) + 1; //recover data block serial number
|
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&info[7], 9); //extract CRC from data
|
|
CRCExtracted = CRCExtracted ^ crcmask;
|
|
|
|
//reorganize the info bit array into confdatabits, just for CRC9 check
|
|
int k = 0;
|
|
for (i = 16; i < 96; i++) confdatabits[k++] = info[i]; //first half
|
|
for (i = 100; i < 196; i++) confdatabits[k++] = info[i]; //second half
|
|
for (i = 0; i < 7; i++) confdatabits[k++] = info[i]; //DBSN
|
|
CRCComputed = ComputeCrc9Bit(confdatabits, k);
|
|
|
|
if (CRCExtracted == CRCComputed)
|
|
{
|
|
CRCCorrect = 1;
|
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
|
}
|
|
else state->data_block_crc_valid[slot][blockcounter] = 0;
|
|
|
|
//debug confirmed data values (working now)
|
|
// fprintf (stderr, " K: %d; DSBN: %d; CRC: %03X / %03X;", k, dbsn, CRCComputed, CRCExtracted);
|
|
|
|
}
|
|
|
|
//copy info to dmr_pdu_bits
|
|
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
|
|
|
}
|
|
|
|
|
|
//time for some pi
|
|
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
|
|
//full link control
|
|
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 1); //VLC
|
|
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 2); //TLC
|
|
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 3); //EMB
|
|
|
|
//dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1
|
|
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
|
|
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
|
if (databurst == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data
|
|
if (databurst == 0x07 && is_udt == 0) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
|
if (databurst == 0x07 && is_udt == 1) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 3); //UDT with 1/2 Rate Encoding
|
|
|
|
//control signalling types (CSBK, MBC)
|
|
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
|
|
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
|
if (databurst == 0x04)
|
|
{
|
|
state->data_block_counter[slot] = 0; //zero block counter before running header
|
|
state->data_header_valid[slot] = 1; //set valid header since we received one
|
|
dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
|
}
|
|
if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
|
|
|
//Unified Single Data Block (USBD) -- Not to be confused with Unified Data Transport (UDT)
|
|
if (databurst == 0x0B)
|
|
{
|
|
//TODO: provide more robust handling at a later date -- ETSI TS 102 361-4 V1.11.1 (2021-01) 6.6.11.3
|
|
usbd_st = (uint8_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 4);
|
|
fprintf (stderr, "%s\n", KYEL);
|
|
fprintf (stderr, " USBD - ");
|
|
if (usbd_st == 0) lip_protocol_decoder (opts, state, DMR_PDU_bits);
|
|
else if (usbd_st > 8) fprintf (stderr, "Manufacturer Specific Service %d ", usbd_st);
|
|
else fprintf (stderr, "Reserved %d ", usbd_st);
|
|
}
|
|
|
|
//set the original CRCCorrect back to its original value if the RAS flag was tripped (if no key value present)
|
|
if (is_ras == 1 && state->ras_effective_key == 0) CRCCorrect = crc_original_validity;
|
|
|
|
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05
|
|
{
|
|
fprintf (stderr, "%s", KRED);
|
|
fprintf(stderr, " (FEC ERR)");
|
|
fprintf (stderr, "%s", KNRM);
|
|
}
|
|
|
|
//print whether or not the 'RAS Field' bit is set to indicate RAS
|
|
//detected for RAS enabled burst types and disposition if key available
|
|
if (IrrecoverableErrors == 0 && is_ras == 1 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07)
|
|
{
|
|
fprintf (stderr, "%s", KRED);
|
|
fprintf (stderr, " {RAS ");
|
|
if (state->ras_effective_key != 0 && CRCComputed != CRCExtracted)
|
|
fprintf (stderr, "Fail} ");
|
|
else if (state->ras_effective_key != 0 && CRCComputed == CRCExtracted)
|
|
fprintf (stderr, "Okay} ");
|
|
else if (state->ras_effective_key == 0)
|
|
fprintf (stderr, "Skip} ");
|
|
fprintf (stderr, "%s", KNRM);
|
|
}
|
|
|
|
//might flicker the terminal on partially enabled systems, may need to find a better placement
|
|
if (IrrecoverableErrors == 0 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07 && databurst != 0x09)
|
|
state->dmr_is_ras = is_ras;
|
|
|
|
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
|
|
{
|
|
fprintf (stderr, "%s", KRED);
|
|
fprintf (stderr, " (CRC ERR) ");
|
|
fprintf (stderr, "%s", KNRM);
|
|
}
|
|
|
|
//print the unified PDU format here, if not slot idle
|
|
if (opts->payload == 1 && databurst != 0x09)
|
|
{
|
|
fprintf (stderr, "\n");
|
|
fprintf (stderr, "%s", KCYN);
|
|
fprintf (stderr, " DMR PDU Payload ");
|
|
for (i = 0; i < pdu_len; i++)
|
|
{
|
|
#ifdef DEBUG_RAS
|
|
fprintf (stderr, "%02X", DMR_PDU[i]);
|
|
#else
|
|
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
|
#endif
|
|
}
|
|
|
|
//RAS 5-bit - always same differential
|
|
//calculation should be 5-bit portion of RAS Effective Key's Last byte & 0x1F
|
|
//5-bit + CRCComputed % 31 = CRCExtracted
|
|
#ifdef DEBUG_RAS
|
|
if (opts->payload == 1 && is_emb && is_ras)
|
|
{
|
|
int32_t diff = ((int32_t)CRCExtracted - (int32_t)CRCComputed + 31) % 31;
|
|
fprintf (stderr, " CRC CMP: %02X EXT: %02X DIFF: %02X", CRCComputed, CRCExtracted, diff);
|
|
}
|
|
#endif
|
|
|
|
//debug print
|
|
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
|
|
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
|
|
|
fprintf (stderr, "%s", KNRM);
|
|
|
|
}
|
|
|
|
}
|
|
|