DMR Reorganization; Initial Trunking Support;

This commit is contained in:
lwvmobile
2022-12-03 20:16:43 -05:00
parent f7d103c6a2
commit 178f88a28f
20 changed files with 3673 additions and 5653 deletions

View File

@@ -138,48 +138,6 @@ typedef struct
uint8_t bytes[3];
} rs_12_9_checksum_t;
typedef struct
{
char VoiceFrame[3][72]; /* 3 x 72 bit of voice per time slot frame */
char Sync[48]; /* 48 bit of data or SYNC per time slot frame */
}TimeSlotRawVoiceFrame_t;
typedef struct
{
char DeInterleavedVoiceSample[3][4][24]; /* 3 x (4 x 24) deinterleaved bit of voice per time slot frame */
}TimeSlotDeinterleavedVoiceFrame_t;
typedef struct
{
int errs1[3]; /* 3 x errors #1 computed when demodulate the AMBE voice bit of the frame */
int errs2[3]; /* 3 x errors #2 computed when demodulate the AMBE voice bit of the frame */
char AmbeBit[3][49]; /* 3 x 49 bit of AMBE voice of the frame */
}TimeSlotAmbeVoiceFrame_t;
typedef struct
{
unsigned int ProtectFlag;
unsigned int Reserved;
unsigned int FullLinkControlOpcode;
unsigned int FeatureSetID;
unsigned int ServiceOptions;
unsigned int GroupAddress; /* Destination ID or TG (in Air Interface format) */
unsigned int SourceAddress; /* Source ID (in Air Interface format) */
unsigned int DataValidity; /* 0 = All Full LC data are incorrect ; 1 = Full LC data are correct (CRC checked OK) */
unsigned int LeftOvers; //what is the 8 bits for that isn't used
}FullLinkControlPDU_t;
typedef struct
{
TimeSlotRawVoiceFrame_t TimeSlotRawVoiceFrame[6]; /* A voice superframe contains 6 timeslot voice frame */
TimeSlotDeinterleavedVoiceFrame_t TimeSlotDeinterleavedVoiceFrame[6];
TimeSlotAmbeVoiceFrame_t TimeSlotAmbeVoiceFrame[6];
FullLinkControlPDU_t FullLC;
} TimeSlotVoiceSuperFrame_t;
//dPMR
/* Could only be 2 or 4 */
#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2
@@ -321,6 +279,7 @@ typedef struct
int inverted_dpmr;
int frame_dpmr;
short int dmr_mono;
short int dmr_stereo;
short int lrrp_file_output;
@@ -528,12 +487,8 @@ typedef struct
char slot1light[8];
char slot2light[8];
int directmode;
TimeSlotVoiceSuperFrame_t TS1SuperFrame;
TimeSlotVoiceSuperFrame_t TS2SuperFrame;
char dmr_branding[25];
uint8_t dmr_12_rate_sf[2][288]; //was 84, expanded to 288 to prevent possible crash if more comes in than expected
uint8_t dmr_34_rate_sf[2][288]; //was 96, expanded to 288 to prevent possible crash if more comes in than expected
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
@@ -542,27 +497,23 @@ typedef struct
uint8_t data_block_counter[2]; //counter for number of data blocks collected
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
//new stuff below here
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
uint8_t data_block_crc_valid[2][25]; //flag each individual block as good crc on confirmed data
char dmr_embedded_signalling[2][6][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
//dmr talker alias new/fixed stuff
uint8_t dmr_alias_format[2]; //per slot
uint8_t dmr_alias_len[2]; //per slot
char dmr_alias_block_segment[2][4][7][16]; //2 slots, by 4 blocks, by up to 7 alias bytes that are up to 16-bit chars
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
//These flags are used to known the DMR frame
//printing format (hex or binary)
int printDMRRawVoiceFrameHex;
int printDMRRawVoiceFrameBin;
int printDMRRawDataFrameHex;
int printDMRRawDataFrameBin;
int printDMRAmbeVoiceSampleHex;
int printDMRAmbeVoiceSampleBin;
//These flags are used to known the dPMR frame
//orinting format (hex or binary)
int printdPMRRawVoiceFrameHex;
int printdPMRRawVoiceFrameBin;
int printdPMRRawDataFrameHex;
int printdPMRRawDataFrameBin;
int printdPMRAmbeVoiceSampleHex;
int printdPMRAmbeVoiceSampleBin;
unsigned char * dpmr_caller_id;
unsigned char * dpmr_target_id;
int dpmr_color_code;
@@ -572,6 +523,8 @@ typedef struct
short int dmr_ms_mode;
unsigned int dmrburstL;
unsigned int dmrburstR;
int dropL;
int dropR;
unsigned long long int R;
unsigned long long int RR;
unsigned long long int H;
@@ -654,6 +607,11 @@ typedef struct
uint32_t nxdn_location_sys_code;
uint16_t nxdn_location_site_code;
//dmr late entry mi
uint64_t late_entry_mi_fragment[2][7][3];
//char dmr_branding[25]; //may need higher value?
char dmr_branding_sub[25];
//Roman DMR End Call Alert Beep
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
@@ -733,13 +691,7 @@ typedef struct
/*
* function prototypes
*/
void processDMRdata (dsd_opts * opts, dsd_state * state);
void processDMRvoice (dsd_opts * opts, dsd_state * state);
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
void dmrBS (dsd_opts * opts, dsd_state * state);
void dmrMS (dsd_opts * opts, dsd_state * state);
void dmrMSData (dsd_opts * opts, dsd_state * state);
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
void processAudio (dsd_opts * opts, dsd_state * state);
void processAudioR (dsd_opts * opts, dsd_state * state);
@@ -799,8 +751,7 @@ void resumeScan (dsd_opts * opts, dsd_state * state);
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
void upsample (dsd_state * state, float invalue);
void processDSTAR (dsd_opts * opts, dsd_state * state);
void processNXDNVoice (dsd_opts * opts, dsd_state * state);
void processNXDNData (dsd_opts * opts, dsd_state * state);
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
void processHDU (dsd_opts * opts, dsd_state * state);
void processLDU1 (dsd_opts * opts, dsd_state * state);
@@ -836,14 +787,14 @@ void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[3
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
//OP25 NXDN CRC functions
//OP25 NXDN CRC functions
static inline int load_i(const uint8_t val[], int len);
static uint8_t crc6(const uint8_t buf[], int len);
static uint16_t crc12f(const uint8_t buf[], int len);
static uint16_t crc15(const uint8_t buf[], int len);
static uint16_t crc16cac(const uint8_t buf[], int len);
/* NXDN functions */
/* NXDN Convolution functions */
void CNXDNConvolution_start(void);
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
@@ -867,8 +818,6 @@ void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message
void nxdn_location_id_handler (dsd_state * state, uint32_t location_id);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
void printdPMRAmbeVoiceSample(dsd_opts * opts, dsd_state * state);
void printdPMRRawVoiceFrame (dsd_opts * opts, dsd_state * state);
//dPMR functions
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble);
@@ -878,9 +827,6 @@ uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength);
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]);
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
void ProcessDMR (dsd_opts * opts, dsd_state * state);
void DMRDataFrameProcess(dsd_opts * opts, dsd_state * state);
void DMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
//BPTC (Block Product Turbo Code) functions
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
@@ -893,26 +839,49 @@ uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
//SYNC DMR data extraction functions
void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state);
//DMR CRC Functions
uint16_t ComputeCrcCCITT(uint8_t * DMRData);
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
uint8_t * DmrAlgIdToStr(uint8_t AlgID);
uint8_t * DmrAlgPrivacyModeToStr(uint32_t PrivacyMode);
void ProcessDmrPIHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void Process1Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessMBCData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessMBChData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessReservedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessUnifiedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
//new simplified dmr functions
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
void dmr_data_sync (dsd_opts * opts, dsd_state * state);
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type);
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
//Embedded Alias and GPS
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
//"DMR STEREO"
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
void dmrBS (dsd_opts * opts, dsd_state * state);
void dmrMS (dsd_opts * opts, dsd_state * state);
void dmrMSData (dsd_opts * opts, dsd_state * state);
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t DMR_PDU[]);
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t DMR_PDU[]);
uint8_t dmr_lrrp_check (dsd_opts * opts, dsd_state * state, uint8_t DMR_PDU[]);
//dmr alg stuff
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state);
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24]);
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state);
//DMR FEC/CRC from Boatbod - OP25
bool Hamming17123(uint8_t* d);
uint8_t crc8(uint8_t bits[], unsigned int len);
bool crc8_ok(uint8_t bits[], unsigned int len);
//LFSR code courtesy of https://github.com/mattames/LFSR/
void LFSR(dsd_state * state);

392
src/dmr_block.c Normal file
View File

@@ -0,0 +1,392 @@
/*-------------------------------------------------------------------------------
* 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 ", KMAG);
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] );
sprintf ( state->dmr_lrrp[slot][1], "SRC [%d] DST [%d] ",
( (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7]),
( (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]) );
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
}
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] );
sprintf ( state->dmr_lrrp[slot][1], "SRC [%d] DST [%d] ",
( (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7]),
( (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]) );
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
}
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] );
sprintf ( state->dmr_lrrp[slot][1], "SRC [%d] DST [%d] ",
( (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7]),
( (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]) );
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
}
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]);
sprintf ( state->dmr_lrrp[slot][1], "SRC [%d] DST [%d] ",
dheader[3], dheader[2] );
state->dmr_lrrp_source[slot] = dheader[3];
}
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]);
sprintf ( state->dmr_lrrp[slot][1], "SRC [%d] DST [%d] ",
dheader[3], dheader[2] );
state->dmr_lrrp_source[slot] = dheader[3];
}
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] );
}
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] );
}
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] );
}
if ( (state->data_header_format[slot] & 0xF ) == 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] );
}
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] );
}
//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;
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];
//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
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 block 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, 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;
}
//TODO: CRC check on Header and full frames as appropriate
//shim these values for now
CRCCorrect = 1;
IrrecoverableErrors = 0;
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 ", 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
}
}

View File

@@ -1,3 +1,11 @@
/*-------------------------------------------------------------------------------
* dmr_bs.c
* DMR BS Voice Handling and Data Gathering Routines - "DMR STEREO"
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "dmr_const.h"
@@ -7,32 +15,37 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
const int *w, *x, *y, *z;
char sync[25] = {0};
char syncdata[48] = {0};
//char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16] = {0};
int EmbeddedSignallingOk;
unsigned int internalcolorcode;
int internalslot;
//redundancy check (carrier signal loss event)
char redundancyA[36];
char redundancyB[36];
unsigned short int vc1;
unsigned short int vc2;
int cach_okay = 0;
//cach fec
char cachdata[25] = {0}; //increased from 24 to 25 to see if this fixes ubuntu 32 bug
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
//memcpy of ambe_fr for late entry
char m1[4][24];
char m2[4][24];
char m3[4][24];
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
char EmbeddedSignalling[16];
uint8_t emb_ok = 0;
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t internalslot;
uint8_t vc1;
uint8_t vc2;
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
uint8_t dummy_bits[196];
memset (dummy_bits, 0, sizeof(dummy_bits));
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
@@ -63,10 +76,21 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
short int loop = 1;
short int skipcount = 0;
//cach
char cachdata[25];
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
//cach tact bits
uint8_t tact_bits[7];
//Run Loop while the getting is good
while (loop == 1) {
// CACH
internalslot = -1; //reset here so we know if this value is being set properly
for(i = 0; i < 12; i++)
{
@@ -77,30 +101,23 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//decode and correct cach and compare
cach_okay = 69;
if ( Hamming_7_4_decode (cachdata) ) //see if we need to de-interleave this...probably do.
for (i = 0; i < 7; i++)
{
cach_okay = 1;
tact_bits[i] = cachdata[i];
}
if (cach_okay == 1)
//disabled for dmr_cach testing
tact_okay = 0;
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1)
{
//fprintf (stderr, "CACH Okay ");
}
if (cach_okay != 1)
{
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
if (opts->aggressive_framesync == 1)
{
goto END;
}
}
state->currentslot = cachdata[1];
internalslot = cachdata[1];
//this cach fec seems to work quite well, really helps on shaky signals
internalslot = state->currentslot = tact_bits[1];
//Setup for first AMBE Frame
//Interleave Schedule
@@ -115,6 +132,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+12] = dibit;
redundancyA[i] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
@@ -134,11 +152,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
{
goto END;
}
// end redundancy test, set B to A
for(i = 0; i < 36; i++)
{
redundancyB[i] = redundancyA[i];
}
//end redundancy test, set B to A
memcpy (redundancyB, redundancyA, sizeof (redundancyA));
//Setup for Second AMBE Frame
//Interleave Schedule
@@ -162,90 +178,39 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
}
//short int l = 0; //do I still use this?
// signaling data or sync
long long int late_entry_pi[24] = {0};
short int m = 0;
//signaling data or sync
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+66] = dibit;
sync[i] = (dibit | 1) + 48; //how does this work again?
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
syncdata[(2*i)+1] = (1 & dibit); // bit 0
if (i < 4 || i > 19)
{
// fprintf(stderr, "\n EMB = ");
EmbeddedSignalling[(2*i)] = (1 & (dibit >> 1)); // bit 1
//fprintf (stderr, "%02b", dibit);
EmbeddedSignalling[(2*i)+1] = (1 & dibit); // bit 0
}
//Used for ProcessVoiceBurstSync
//embedded link control
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
{
/* Time slot 1 superframe buffer filling => SYNC data */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2+1] = (1 & dibit); // bit 0
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
}
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
{
/* Time slot 2 superframe buffer filling => SYNC data */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2+1] = (1 & dibit); // bit 0
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
}
}
sync[24] = 0;
EmbeddedSignallingOk = -1;
if(QR_16_7_6_decode(EmbeddedSignalling))
{
EmbeddedSignallingOk = 1;
}
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
internalcolorcode = 69; //set so we know if this value is being set properly
if( EmbeddedSignallingOk == 1 ) //don't set on 1?
{
//state->color_code = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
internalcolorcode = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
state->color_code_ok = EmbeddedSignallingOk;
//Power Indicator, not the other PI (header)
state->PI = (unsigned int)EmbeddedSignalling[4];
state->PI_ok = EmbeddedSignallingOk;
//Link Control Start Stop Indicator
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->LCSS_ok = EmbeddedSignallingOk;
for (i = 0; i < 8; i++)
{
//fprintf (stderr, " %b", EmbeddedSignalling[i]);
}
}
//else skipcount++;
else
{
for (i = 0; i < 8; i++)
{
//fprintf (stderr, " %b", EmbeddedSignalling[i]);
}
}
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+90] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
@@ -268,10 +233,6 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+108] = dibit;
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
@@ -286,305 +247,137 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
//reset vc counters to 1 if new voice sync frame on each slot
if ( strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
{
if (internalslot == 0)
{
vc1 = 1;
state->DMRvcL = 0; //reset here if internal sync forces reset
}
if (internalslot == 1)
{
vc2 = 1;
state->DMRvcR = 0; //reset here if internal sync forces reset
}
if (internalslot == 0) vc1 = 1;
if (internalslot == 1) vc2 = 1;
}
// if( strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
// {
// fprintf (stderr,"%s ", getTime());
// fprintf (stderr, " Sync: DMR TDMA Direct Mode TS 1 \n");
// goto SKIP;
// }
//
// if( strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
// {
// fprintf (stderr,"%s ", getTime());
// fprintf (stderr, " Sync: DMR TDMA Direct Mode TS 2 \n");
// goto SKIP;
// }
//check for sync pattern here after collected the rest of the payload, decide what to do with it
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
//constantly reset the vc counter to 1 each data frame in anticipation of new voice frame
vc1 = 1;
}
if (internalslot == 1)
{
sprintf(state->slot2light, "[slot2]");
sprintf(state->slot1light, " slot1 ");
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
//constantly reset the vc counter to 1 each data frame in anticipation of new voice frame
vc2 = 1;
}
processDMRdata (opts, state);
skipcount++; //after 2 data frames, drop back to getFrameSync and process subsequent data with processDMRdata
goto SKIP;
}
//find way to mitigate or correct coming back here after leaving in inverted signal
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 && opts->inverted_dmr == 1)
{
dmr_data_sync (opts, state);
skipcount++;
goto SKIP;
}
//extra check to see if we have missed a voice/data frame sync at this point and then exit if required
if (1 == 1) //opts->aggressive_framesync
{
//extremely aggressive
if ( vc1 > 7 || vc2 > 7 )
{
goto END;
}
}
//only play voice on no data sync
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0)
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
{
if (EmbeddedSignallingOk == 0)
//check the embedded signalling, if bad at this point, we probably aren't quite in sync
if(QR_16_7_6_decode(EmbeddedSignalling)) emb_ok = 1;
else emb_ok = 0;
//disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
else if (emb_ok == 1)
{
//goto END;
uint8_t cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
uint8_t power = EmbeddedSignalling[4];
uint8_t lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
}
skipcount = 0; //reset skip count if processing voice frames
fprintf (stderr,"%s ", getTime());
//simplifying things
char polarity[3];
char light[18];
uint8_t vc;
if (internalslot == 0)
{
state->dmrburstL = 16; //use 16 for Voice?
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR [SLOT1] slot2 | | DMRSTEREO | VC%d ",vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR [SLOT1] slot2 | | DMRSTEREO | VC%d ",vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
}
if (internalslot == 1)
state->dmrburstL = 16;
vc = vc1;
sprintf (light, "%s", " [SLOT1] slot2 ");
}
else
{
state->dmrburstR = 16; //use 16 for Voice?
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR slot1 [SLOT2] | | DMRSTEREO | VC%d ",vc2);
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR slot1 [SLOT2] | | DMRSTEREO | VC%d ",vc2);
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
}
if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues)
state->dmrburstR = 16;
vc = vc2;
sprintf (light, "%s", " slot1 [SLOT2] ");
}
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
else sprintf (polarity, "%s", "-");
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
if (internalslot == 0 && vc1 == 6)
{
//process voice burst
ProcessVoiceBurstSync(opts, state);
//process embedded link control
fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
}
if (internalslot == 1 && vc2 == 6) //presumably when full (and no sync issues)
if (internalslot == 1 && vc2 == 6)
{
//process voice burst
ProcessVoiceBurstSync(opts, state);
//process embedded link control
fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
}
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
//copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when
//we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3));
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
if (internalslot == 0 && vc1 == 6)
{
state->DMRvcL = 0;
if (state->payload_algid >= 0x21)
{
cach_err = dmr_cach (opts, state, cachdata);
fprintf (stderr, "\n");
if (state->payload_mi != 0 && state->payload_algid >= 0x21)
{
LFSR(state);
}
fprintf (stderr, "\n");
}
}
// run alg refresh after vc6 ambe processing
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 1 && vc2 == 6)
{
state->DMRvcR = 0;
if (state->payload_algidR >= 0x21)
{
if (state->payload_miR != 0 && state->payload_algidR >= 0x21)
{
LFSR(state);
}
fprintf (stderr, "\n");
}
}
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
if (internalslot == 0)
{
vc1++;
}
if (internalslot == 1)
{
vc2++;
}
//increment the vc counters
if (internalslot == 0) vc1++;
if (internalslot == 1) vc2++;
//reset err checks
cach_err = 1;
tact_okay = 0;
emb_ok = 0;
}
// recalibrate center/umid/lmid, not sure this helps or does anything particularly in this case
state->center = ((state->max) + (state->min)) / 2;
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
//Escape conditions to break the loop
//7 seems optimal, allows for an extra frame each in case of late entry on dual voices
if ( (vc1 > 7 && vc2 > 7) )
{
goto END;
}
//14 probably allows a few more in case of a bit of jitter but eventually breaks loop (1 second?)
if ( (vc1 > 14 || vc2 > 14) )
{
goto END;
}
//earlier skip conditions teleport us here to process further skip conditions before restarting while loop
SKIP:
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with processDMRdata
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
{
//set tests to all good so we don't get a bogus/redundant voice error
cach_err = 0;
tact_okay = 1;
emb_ok = 1;
goto END;
}
@@ -595,26 +388,37 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
}
} // while loop
//Teleport here on END condition, set or reset variables and return to look for new frame sync
END:
state->dmr_stereo = 0;
state->errs = 0;
state->errs2 = 0;
state->errs2R = 0;
state->errs2 = 0;
//if we have a cach err, then produce sync pattern/err message
if (cach_okay != 1)
//if we have a tact or emb err, then produce sync pattern/err message
if (tact_okay != 1 || emb_ok != 1)
{
fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** VOICE CACH ERR ** ");
fprintf (stderr, "| VOICE CACH/EMB ERR");
fprintf (stderr, "%s", KNRM);
//run LFSR if either slot had an active MI in it.
state->currentslot = 0;
if (state->payload_algid >= 0x21) LFSR(state);
state->currentslot = 1;
if (state->payload_algidR >= 0x21) LFSR(state);
fprintf (stderr, "\n");
//run LFSR if either slot had an active MI in it.
if (state->payload_algid >= 0x21)
{
state->currentslot = 0;
LFSR(state);
fprintf (stderr, "\n");
}
if (state->payload_algidR >= 0x21)
{
state->currentslot = 1;
LFSR(state);
fprintf (stderr, "\n");
}
}
}
@@ -624,22 +428,28 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
//memcpy of ambe_fr for late entry
char m1[4][24];
char m2[4][24];
char m3[4][24];
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned int EmbeddedSignallingOk;
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
//cach fec
char cachdata[25] = {0}; //increased from 24 to 25 to see if this fixes ubuntu 32 bug
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
char cachdata[25];
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
@@ -678,27 +488,24 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//decode and correct cach and compare
int cach_okay = 69;
if ( Hamming_7_4_decode (cachdata) ) //see if we need to de-interleave this...probably do.
//cach tact bits
uint8_t tact_bits[7];
for (i = 0; i < 7; i++)
{
cach_okay = 1;
tact_bits[i] = cachdata[i];
}
if (cach_okay == 1)
//decode and correct tact and compare
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1)
{
//fprintf (stderr, "CACH Okay ");
}
if (cach_okay != 1)
{
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
if (opts->aggressive_framesync == 1)
{
goto END;
}
}
state->currentslot = cachdata[1];
state->currentslot = tact_bits[1];
//Setup for first AMBE Frame
@@ -751,12 +558,8 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
//this method seems to work to make sure we aren't in a data sync especially when inverted signal
//consider only testing this when inverted signal if this causes issues with sync later on
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
{
goto END;
}
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0) goto END;
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
@@ -795,95 +598,65 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
}
fprintf (stderr,"%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
char polarity[3];
char light[18];
}
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
if (state->currentslot == 0)
{
sprintf (light, "%s", " [SLOT1] slot2 ");
}
else
{
fprintf (stderr,"Sync: -DMR | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
sprintf (light, "%s", " slot1 [SLOT2] ");
}
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
else sprintf (polarity, "%s", "-");
}
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
state->DMRvcL = 0;
state->DMRvcR = 0;
dmr_alg_reset (opts, state);
//copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when
//we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3));
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
//collect the mi fragment
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
cach_err = dmr_cach (opts, state, cachdata);
fprintf (stderr, "\n");
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
END:
//if we have a cach err, then produce sync pattern/err message
if (cach_okay != 1)
//if we have a tact err, then produce sync pattern/err message
if (tact_okay != 1)
{
fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** VOICE CACH ERR ** ");
fprintf (stderr, "| VOICE CACH/EMB ERR");
fprintf (stderr, "%s", KNRM);
//run LFSR if either slot had an active MI in it.
state->currentslot = 0;
if (state->payload_algid >= 0x21) LFSR(state);
state->currentslot = 1;
if (state->payload_algidR >= 0x21) LFSR(state);
fprintf (stderr, "\n");
if (state->payload_algid >= 0x21)
{
state->currentslot = 0;
LFSR(state);
fprintf (stderr, "\n");
}
if (state->payload_algidR >= 0x21)
{
state->currentslot = 1;
LFSR(state);
fprintf (stderr, "\n");
}
}
}

586
src/dmr_csbk.c Normal file
View File

@@ -0,0 +1,586 @@
/*-------------------------------------------------------------------------------
* dmr_csbk.c
* DMR Control Signal Data PDU (CSBK, MBC, UDT) Handler and Related Functions
*
* Portions of Connect+ code reworked from Boatbod OP25
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
*
* Portions of Capacity+ code reworked from Eric Cottrell
* Source: https://github.com/LinuxSheeple-E/dsd/blob/Feature/DMRECC/dmr_csbk.c
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//function for handling Control Signalling PDUs (CSBK, MBC, UDT) messages
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{
int csbk_lb = 0;
int csbk_pf = 0;
int csbk_o = 0;
int csbk_fid = 0;
uint64_t csbk_data = 0;
int csbk = 0;
if(IrrecoverableErrors == 0 && CRCCorrect == 1)
{
csbk_lb = ( (cs_pdu[0] & 0x80) >> 7 );
csbk_pf = ( (cs_pdu[0] & 0x40) >> 6 );
csbk_o = cs_pdu[0] & 0x3F;
csbk_fid = cs_pdu[1]; //feature set id
csbk_pf = ( (cs_pdu[1] & 0x80) >> 7);
csbk = ((cs_pdu[0] & 0x3F) << 8) | cs_pdu[1]; //opcode and fid combo set
//set overarching manufacturer in use when non-standard feature id set is up
if (csbk_fid != 0) state->dmr_mfid = csbk_fid;
//TIII standard with fid of 0 - (opcodes in decimal)
if (0 == 0) //standard feature set for TIII //csbk_fid == 0
{
fprintf (stderr, "%s", KYEL);
//7.1.1.1.1 Channel Grant CSBK/MBC PDU
if (csbk_o > 47 && csbk_o < 53 )
{
//initial line break
fprintf (stderr, "\n");
long int freq = 0;
//all of these messages share the same format (thankfully)
if (csbk_o == 48) fprintf (stderr, " Private Voice Channel Grant (PV_GRANT)");
if (csbk_o == 49) fprintf (stderr, " Talkgroup Voice Channel Grant (TV_GRANT)");
if (csbk_o == 50) fprintf (stderr, " Private Broadcast Voice Channel Grant (BTV_GRANT)");
if (csbk_o == 51) fprintf (stderr, " Private Data Channel Grant: Single Item (PD_GRANT)");
if (csbk_o == 52) fprintf (stderr, " Talkgroup Data Channel Grant: Single Item (TD_GRANT)");
//Logical Physical Channel Number
uint16_t lpchannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 12);
if (lpchannum == 0) fprintf (stderr, " - Invalid Channel"); //invalid channel, not sure why this would even be transmitted
else if (lpchannum == 0xFFF) fprintf (stderr, " - Absolute"); //This is from an MBC, signalling an absolute and not a logical
else fprintf (stderr, " - Logical");
//LCN conveyed here is the tdma timeslot variety, and not the RF frequency variety
uint8_t lcn = cs_pdu_bits[28];
//the next three bits can have different meanings depending on which item above for context
uint8_t st1 = cs_pdu_bits[29]; //late_entry, hi_rate
uint8_t st2 = cs_pdu_bits[30]; //emergency
uint8_t st3 = cs_pdu_bits[31]; //offset, call direction
//target and source are always the same bits
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
fprintf (stderr, "\n");
fprintf (stderr, " CHAN [%03X][%04d] - LCN/TS [%d] - Target [%08d] - Source [%08d]", lpchannum, lpchannum, lcn, target, source);
if (st2) fprintf (stderr, " Emergency");
if (lpchannum == 0xFFF) //This is from an MBC, signalling an absolute and not a logical
{
//7.1.1.1.2 Channel Grant Absolute Parameters CG_AP appended MBC PDU
uint8_t mbc_lb = cs_pdu_bits[96]; //
uint8_t mbc_pf = cs_pdu_bits[97];
uint8_t mbc_csbko = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[98], 6);
uint8_t mbc_res = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[104], 4);
uint8_t mbc_cc = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[108], 4);
uint8_t mbc_cdeftype = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[112], 4); //see 7.2.19.7 = 0 for channel parms, 1 through FFFF reserved
uint8_t mbc_res2 = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[116], 2);
uint64_t mbc_cdefparms = (uint64_t)ConvertBitIntoBytes(&cs_pdu_bits[118], 58); //see 7.2.19.7.1
//this is how you read the 58 parm bits according to the appendix 7.2.19.7.1
if (mbc_cdeftype == 0) //if 0, then absolute channel parms
{
uint16_t mbc_lpchannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[118], 12);
uint16_t mbc_abs_tx_int = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[130], 10);
uint16_t mbc_abs_tx_step = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[140], 13);
uint16_t mbc_abs_rx_int = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[153], 10);
uint16_t mbc_abs_rx_step = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[163], 13);
//tx_int (Mhz) + (tx_step * 125) = tx_freq
//rx_int (Mhz) + (rx_step * 125) = rx_freq
fprintf (stderr, "\n");
fprintf (stderr, " ABS-CHAN [%03X][%04d] - RX INT [%d][%X] - RX STEP [%d][%X]", mbc_lpchannum, mbc_lpchannum, mbc_abs_rx_int, mbc_abs_rx_int, mbc_abs_rx_step, mbc_abs_rx_step );
//DOULBE CHECK APPENDIX C - C.1.1.2 Fixed Channel Plan for accurate information (found it after figuring out calc, so might be worth double checking)
//The Frequency we want to tune is the RX Frequency
freq = (mbc_abs_rx_int * 1000000 ) + (mbc_abs_rx_step * 125);
//tx_freq = (mbc_abs_tx_int * 1000000 ) + (mbc_abs_tx_step * 125);
}
}
//print frequency from absolute
if (freq != 0 && lpchannum == 0xFFF) fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
//run external channel map function on logical
if (lpchannum != 0 && lpchannum != 0xFFF)
{
freq = state->trunk_chan_map[lpchannum];
(stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
}
//if not a data channel grant (only tuning to voice channel grants)
if (csbk_o < 51) //48, 49, 50 are voice grants, 51 and 52 are data grants
{
//shim in chan map 0 as the cc frequency, user will need to specify it in the channel map file
if (state->p25_cc_freq != 0 && state->trunk_chan_map[0] != 0) state->p25_cc_freq = state->trunk_chan_map[0];
//shim in here for ncurses freq display when not trunking (playback, not live)
if (opts->p25_trunk == 0 && freq != 0)
{
//just set to both for now, could go on tslot later
state->p25_vc_freq[0] = freq;
state->p25_vc_freq[1] = freq;
}
char mode[8]; //allow, block, digital, enc, etc
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == target)
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
}
}
if (state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0))
{
if (freq != 0) //if we have a valid frequency
{
//RIGCTL
if (opts->use_rigctl == 1)
{
SetModulation(opts->rigctl_sockfd, 12500); //bw depends on system strength more than anything, 12.5 should be safe for DMR
SetFreq(opts->rigctl_sockfd, freq); //minus one because our index starts at zero
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
}
}
//rtl_udp
else if (opts->audio_in_type == 3)
{
rtl_udp_tune (opts, state, freq);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
opts->p25_is_tuned = 1;
}
}
}
}
//Move
if (csbk_o == 57)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " Move (C_MOVE) (TODO)");
}
//Aloha
if (csbk_o == 25)
{
//initial line break
fprintf (stderr, "\n");
uint8_t reserved = cs_pdu_bits[16];
uint8_t tsccas = cs_pdu_bits[17];
uint8_t sync = cs_pdu_bits[18];
uint8_t version = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[19], 3); //Document Version Control
uint8_t offset = cs_pdu_bits[22]; //0-TSCC uses aligned timing; 1-TSCC uses offset timing
uint8_t active = cs_pdu_bits[23]; //Active_Connection
uint8_t mask = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24], 5);
uint8_t sf = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[29], 2); //service function
uint8_t nrandwait = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[31], 4);
uint8_t regreq = cs_pdu_bits[35];
uint8_t backoff = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[36], 4);
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
uint16_t net = 0;
uint16_t site = 0;
char model_str[8];
sprintf (model_str, "%s", " ");
if (model == 0) //Tiny
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 9);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[51], 3);
sprintf (model_str, "%s", "Tiny");
}
else if (model == 1) //Small
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 7);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[49], 5);
sprintf (model_str, "%s", "Small");
}
else if (model == 2) //Large
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 4);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[46], 8);
sprintf (model_str, "%s", "Large");
}
else if (model == 3) //Huge
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 2);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[44], 10);
sprintf (model_str, "%s", "Huge");
}
uint8_t par = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[54], 2);
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
fprintf (stderr, " C_ALOHA_SYS_PARMS - %s - Net ID: %d Site ID: %d Par: %d \n Reg Req: %d V: %d MS: %d", model_str, net, site, par, regreq, version, target);
//fprintf (stderr, " Sys ID Code: [%04X]", sysidcode);
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
//fprintf (stderr, "\n SYSCODE: %016b", syscode);
}
//P_CLEAR
if (csbk_o == 46)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " Clear (P_CLEAR) (TODO)");
}
//(P_PROTECT) - Could be a useful way of determining who is on the current timeslot p_kind 2
if (csbk_o == 47)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " Protect (P_PROTECT) -");
uint16_t reserved = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 12);
uint8_t p_kind = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28], 3);
uint8_t gi = cs_pdu_bits[31];
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
if (p_kind == 0) fprintf (stderr, " Disable Target PTT (DIS_PTT)");
if (p_kind == 1) fprintf (stderr, " Enable Target PTT (EN_PTT)");
if (p_kind == 2) fprintf (stderr, " Private Call (ILLEGALLY_PARKED)");
if (p_kind == 3) fprintf (stderr, " Enable Target MS PTT(EN_PTT_ONE_MS)");//EN_PTT_ONE_MS
fprintf (stderr, "\n");
fprintf (stderr, " Target [%08d] - Source [%08d]", target, source);
//experimental, but this may work work if we stay on this channel
//may be reversed considering we won't know exactly who is going to answer first
//only good for an initial protect clear all as well
if (p_kind == 2)
{
//don't set this if operating out of dmr mono or using call alert beep
if (opts->dmr_mono == 0 && opts->call_alert == 0)
{
if (state->currentslot == 0)
{
state->lasttg = target;
state->lastsrc = source;
}
else
{
state->lasttgR = target;
state->lastsrcR = source;
}
}
}
}
if (csbk_o == 40) //0x28 //check to see if these are fid 0 only, or also in other manufacturer fids
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " Announcements (C_BCAST)"); //site it, vote for etc etc
uint8_t a_type = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 5);
if (a_type == 0) fprintf (stderr, " Announce/Withdraw TSCC (Ann_WD_TSCC)");
if (a_type == 1) fprintf (stderr, " Specify Call Timer Parameters (CallTimer_Parms)");
if (a_type == 2) fprintf (stderr, " Vote Now Advice (Vote_Now)");
if (a_type == 3) fprintf (stderr, " Broadcast Local Time (Local_Time)");
if (a_type == 4) fprintf (stderr, " Mass Registration (MassReg)");
if (a_type == 5) fprintf (stderr, " Announce Logical Channel/Frequency Relationship (Chan_Freq)");
if (a_type == 6) fprintf (stderr, " Adjacent Site Information (Adjacent_Site)");
if (a_type == 7) fprintf (stderr, " General Site Parameters (Gen_Site_Params)");
if (a_type > 7 && a_type < 0x1E) fprintf (stderr, " Reserved %02X", a_type);
if (a_type == 0x1E || a_type == 0x1F) fprintf (stderr, " Manufacturer Specific %02X", a_type);
//sysidcode, probably would have been easier to make this its own external function
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
uint16_t net = 0;
uint16_t site = 0;
char model_str[8];
sprintf (model_str, "%s", " ");
if (model == 0) //Tiny
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 9);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[51], 3);
sprintf (model_str, "%s", "Tiny");
}
else if (model == 1) //Small
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 7);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[49], 5);
sprintf (model_str, "%s", "Small");
}
else if (model == 2) //Large
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 4);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[46], 8);
sprintf (model_str, "%s", "Large");
}
else if (model == 3) //Huge
{
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 2);
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[44], 10);
sprintf (model_str, "%s", "Huge");
}
uint8_t par = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[54], 2);
uint32_t parms2 = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
fprintf (stderr, "\n");
fprintf (stderr, " C_BCAST_SYS_PARMS - %s - Net ID: %d Site ID: %d Par: %d ", model_str, net, site, par);
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
//fprintf (stderr, "\n SYSCODE: %016b", syscode);
}
if (csbk == 28)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " C_AHOY (TODO)");
}
if (csbk == 42)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " P_MAINT (TODO)");
}
if (csbk == 30)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " C_ACKVIT (TODO)");
}
if (csbk == 31)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, " C_RAND (TODO)");
}
}
//Capacity+ Section
if (csbk_fid == 0x10)
{
//will need to ponder the best way to do trunking rules for this
if (csbk_o == 0x3E)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
uint8_t fl = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 2);
uint8_t slot = cs_pdu_bits[18];
uint8_t rest_channel = cs_pdu[2] & 0xF; //0xF, or 0x1F?
uint8_t ch[8]; //one bit per channel
uint8_t tg = 0;
uint32_t tghex = 0; //combined all tgs for debug
int i, k;
k = 0;
if (rest_channel != state->dmr_rest_channel)
{
state->dmr_rest_channel = rest_channel;
}
for (int i = 0; i < 8; i++)
{
ch[i] = cs_pdu_bits[i+24];
}
fprintf (stderr, " Capacity Plus Channel Status - Rest Channel %d", rest_channel);
fprintf (stderr, "\n ");
for (i = 0; i < 8; i++)
{
fprintf (stderr, "Ch%d: ", i+1);
if (ch[i] != 0)
{
tg = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[k*8+32], 8);
fprintf (stderr, " %03d ", tg);
k++;
}
else if (i+1 == rest_channel) fprintf (stderr, "Rest ");
else fprintf (stderr, "Idle ");
if (i == 3) fprintf (stderr, "\n ");
}
tghex = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
//fprintf (stderr, "\n TG Hex = 0x%06X", tghex);
state->dmr_mfid = 0x10;
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
fprintf (stderr, "%s", KNRM);
}
}
//Connect+ Section
if (csbk_fid == 0x06)
{
if (csbk_o == 0x01)
{
//initial line break
fprintf (stderr, "\n");
uint8_t nb1 = cs_pdu[2] & 0x3F;
uint8_t nb2 = cs_pdu[3] & 0x3F;
uint8_t nb3 = cs_pdu[4] & 0x3F;
uint8_t nb4 = cs_pdu[5] & 0x3F;
uint8_t nb5 = cs_pdu[6] & 0x3F;
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Neighbors\n");
fprintf (stderr, " NB1(%02d), NB2(%02d), NB3(%02d), NB4(%02d), NB5(%02d)", nb1, nb2, nb3, nb4, nb5);
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
}
if (csbk_o == 0x03)
{
//shim in chan map 0 as the cc frequency, user will need to specify it in the channel map file
if (state->p25_cc_freq != 0 && state->trunk_chan_map[0] != 0) state->p25_cc_freq = state->trunk_chan_map[0];
//initial line break
fprintf (stderr, "\n");
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 );
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 );
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Voice Channel Grant\n");
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
// state->dmr_vc_lcn = lcn;
// state->dmr_vc_lsn = lcn * (tslot+1);
//shim in here for ncurses freq display when not trunking (playback, not live)
if (opts->p25_trunk == 0 && state->trunk_chan_map[lcn] != 0)
{
//just set to both for now, could go on tslot later
state->p25_vc_freq[0] = state->trunk_chan_map[lcn];
state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
}
char mode[8]; //allow, block, digital, enc, etc
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == grpAddr)
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
}
}
//need channel map frequencies and stuff, also way to figure out control channel frequency? (channel 0 from channel map?)
if (state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0))
{
if (state->trunk_lcn_freq[lcn] != 0) //if we have a valid frequency
{
//RIGCTL
if (opts->use_rigctl == 1)
{
SetModulation(opts->rigctl_sockfd, 12500); //bw depends on system strength more than anything, 12.5 should be safe for DMR
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[lcn]); //minus one because our index starts at zero
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
}
}
//rtl_udp
else if (opts->audio_in_type == 3)
{
rtl_udp_tune (opts, state, state->trunk_chan_map[lcn]);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
opts->p25_is_tuned = 1;
}
}
}
if (csbk_o == 0x06)
{
//initial line break
fprintf (stderr, "\n");
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 ) ;
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 );
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Data Channel Grant\n");
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
}
if (csbk_o == 0x0C)
{
//initial line break
fprintf (stderr, "\n");
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 ) ; ////extract(csbk, 64, 68); //double check these?
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 ); //csbk[68];
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Terminate Channel Grant\n"); //Data only shows a srcAddr??
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
}
if (csbk_o == 0x11)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Registration Request");
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
}
if (csbk_o == 0x12)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Registration Response");
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
}
if (csbk_o == 0x18)
{
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Connect Plus Talkgroup Affiliation");
state->dmr_mfid = 0x06;
sprintf(state->dmr_branding_sub, "Con+ ");
}
fprintf (stderr, "%s", KNRM);
}
}
END:
fprintf (stderr, "%s", KNRM);
}

View File

@@ -18,35 +18,32 @@
#include "dsd.h"
void
processDMRdata (dsd_opts * opts, dsd_state * state)
dmr_data_sync (dsd_opts * opts, dsd_state * state)
{
int i, dibit;
int *dibit_p;
char sync[25];
char syncdata[48];
char cachdata[25] = {0};
char cc[5] = {0};
char ccAscii[5] = {0};
char bursttype[5];
unsigned int burst;
char info[196];
char cachdata[25];
char bursttype[5];
uint8_t burst;
char info[196];
unsigned char SlotType[20];
unsigned int SlotTypeOk;
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
uint8_t cach_err = 0;
#ifdef DMR_DUMP
char syncbits[49];
char cachbits[25];
int k;
#endif
ccAscii[4] = 0;
bursttype[4] = 0;
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
dibit_p = state->dmr_payload_p - 90;
// CACH
//collect cach and de-interleave
for (i = 0; i < 12; i++)
{
dibit = *dibit_p;
@@ -65,67 +62,34 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
}
//decode and correct cach and compare
int cach_okay = 69;
if ( Hamming_7_4_decode (cachdata) ) //is now de-interleaved appropriately
//seperate tact bits from cach
uint8_t tact_bits[7];
for (i = 0; i < 7; i++)
{
cach_okay = 1;
tact_bits[i] = cachdata[i];
}
if (cach_okay == 1)
//run hamming on tact bits
int cach_okay = -1;
if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1;
else
{
//fprintf (stderr, "CACH Okay ");
}
if (cach_okay != 1)
{
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
if (opts->aggressive_framesync == 1) //may not worry about it on data
cach_okay = -1;
if (opts->aggressive_framesync == 2)
{
SlotTypeOk = 0;
goto END;
}
}
state->currentslot = cachdata[1];
state->currentslot = tact_bits[1];
//minor bugfix for MSData when MSData has accumulated data blocks
//(i.e. data header and 1/2 rate data), force all data into slot 0
//in the future, maybe we will remove the hard set on this
if (state->dmr_ms_mode == 1)
{
state->currentslot = 0;
}
if (state->currentslot == 0 && state->dmr_ms_mode == 0)
{
state->slot1light[0] = '[';
state->slot1light[6] = ']';
state->slot2light[0] = ' ';
state->slot2light[6] = ' ';
}
//else
if (state->currentslot == 1 && state->dmr_ms_mode == 0)
{
state->slot2light[0] = '[';
state->slot2light[6] = ']';
state->slot1light[0] = ' ';
state->slot1light[6] = ' ';
}
//end correct cach and set slot
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
fprintf(stderr, "%s ", cachbits);
#endif
//trellis bits
unsigned char trellisdibits[98];
// Current slot - First half - Data Payload - 1st part
for (i = 0; i < 49; i++)
{
@@ -139,7 +103,6 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
{
dibit = (int)state->dmr_stereo_payload[i+12];
}
trellisdibits[i] = dibit;
info[2*i] = (1 & (dibit >> 1)); // bit 1
info[(2*i)+1] = (1 & dibit); // bit 0
}
@@ -153,10 +116,9 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[61]; //61, not i+61
dibit = (int)state->dmr_stereo_payload[61];
}
cc[0] = (1 & (dibit >> 1)); // bit 1
cc[1] = (1 & dibit); // bit 0
SlotType[0] = (1 & (dibit >> 1)); // bit 1
SlotType[1] = (1 & dibit); // bit 0
@@ -170,17 +132,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
{
dibit = (int)state->dmr_stereo_payload[62];
}
cc[2] = (1 & (dibit >> 1)); // bit 1
cc[3] = (1 & dibit); // bit 0
SlotType[2] = (1 & (dibit >> 1)); // bit 1
SlotType[3] = (1 & dibit); // bit 0
ccAscii[0] = cc[0] + '0';
ccAscii[1] = cc[1] + '0';
ccAscii[2] = cc[2] + '0';
ccAscii[3] = cc[3] + '0';
ccAscii[4] = cc[4] + '\0';
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
@@ -189,7 +144,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
}
if (state->dmr_stereo == 1) //state
{
dibit = (int)state->dmr_stereo_payload[63]; //(int)
dibit = (int)state->dmr_stereo_payload[63];
}
SlotType[4] = (1 & (dibit >> 1)); // bit 1
@@ -203,7 +158,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
}
if (state->dmr_stereo == 1) //state
{
dibit = (int)state->dmr_stereo_payload[64]; //(int)
dibit = (int)state->dmr_stereo_payload[64];
}
SlotType[6] = (1 & (dibit >> 1)); // bit 1
@@ -223,7 +178,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
SlotType[8] = (1 & (dibit >> 1)); // bit 1
SlotType[9] = (1 & dibit); // bit 0
// signalling data or sync
// signaling data or sync
for (i = 0; i < 24; i++)
{
dibit = *dibit_p;
@@ -239,32 +194,19 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 24; i++)
{
syncbits[k] = syncdata[(k*2)] + 48; // bit 1
k++;
syncbits[k] = syncdata[(k*2)+1] + 48; // bit 0
k++;
}
syncbits[48] = 0;
fprintf(stderr, "%s ", syncbits);
#endif
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )//|| (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )
{
if (state->currentslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
}
else
{
sprintf(state->slot1light, " slot1 ");
sprintf(state->slot2light, "[slot2]");
}
}
@@ -273,15 +215,17 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
{
state->currentslot = 0;
sprintf(state->slot1light, "[sLoT1]");
sprintf(state->slot2light, "[DMODE]");
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
{
state->currentslot = 1;
sprintf(state->slot1light, "[DMODE]");
sprintf(state->slot2light, "[sLoT2]");
}
if (opts->errorbars == 1 && state->dmr_ms_mode == 0)
if (state->dmr_ms_mode == 0)
{
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
}
@@ -289,283 +233,85 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
// Slot type - Second part - Parity bit
for (i = 0; i < 5; i++)
{
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
if (state->dmr_stereo == 0)
{
dibit = getDibit(opts, state);
}
if (opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i+90]; //double checked these i+ values make sure we are on the right ones
dibit = (int)state->dmr_stereo_payload[i+90];
}
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
}
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
if( Golay_20_8_decode(SlotType) )
{
SlotTypeOk = 1;
//fprintf (stderr, "Slot Okay ");
}
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
else
{
SlotTypeOk = -1;
if (opts->aggressive_framesync == 1)
{
SlotTypeOk = 0;
goto END;
}
}
/* Slot Type parity checked => Fill the color code */
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
state->color_code_ok = SlotTypeOk;
/* Reconstitute the burst type */
//consider assigning this only when slottypeok or similar check passes, eliminate bad burst types from decoding randomly
burst = (unsigned int)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
if (state->currentslot == 0)
{
state->dmrburstL = burst;
}
if (state->currentslot == 1)
{
state->dmrburstR = burst;
}
//not sure why I still have two variables for this, need to look and see what state->color_code still ties into
if (SlotTypeOk == 1) state->dmr_color_code = state->color_code;
/* Reconstitute the burst type */
bursttype[0] = SlotType[4] + '0';
bursttype[1] = SlotType[5] + '0';
bursttype[2] = SlotType[6] + '0';
bursttype[3] = SlotType[7] + '0';
bursttype[4] = '\0';
burst = (uint8_t)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
if (state->currentslot == 0) state->dmrburstL = burst;
if (state->currentslot == 1) state->dmrburstR = burst;
if (strcmp (bursttype, "0000") == 0)
{
sprintf(state->fsubtype, " PI Header ");
}
else if (strcmp (bursttype, "0001") == 0)
{
sprintf(state->fsubtype, " VOICE LC ");
}
else if (strcmp (bursttype, "0010") == 0)
{
sprintf(state->fsubtype, " TLC ");
}
else if (strcmp (bursttype, "0011") == 0)
{
sprintf(state->fsubtype, " CSBK ");
}
else if (strcmp (bursttype, "0100") == 0)
{
sprintf(state->fsubtype, " MBC Header ");
}
else if (strcmp (bursttype, "0101") == 0)
{
sprintf(state->fsubtype, " MBC ");
}
else if (strcmp (bursttype, "0110") == 0)
{
sprintf(state->fsubtype, " DATA Header ");
}
else if (strcmp (bursttype, "0111") == 0)
{
sprintf(state->fsubtype, " RATE 1/2 DATA");
}
else if (strcmp (bursttype, "1000") == 0)
{
sprintf(state->fsubtype, " RATE 3/4 DATA");
}
else if (strcmp (bursttype, "1001") == 0)
{
sprintf(state->fsubtype, " Slot idle ");
}
else if (strcmp (bursttype, "1010") == 0)
{
sprintf(state->fsubtype, " Rate 1 DATA ");
}
else
{
sprintf(state->fsubtype, " ");
}
// Current slot - First half - Data Payload - 1st part
//trellis
// Current slot - Second Half - Data Payload - 2nd part
for (i = 0; i < 49; i++)
{
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
if (state->dmr_stereo == 0)
{
dibit = getDibit(opts, state);
}
if (opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i+95]; //double checked these i+ values make sure we are on the right ones
dibit = (int)state->dmr_stereo_payload[i+95];
}
trellisdibits[i+49] = dibit;
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
info[(2*i) + 99] = (1 & dibit); // bit 0
}
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
if (state->dmr_stereo == 0)
//don't run cach on simplex or mono
if (state->dmr_ms_mode == 0 || opts->dmr_mono == 0)
{
skipDibit (opts, state, 12 + 49 + 5);
}
cach_err = dmr_cach (opts, state, cachdata);
}
if (opts->errorbars == 1)
{
/* Print the color code */
//if (burst == 0b1001 && state->color_code_ok) //only print on idle for data types, otherwise prints on voice frames.
if (SlotTypeOk == 1)
{
fprintf (stderr, "%s", KCYN);
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
fprintf (stderr, "%s", KNRM);
//state->dmr_color_code = state->color_code;
}
else fprintf(stderr, "| ");
//ending line break
fprintf(stderr, "\n");
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
if(state->color_code_ok) fprintf(stderr, "| (CRC OK ) |"); //add line break
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "| (CRC ERR) |");
fprintf (stderr, "%s", KNRM);
}
//fprintf (stderr, "\n"); //print line break
if (strcmp (state->fsubtype, " ") == 0)
{
//fprintf(stderr, " Unknown burst type: %s", bursttype);
fprintf(stderr, " Unknown");
}
else
{
fprintf(stderr, "%s", state->fsubtype);
}
}
switch(burst)
{
/* Burst = PI header */
case 0b0000:
{
ProcessDmrPIHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = Voice LC header */
case 0b0001:
{
/* Extract data from Voice LC Header */
ProcessDmrVoiceLcHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = TLC */
case 0b0010:
{
//Not sure if I really care about this in the context of what it shows
ProcessDmrTerminaisonLC(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = CSBK */
case 0b0011:
{
//going to disable until I can find more useful info to present
ProcessCSBK(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = MBC Header */
case 0b0100:
{
ProcessMBChData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = MBC Continuation*/
case 0b0101:
{
ProcessMBCData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = DATA Header */
case 0b0110:
{
ProcessDataData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = RATE 1/2 DATA */
case 0b0111:
{
Process12Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = RATE 3/4 DATA */
case 0b1000:
{
//need to keep working on improving the trellis decode, but is partially viable now
Process34Data(opts, state, trellisdibits, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = Slot idle */
case 0b1001:
{
if(state->color_code_ok && state->dmr_stereo == 0) state->dmr_color_code = state->color_code; //try setting this on idle if crc ok
break;
}
/* Burst = Rate 1 DATA */
case 0b1010:
{
Process1Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = Unified Single Block DATA */
case 0b1101:
{
ProcessUnifiedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Default */
default:
{
/* Nothing to do */
ProcessReservedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
} /* End switch(burst) */
if (opts->errorbars == 1)
{
fprintf(stderr, "\n");
}
END:
if (SlotTypeOk == 0 || cach_okay != 1)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
fprintf (stderr, "| CACH/Burst FEC ERR");
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
} /* End processDMRdata() */
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
if (state->dmr_stereo == 0)
{
skipDibit (opts, state, 12 + 49 + 5);
}
}

446
src/dmr_dburst.c Normal file
View File

@@ -0,0 +1,446 @@
/*-------------------------------------------------------------------------------
* 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
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
//TODO: CRC9 on confirmed data blocks; CRC32 on completed data sf;
//TODO: Embedded GPS Calculations
//TODO: MBC full message CRC16 (does this include the header or not though?)
//TODO: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC
//TODO: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
#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;
uint8_t block = state->data_block_counter[slot];
//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;
//Unified PDU (is this the best nomenclature) Bytes
uint8_t DMR_PDU[25]; //using 25 bytes as the max for now
uint8_t DMR_PDU_bits[196]; //bitwise version of unified payload, will probably be a lot easier to use this w/ manual
memset (DMR_PDU, 0, sizeof (DMR_PDU));
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
uint8_t R[3];
uint8_t BPTCReservedBits = 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 pdu_len = 0;
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
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;
//TODO - 1/2 0x0F0 - 3/4 0x1FF - Rate 1 0x10F
//going to set crclen and mask according to confirmed data here,
//use block assmebler to carry out completed message crc
case 0x07: //1/2 Rate Data
is_bptc = 1;
crclen = 9; //confirmed data only
crcmask = 0x0F0;
pdu_len = 12; //12 bytes unconfirmed
if (state->data_conf_data[slot] == 1)
{
pdu_len = 10;
pdu_start = 2; //start at plus two when assembling
}
sprintf(state->fsubtype, " R12D ");
break;
case 0x08: //3/4 Rate Data
is_trellis = 1;
crclen = 9; //confirmed data only
crcmask = 0x1FF;
pdu_len = 18; //18 bytes unconfirmed
if (state->data_conf_data[slot] == 1)
{
pdu_len = 16;
pdu_start = 2; //start at plus two when assembling
}
sprintf(state->fsubtype, " R34D ");
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 = 0x1FF;
is_full = 1;
pdu_len = 25; //196 bits - 24.5 bytes
sprintf(state->fsubtype, " R_1D ");
break;
case 0x0B: //Unified Data
is_bptc = 1;
crclen = 16;
crcmask = 0x3333;
pdu_len = 12; //12 bytes
sprintf(state->fsubtype, " UDAT ");
break;
//special types (not real data 'sync' bursts)
case 0xEB: //Embedded Signalling
is_emb = 1;
pdu_len = 9;
break;
default:
break;
}
if (databurst != 0xEB)
{
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
fprintf(stderr, "|%s", state->fsubtype);
}
//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, R);
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x08);
/* Convert the 96 bits BPTC data into 12 bytes */
k = 0;
for(i = 0; i < 12; i++)
{
BPTCDmrDataByte[i] = 0;
for(j = 0; j < 8; j++)
{
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
k++;
}
}
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
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) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
//TODO: run CRC9 on confirmed 1/2 data blocks
else if (state->data_conf_data[slot] == 1)
{
//run CRC on individual blocks according to type and validate each individual block
}
//run CCITT on other data forms
else //will still run on unconfirmed data (shouldn't matter other than a possible bogus warning message)
{
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
else CRCCorrect = 0;
}
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 */
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
{
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
}
//convert to unified 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 = (LC_DataBit[72] & 1) << 4;
CRCExtracted |= (LC_DataBit[73] & 1) << 3;
CRCExtracted |= (LC_DataBit[74] & 1) << 2;
CRCExtracted |= (LC_DataBit[75] & 1) << 1;
CRCExtracted |= (LC_DataBit[76] & 1) << 0;
/* Compute the 5 bit CRC */
CRCComputed = ComputeCrc5Bit(LC_DataBit);
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 ugly one
if (is_trellis)
{
CRCExtracted = 0;
CRCComputed = 0;
IrrecoverableErrors = 1; //can't be tested, but maybe can poll trellis decoder for a value in the future
uint8_t tdibits[98];
memset (tdibits, 0, sizeof(tdibits));
//reconstitute info bits into reverse ordered dibits for the trellis decoder
for (i = 0; i < 98; i++)
{
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
}
unsigned char TrellisReturn[18];
memset (TrellisReturn, 0, sizeof(TrellisReturn));
CDMRTrellisDecode(tdibits, TrellisReturn);
for (i = 0; i < pdu_len; i++) //18
{
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
}
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;
}
//TODO: run CRC9 on confirmed 3/4 data blocks
}
if (is_full) //Rate 1 Data
{
CRCExtracted = 0;
CRCComputed = 0;
IrrecoverableErrors = 0;
//TODO: run CRC9 on confirmed data
for (i = 0; i < 24; i++)
{
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
}
//leftover 4 bits, manual doesn't say which 4 so assuming last 4
//pushing last 4 to 25 and shifting it
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
DMR_PDU[25] = DMR_PDU[25] << 4;
//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, Unified) - type 1
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
if (databurst == 0x07) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
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 - Disabled until further testing can be done
//is unified data handled like normal data, or control signalling data?
if (databurst == 0x0B) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Unified Data
//if (databurst == 0x0B) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
//control signalling types (CSBK, MBC, UDT)
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
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);
//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++)
{
fprintf (stderr, "[%02X]", DMR_PDU[i]);
}
fprintf (stderr, "%s", KNRM);
// fprintf (stderr, "%s", KCYN);
// fprintf (stderr, "\n Hex to Ascii - ");
// for (i = 0; i < 12; i++)
// {
// if (DMR_PDU[i] <= 0x7E && DMR_PDU[i] >=0x20)
// {
// fprintf (stderr, "%c", DMR_PDU[i]);
// }
// else fprintf (stderr, ".");
// }
fprintf (stderr,"%s", KNRM);
}
}

587
src/dmr_flco.c Normal file
View File

@@ -0,0 +1,587 @@
/*-------------------------------------------------------------------------------
* dmr_flco.c
* DMR Full Link Control, Short Link Control, TACT/CACH and related funtions
*
* Portions of link control/voice burst/vlc/tlc from LouisErigHerve
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//combined flco handler (vlc, tlc, emb), minus the superfluous structs and strings
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type)
{
//force slot to 0 if using dmr mono handling
if (opts->dmr_mono == 1) state->currentslot = 0;
uint8_t pf = 0;
uint8_t reserved = 0;
uint8_t flco = 0;
uint8_t fid = 0;
uint8_t so = 0;
uint32_t target = 0;
uint32_t source = 0;
int restchannel = -1;
int is_cap_plus = 0;
int is_alias = 0;
int is_gps = 0;
pf = (uint8_t)(lc_bits[0]); //Protect Flag
reserved = (uint8_t)(lc_bits[1]); //Reserved
flco = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //Full Link Control Opcode
fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8); //Feature set ID (FID)
so = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //Service Options
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
if (type == 1 && fid == 0x10 && flco == 0x04)
{
is_cap_plus = 1;
restchannel = (int)ConvertBitIntoBytes(&lc_bits[48], 8);
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
}
//Embedded Talker Alias Header Only (format and len storage)
if (IrrecoverableErrors == 0)
{
if (type == 3 && flco == 0x04)
{
dmr_embedded_alias_header(opts, state, lc_bits);
}
//Embedded Talker Alias Header (continuation) and Blocks
if (type == 3 && flco > 0x3 && flco < 0x08)
{
is_alias = 1;
dmr_embedded_alias_blocks(opts, state, lc_bits);
}
//Embedded GPS
if (type == 3 && flco == 0x08)
{
is_gps = 1;
dmr_embedded_gps(opts, state, lc_bits);
}
}
//will want to continue to observe for different flco and fid combinations to find out their meaning
//Standard Addressing/Cap+ Addressing (trying to avoid embedded alias and gps, etc)
if(IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0)
{
//set overarching manufacturer in use when non-standard feature id set is up
//may not want to set moto 0x10 here either, lots of radios use that set as well
if (fid != 0) state->dmr_mfid = fid;
if (type != 2) //VLC and EMB, set our target, source, so, and fid per channel
{
if (state->currentslot == 0)
{
state->dmr_fid = fid;
state->dmr_so = so;
state->lasttg = target;
state->lastsrc = source;
}
if (state->currentslot == 1)
{
state->dmr_fidR = fid;
state->dmr_soR = so;
state->lasttgR = target;
state->lastsrcR = source;
}
}
if (type == 2) //TLC, zero out target, source, so, and fid per channel, and other odd and ends
{
//I wonder which of these we truly want to zero out, possibly none of them
if (state->currentslot == 0)
{
// state->dmr_fid = 0;
// state->dmr_so = 0;
state->lasttg = 0;
state->lastsrc = 0;
state->payload_mi = 0;
}
if (state->currentslot == 1)
{
// state->dmr_fidR = 0;
// state->dmr_soR = 0;
state->lasttgR = 0;
state->lastsrcR = 0;
state->payload_miR = 0;
}
}
if (restchannel != state->dmr_rest_channel && restchannel != -1)
{
state->dmr_rest_channel = restchannel;
}
if (type == 1) fprintf (stderr, "%s \n", KGRN);
if (type == 2) fprintf (stderr, "%s \n", KRED);
if (type == 3) fprintf (stderr, "%s", KGRN);
fprintf (stderr, " SLOT %d ", state->currentslot+1);
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
if (flco == 3) fprintf (stderr, "Private "); //UU_V_Ch_Usr
else fprintf (stderr, "Group "); //Grp_V_Ch_Usr
if(so & 0x80) fprintf(stderr, "Emergency ");
if(so & 0x40)
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "Encrypted ");
}
/* Check the "Service Option" bits */
if(so & 0x30)
{
/* Experimentally determined with DSD+,
* is equal to 0x2, this is a TXI call */
if((so & 0x30) == 0x20) fprintf(stderr, "TXI ");
else fprintf(stderr, "Reserved=%d ", (so & 0x30) >> 4);
}
if(so & 0x08) fprintf(stderr, "Broadcast ");
if(so & 0x04) fprintf(stderr, "OVCM ");
if(so & 0x03)
{
if((so & 0x03) == 0x01) fprintf(stderr, "Priority 1 ");
else if((so & 0x03) == 0x02) fprintf(stderr, "Priority 2 ");
else if((so & 0x03) == 0x03) fprintf(stderr, "Priority 3 ");
else fprintf(stderr, "No Priority "); /* We should never go here */
}
fprintf(stderr, "Call ");
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/RAS)");
else fprintf(stderr, "(FEC/CRC ERR)");
//check Cap+ rest channel info if available and good fec
if (is_cap_plus == 1)
{
if (restchannel != -1)
{
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "Cap+ R-Ch %d", restchannel);
}
}
fprintf (stderr, "%s ", KNRM);
//group labels
for (int i = 0; i < state->group_tally; i++)
{
//ROMAN! Change target to source if you prefer
if (state->group_array[i].groupNumber == target)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, "[%s] ", state->group_array[i].groupName);
fprintf (stderr, "%s", KNRM);
}
}
if (state->K != 0 && fid == 0x10 && so & 0x40)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key %lld ", state->K);
fprintf (stderr, "%s ", KNRM);
}
if (state->K1 != 0 && fid == 0x68 && so & 0x40)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Key %010llX ", state->K1);
if (state->K2 != 0) fprintf (stderr, "%016llX ", state->K2);
if (state->K4 != 0) fprintf (stderr, "%016llX %016llX", state->K3, state->K4);
fprintf (stderr, "%s ", KNRM);
}
}
}
//externalized dmr cach - tact and slco fragment handling
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25])
{
int i, j;
uint8_t err = 0;
uint8_t tact_valid = 0;
uint8_t cach_valid = 0;
bool h1, h2, h3, crc;
//dump payload - testing
uint8_t slco_raw_bits[68]; //raw
uint8_t slco_bits[68]; //de-interleaved
//tact pdu
uint8_t tact_bits[7];
uint8_t at = 0; //access type, set to 1 during continuous transmission mode
uint8_t slot = 2; //tdma time slot
uint8_t lcss = 0; //link control start stop (9.3.3) NOTE: There is no Single fragment LC defined for CACH signalling
//cach_bits are already de-interleaved upon initial collection (still needs secodary slco de-interleave)
for (i = 0; i < 7; i++)
{
tact_bits[i] = cach_bits[i];
}
//run hamming 7_4 on the tact_bits (redundant, since we do it earlier, but need the lcss)
if ( Hamming_7_4_decode (tact_bits) )
{
at = tact_bits[0]; //any useful tricks with this? csbk on/off etc?
slot = tact_bits[1]; //
lcss = (tact_bits[2] << 1) | tact_bits[3];
tact_valid = 1; //set to 1 for valid, else remains 0.
//fprintf (stderr, "AT=%d LCSS=%d - ", at, lcss); //debug print
}
else //probably skip/memset/zeroes with else statement?
{
//do something?
err = 1;
//return (err);
}
//determine counter value based on lcss value
if (lcss == 0) //run as single block/fragment NOTE: There is no Single fragment LC defined for CACH signalling (but is mentioned in the manual table)
{
//reset the full cach, even if we aren't going to use it, may be beneficial for next time
state->dmr_cach_counter = 0; //first fragment, set to zero.
memset (state->dmr_cach_fragment, 0, sizeof (state->dmr_cach_fragment));
//seperate
for (i = 0; i < 17; i++)
{
slco_raw_bits[i] = cach_bits[i+7];
}
//De-interleave
int src = 0;
for (i = 0; i < 17; i++)
{
src = (i * 4) % 67;
slco_bits[i] = slco_raw_bits[src];
}
//slco_bits[i] = slco_raw_bits[i];
//hamming check here
h1 = Hamming17123 (slco_bits + 0);
//run single - manual would suggest that though this exists, there is no support? or perhaps its manufacturer only thing?
if (h1) dmr_slco (opts, state, slco_bits);
else
{
err = 1;
//return (err);
}
return (err);
}
if (lcss == 1) //first block, reset counters and memset
{
//reset the full cach and counter
state->dmr_cach_counter = 0;
memset (state->dmr_cach_fragment, 0, sizeof (state->dmr_cach_fragment));
}
if (lcss == 3) state->dmr_cach_counter++; //continuation, so increment counter by one.
if (lcss == 2) //final segment - assemble, de-interleave, hamming, crc, and execute
{
state->dmr_cach_counter = 3;
}
//sanity check
if (state->dmr_cach_counter > 3) //marginal/shaky/bad signal or tuned away
{
//zero out complete fragment array
lcss = 5; //toss away value
state->dmr_cach_counter = 0;
memset (state->dmr_cach_fragment, 0, sizeof (state->dmr_cach_fragment));
err = 1;
return (err);
}
//add fragment to array
for (i = 0; i < 17; i++)
{
state->dmr_cach_fragment[state->dmr_cach_counter][i] = cach_bits[i+7];
}
if (lcss == 2) //last block arrived, compile, hamming, crc and send off to dmr_slco
{
//assemble
for (j = 0; j < 4; j++)
{
for (i = 0; i < 17; i++)
{
slco_raw_bits[i+(17*j)] = state->dmr_cach_fragment[j][i];
}
}
//De-interleave method, hamming, and crc from Boatbod OP25
//De-interleave
int src = 0;
for (i = 0; i < 67; i++)
{
src = (i * 4) % 67;
slco_bits[i] = slco_raw_bits[src];
}
slco_bits[i] = slco_raw_bits[i];
//hamming checks here
h1 = Hamming17123 (slco_bits + 0);
h2 = Hamming17123 (slco_bits + 17);
h3 = Hamming17123 (slco_bits + 34);
// remove hamming and leave 36 bits of Short LC
for (i = 17; i < 29; i++) {
slco_bits[i-5] = slco_bits[i];
}
for (i = 34; i < 46; i++) {
slco_bits[i-10] = slco_bits[i];
}
//zero out the hangover bits
for (i = 36; i < 68; i++)
{
slco_bits[i] = 0;
}
//run crc8
crc = crc8_ok(slco_bits, 36);
//only run SLCO on good everything
if (h1 && h2 && h3 && crc) dmr_slco (opts, state, slco_bits);
else
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KRED);
fprintf (stderr, " SLCO CRC ERR");
fprintf (stderr, "%s", KNRM);
}
}
return (err); //return err value based on success or failure, even if we aren't checking it
}
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
{
int i;
uint8_t slco_bytes[6]; //completed byte blocks for payload print
for (i = 0; i < 5; i++) slco_bytes[i] = (uint8_t)ConvertBitIntoBytes(&slco_bits[i*8], 8);
slco_bytes[5] = (uint8_t)ConvertBitIntoBytes(&slco_bits[32], 4);
//just going to decode the Short LC with all potential parameters known
uint8_t slco = (uint8_t)ConvertBitIntoBytes(&slco_bits[0], 4);
uint8_t model = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 2);
uint16_t netsite = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 12);
uint8_t reg = slco_bits[18]; //registration required/not required or normalchanneltype/composite cch
uint8_t csc = (uint16_t)ConvertBitIntoBytes(&slco_bits[19], 9); //common slot counter, 0-511
uint16_t net = 0;
uint16_t site = 0;
char model_str[8];
sprintf (model_str, "%s", "");
//activity update stuff
uint8_t ts1_act = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 4); //activity update ts1
uint8_t ts2_act = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 4); //activity update ts2
uint8_t ts1_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8); //ts1 address hash (crc8) //361-1 B.3.7
uint8_t ts2_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //ts2 address hash (crc8) //361-1 B.3.7
//Sys_Parms
if (slco == 0x2 || slco == 0x3)
{
if (model == 0) //Tiny
{
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 9);
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[15], 3);
sprintf (model_str, "%s", "Tiny");
}
else if (model == 1) //Small
{
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 7);
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[13], 5);
sprintf (model_str, "%s", "Small");
}
else if (model == 2) //Large
{
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 4);
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[10], 8);
sprintf (model_str, "%s", "Large");
}
else if (model == 3) //Huge
{
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 2);
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[8], 10);
sprintf (model_str, "%s", "Huge");
}
}
//Con+
uint8_t con_netid = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 8);
uint8_t con_siteid = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 8);
//Cap+
uint8_t restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8);
//initial line break
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
if (slco == 0x2) //C_SYS_Parms
fprintf (stderr, " C_SYS_PARMS - %s - Net ID: %d Site ID: %d - Reg Req: %d - CSC: %d ", model_str, net, site, reg, csc);
else if (slco == 0x3) //P_SYS_Parms
fprintf (stderr, " P_SYS_PARMS - %s - Net ID: %d Site ID: %d - Comp Ch: %d ", model_str, net, site, reg);
else if (slco == 0x0) //null
fprintf (stderr, " SLCO NULL ");
else if (slco == 0x1)
fprintf (stderr, " SLCO Activity Update TS1: %X Hash: %02X TS2: %X Hash: %02X", ts1_act, ts1_hash, ts2_act, ts2_hash); //102 361-2 7.1.3.2
else if (slco == 0x9)
{
state->dmr_mfid = 0x10;
sprintf (state->dmr_branding_sub, "%s", "Con+ ");
fprintf (stderr, " SLCO Connect Plus Voice Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
}
else if (slco == 0xA)
{
state->dmr_mfid = 0x10;
sprintf (state->dmr_branding_sub, "%s", "Con+ ");
fprintf (stderr, " SLCO Connect Plus Control Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
}
else if (slco == 0xF)
{
state->dmr_mfid = 0x10;
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
fprintf (stderr, " SLCO Capacity Plus Rest Channel %d", restchannel);
}
else fprintf (stderr, " SLCO Unknown - %d ", slco);
if (opts->payload == 1 && slco != 0) //if not SLCO NULL
{
fprintf (stderr, "\n SLCO Completed Block ");
for (i = 0; i < 5; i++)
{
fprintf (stderr, "[%02X]", slco_bytes[i]);
}
}
fprintf (stderr, "%s", KNRM);
}
//externalize embedded alias to keep the flco function relatively clean
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
uint8_t slot = state->currentslot;
uint8_t pf;
uint8_t res;
uint8_t format = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 2);
uint8_t len;
//this len seems to pertain to number of blocks? not bit len.
//len = (uint8_t)ConvertBitIntoBytes(&lc_bits[18], 5);
if (format == 0) len = 7;
else if (format == 1 || format == 2) len = 8;
else len = 16;
state->dmr_alias_format[slot] = format;
state->dmr_alias_len[slot] = len;
//fprintf (stderr, "F: %d L: %d - ", format, len);
}
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Embedded Alias: ");
uint8_t slot = state->currentslot;
uint8_t block = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //FLCO equals block number
uint8_t format = state->dmr_alias_format[slot]; //0=7-bit; 1=ISO8; 2=UTF-8; 3=UTF16BE
uint8_t len = state->dmr_alias_len[slot];
uint8_t start; //starting position depends on context of block and format
//there is some issue with the next three lines of code that prevent proper assignments, not sure what.
//if (block > 4) start = 16;
//else if (block == 4 && format > 0) start = 23; //8-bit and 16-bit chars
//else start = 24;
//forcing start to 16 make it work on 8-bit alias, len seems okay when set off of format
start = 16;
len = 8;
// fprintf (stderr, "block: %d start: %d len: %d ", block, start, len);
//all may not be used depending on format, len, start.
uint16_t A0, A1, A2, A3, A4, A5, A6;
//sanity check
if (block > 7) block = 4; //prevent oob array (although we should never get here)
if (len > 6) //if not greater than zero, then the header hasn't arrived yet
{
A0 = 0; A1 = 0; A2 = 0; A3 = 0; A4 = 0; A5 = 0; A6 = 0; //NULL ASCII Characters
A0 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*0], len);
A1 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*1], len);
A2 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*2], len);
A3 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*3], len);
A4 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*4], len);
A5 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*5], len);
A6 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*6], len);
//just going to assign the usual ascii set here to prevent 'naughty' characters, sans diacriticals
if (A0 > 0x19 && A0 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][0], "%c", A0);
if (A1 > 0x19 && A1 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][1], "%c", A1);
if (A2 > 0x19 && A2 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][2], "%c", A2);
if (A3 > 0x19 && A3 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][3], "%c", A3);
if (A4 > 0x19 && A4 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][4], "%c", A4);
if (A5 > 0x19 && A5 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][5], "%c", A5);
if (A6 > 0x19 && A6 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][6], "%c", A6);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 7; j++)
{
fprintf (stderr, "%s", state->dmr_alias_block_segment[slot][i][j]);
}
}
}
else fprintf (stderr, "Missing Header Block Format and Len Data");
fprintf (stderr, "%s", KNRM);
}
//externalize embedded GPS
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Embedded GPS");
uint8_t pf = lc_bits[0];
uint8_t res_a = lc_bits[1];
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4);
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 3);
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[23], 25);
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
//run calculations and print
fprintf (stderr, "%s", KNRM);
}

139
src/dmr_le.c Normal file
View File

@@ -0,0 +1,139 @@
/*-------------------------------------------------------------------------------
* dmr_le.c
* DMR Late Entry MI Fragment Assembly, Procesing, and related Alg functions
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//gather ambe_fr mi fragments for processing
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24])
{
uint8_t slot = state->currentslot;
//collect our fragments and place them into storage
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
state->late_entry_mi_fragment[slot][vc][2] = (uint64_t)ConvertBitIntoBytes(&ambe_fr3[3][0], 4);
if (vc == 6) dmr_late_entry_mi (opts, state);
}
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
{
uint8_t slot = state->currentslot;
int i, j;
int g[3];
unsigned char mi_go_bits[24];
uint64_t mi_test = 0;
uint64_t go_test = 0;
uint64_t mi_corrected = 0;
uint64_t go_corrected = 0;
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
(state->late_entry_mi_fragment[slot][1][2] << 8) | (state->late_entry_mi_fragment[slot][2][2] << 4) | (state->late_entry_mi_fragment[slot][3][2] << 0);
go_test = (state->late_entry_mi_fragment[slot][4][0] << 32L) | (state->late_entry_mi_fragment[slot][5][0] << 28) | (state->late_entry_mi_fragment[slot][6][0] << 24) |
(state->late_entry_mi_fragment[slot][4][1] << 20) | (state->late_entry_mi_fragment[slot][5][1] << 16) | (state->late_entry_mi_fragment[slot][6][1] << 12) |
(state->late_entry_mi_fragment[slot][4][2] << 8) | (state->late_entry_mi_fragment[slot][5][2] << 4) | (state->late_entry_mi_fragment[slot][6][2] << 0);
for (j = 0; j < 3; j++)
{
for (i = 0; i < 12; i++)
{
mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35;
mi_go_bits[i+12] = (( go_test << (i+j*12) ) & 0x800000000) >> 35;
}
//execute golay decode and assign pass or fail to g
if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1;
else g[j] = 0;
for (i = 0; i < 12; i++)
{
mi_corrected = mi_corrected << 1;
mi_corrected |= mi_go_bits[i];
go_corrected = go_corrected << 1;
go_corrected |= mi_go_bits[i+12];
}
}
int mi_final = 0;
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
if (g[0] && g[1] && g[2])
{
if (slot == 0 && state->payload_algid != 0)
{
if (state->payload_mi != mi_final)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
state->payload_mi = mi_final;
fprintf (stderr, "%s", KNRM);
}
}
if (slot == 1 && state->payload_algidR != 0)
{
if (state->payload_miR != mi_final)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
state->payload_miR = mi_final;
fprintf (stderr, "%s", KNRM);
}
}
}
}
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
{
if (state->currentslot == 0)
{
state->dropL = 256;
if (state->K1 != 0)
{
state->DMRvcL = 0;
}
if (state->payload_algid >= 0x21)
{
LFSR (state);
fprintf (stderr, "\n");
}
}
if (state->currentslot == 1)
{
state->dropR = 256;
if (state->K1 != 0)
{
state->DMRvcR = 0;
}
if (state->payload_algidR >= 0x21)
{
LFSR (state);
fprintf (stderr, "\n");
}
}
}
void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
{
state->dropL = 256;
state->dropR = 256;
state->DMRvcL = 0;
state->DMRvcR = 0;
state->payload_miP = 0;
}

View File

@@ -1,40 +1,58 @@
/*-------------------------------------------------------------------------------
* dmr_ms.c
* DMR MS/Simplex/Direct Mode Voice Handling and Data Gathering Routines
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "dmr_const.h"
//A subroutine for processing MS voice
void dmrMS (dsd_opts * opts, dsd_state * state)
{
//fprintf (stderr, "\n Quick DMR Voice Testing\n");
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
char ambe_fr4[4][24] = {0};
char t_ambe_fr[4][24] = {9};
char t_ambe_fr2[4][24] = {9};
char t_ambe_fr3[4][24] = {9};
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
char ambe_fr4[4][24];
//memcpy of ambe_fr for late entry
char m1[4][24];
char m2[4][24];
char m3[4][24];
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
//standalone inbound rc sync exists on dibits 48-96?
//or 44-92? of each potential payload, middle 10 ms or 30 ms frame
char rcsync[25];
char rcsyncdata[96]; //96 bit RC sync data
char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned char EmbeddedSignallingInverse[16];
int EmbeddedSignallingOk;
unsigned int internalcolorcode;
int internalslot;
int activeslot;
char redundancyA[36];
char redundancyB[36];
unsigned short int vc1;
unsigned short int vc2;
char EmbeddedSignalling[16];
//cach
char cachdata[25];
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
//cach tact bits
uint8_t tact_bits[7];
uint8_t emb_ok = 0;
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t EmbeddedSignallingOk = 0;
uint8_t internalslot;
uint8_t vc;
//dummy bits to pass to dburst for link control
uint8_t dummy_bits[196];
memset (dummy_bits, 0, sizeof (dummy_bits));
//add time to mirror printFrameSync
time_t now;
@@ -53,39 +71,43 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
return curr;
}
//Init the color code status
state->color_code_ok = 0;
vc = 2;
vc1 = 2;
vc2 = 2;
short int loop = 1;
short int skipcount = 0;
//Hardset variables for MS
//Hardset variables for MS/Mono
state->currentslot = 0; //0
internalslot = 0;
activeslot = 0;
//Run Loop while the getting is good
while (loop == 1) {
//Note: Manual dibit inversion required here since I didn't seperate inverted return from normal return in framesync,
//so getDibit doesn't know to invert it before it gets here
short int m = 0;
state->dmrburstL = 16; //Use 16 for Voice
for (j = 0; j < 6; j++) {
state->dmrburstL = 16;
for(i = 0; i < 12; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
cachdata[i] = dibit;
state->dmr_stereo_payload[i] = dibit;
}
for (i = 0; i < 7; i++)
{
tact_bits[i] = cachdata[i];
}
tact_okay = 0;
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1)
{
//do nothing since we aren't loop locked forever.
}
//internalslot = tact_bits[1];
internalslot = 0;
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
@@ -97,12 +119,10 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i+12] = dibit;
redundancyA[i] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
@@ -113,20 +133,6 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
}
//check for repetitive data if caught in a 'no carrier' loop? Just picking random values.
//this will test for no carrier (input signal) and return us to no sync state if necessary
if (redundancyA[16] == redundancyB[16] && redundancyA[27] == redundancyB[27] &&
redundancyA[01] == redundancyB[01] && redundancyA[32] == redundancyB[32] &&
redundancyA[03] == redundancyB[03] && redundancyA[33] == redundancyB[33] &&
redundancyA[13] == redundancyB[13] && redundancyA[07] == redundancyB[07] )
{
//goto END;
}
for(i = 0; i < 36; i++)
{
redundancyB[i] = redundancyA[i];
}
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
@@ -138,10 +144,8 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i+48] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
@@ -157,85 +161,37 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i+66] = dibit;
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
if (i < 4 || i > 19) //
// load the superframe to do embedded signal processing
if(vc > 1 && vc < 6) //grab on vc1 values 2-5 B C D and E
{
EmbeddedSignalling[2*i] = (1 & (dibit >> 1)); // bit 1
EmbeddedSignalling[2*i+1] = (1 & dibit); // bit 0
}
// load the superframe to do the voiceburstsync processing
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
{
/* Time slot 1 superframe buffer filling => SYNC data */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2+1] = (1 & dibit); // bit 0
}
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
{
/* Time slot 2 superframe buffer filling => SYNC data */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2+1] = (1 & dibit); // bit 0
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 1
}
}
sync[24] = 0;
EmbeddedSignallingOk = -1;
// for (i = 0; i < 16; i++)
// {
// EmbeddedSignallingInverse[i] = EmbeddedSignalling[i] ^ 1;
// }
if(QR_16_7_6_decode(EmbeddedSignalling))
{
EmbeddedSignallingOk = 1;
}
//test for inverted signal - this should move to data, and have a slottype check here perhaps?
// else if (QR_16_7_6_decode(EmbeddedSignallingInverse))
// {
// if (opts->inverted_dmr == 0)
// {
// opts->inverted_dmr = 1;
// }
// else opts->inverted_dmr = 0;
// goto END;
// }
internalcolorcode = 69; //set so we know if this value is being set properly
if(EmbeddedSignallingOk == 1)
{
state->color_code = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
internalcolorcode = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
state->color_code_ok = EmbeddedSignallingOk;
//Power Indicator, not the other PI (header)
state->PI = (unsigned int)EmbeddedSignalling[4];
state->PI_ok = EmbeddedSignallingOk;
//Link Control Start Stop Indicator
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->LCSS_ok = EmbeddedSignallingOk;
}
//else skipcount++;
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i+90] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
@@ -245,12 +201,6 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
y++;
z++;
//WIP for RC inbound sync data, part 2, second 24 bits (12 dibits)
if (i <= 12)
{
rcsyncdata[i+12] = (dibit | 1) + 48; //test and double check positions for this sync data
}
}
//Setup for Third AMBE Frame
@@ -264,10 +214,8 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i+108] = dibit;
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
@@ -277,229 +225,64 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
y++;
z++;
}
if ( ((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) && opts->inverted_dmr == 0) ||
((strcmp (sync, DMR_MS_DATA_SYNC) == 0) && opts->inverted_dmr == 1) )
state->dmr_ms_mode = 1;
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when
//we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3));
if (state->directmode == 0)
{
if (internalslot == 0)
{
vc1 = 1;
state->DMRvcL = 0; //reset here if jitter/sync forces reset
fprintf (stderr, "MS Slot 1 Voice Sync \n");
}
if (internalslot == 1)
{
vc2 = 1;
state->DMRvcR = 0; //reset here if jitter/sync forces reset
}
state->dmr_ms_mode = 1; //set to 1 here??
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
state->dmr_ms_mode = 1; //set to 1 here?
//don't know if we will catch an RC sync in here or not, should be on VC6 if it occurs
if ( (strcmp (rcsync, DMR_RC_DATA_SYNC) == 0) || (strcmp (sync, DMR_RC_DATA_SYNC) == 0) ) //should be in same position as sync
else
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
//fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
//test with vc1 reset disabled, if all is well, leave disabled
//vc1 = 1;
}
if (internalslot == 1)
{
sprintf(state->slot2light, "[slot2]");
sprintf(state->slot1light, " slot1 ");
//fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
//test with vc1 reset disabled, if all is well, leave disabled
//vc2 = 1;
}
fprintf (stderr, "\n "); //21 spaces to line up the sync
//processDMRdata (opts, state);
state->dmr_ms_rc = 1; //don't know what to do with this, if anything...
goto SKIP;
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
//check for sync pattern here after collected the rest of the payload, decide what to do with it
//if ( (strcmp (sync, DMR_MS_DATA_SYNC) == 0) )
//fixed to compensate for inverted signal
if ( ((strcmp (sync, DMR_MS_DATA_SYNC) == 0) && opts->inverted_dmr == 0) ||
((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) && opts->inverted_dmr == 1) )
if (vc == 6)
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
//fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | MS Data ", state->color_code);
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
//test with vc1 reset disabled, if all is well, leave disabled
//vc1 = 1;
}
if (internalslot == 1)
{
sprintf(state->slot2light, "[slot2]");
sprintf(state->slot1light, " slot1 ");
//fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | Data ", state->color_code);
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE ");
}
else fprintf (stderr,"Sync: -DMR MS/DM MODE ");
//test with vc1 reset disabled, if all is well, leave disabled
//vc2 = 1;
}
//fprintf (stderr, "\n "); //21 spaces to line up the sync
//fprintf (stderr, " "); //21 spaces to line up the sync
state->dmr_ms_mode = 1; //set to 1 here??
processDMRdata (opts, state);
skipcount++;
//goto SKIP;
goto END;
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
fprintf (stderr, "\n");
dmr_alg_refresh (opts, state);
}
if( ( ( ((strcmp (sync, DMR_MS_DATA_SYNC) != 0) && opts->inverted_dmr == 0) ||
((strcmp (sync, DMR_MS_VOICE_SYNC) != 0) && opts->inverted_dmr == 1) ) )
&& internalslot == activeslot && vc1 < 7)
//collect the mi fragment
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
{
//errors in ms/mono since we skip the other slot
// cach_err = dmr_cach (opts, state, cachdata);
skipcount = 0; //reset skip count if processing voice frames
fprintf (stderr,"%s ", getTime());
if (internalslot == 0 && opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | VC%d ", state->color_code, vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
vc++;
//this is necessary because we need to skip and collect dibits, not just skip them
if (vc > 6) goto END;
if (internalslot == 0 && opts->inverted_dmr == 1)
{
fprintf (stderr,"Sync: -DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | VC%d ", state->color_code, vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues)
{
//process voice burst
ProcessVoiceBurstSync(opts, state);
fprintf (stderr, "\n");
}
state->dmr_ms_mode == 1;
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//state->directmode = 0; //flag off for testing
if (state->directmode == 0)
{
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
if (vc1 == 6 && state->payload_algid >= 21)
{
state->DMRvcL = 0;
if (state->payload_mi != 0 && state->payload_algid >= 0x21)
{
LFSR(state);
}
fprintf (stderr, "\n");
}
}
vc1++;
//6 seems optimal for MS, allows to quickly exit and resync if wonky or something
if ( (vc1 > 6) || skipcount > 2) //escape to find new sync if deadspins without a renewed voice frame sync inside of loop
{
goto END;
}
SKIP:
skipDibit (opts, state, 144); //skip to next tdma channel
state->dmr_ms_rc = 0;
//since we are in a repetitive loop, run ncursesPrinter here
//since we are in a loop, run ncursesPrinter here
if (opts->use_ncurses_terminal == 1)
{
ncursesPrinter(opts, state);
}
} // end while loop
//reset these variables when exiting MS/DM mode.
} // end loop
END:
//get first half payload dibits and store them in the payload for the next repitition
skipDibit (opts, state, 144);
skipDibit (opts, state, 144); //should we have two of these?
//CACH + First Half Payload = 12 + 54
for (i = 0; i < 66; i++) //66
{
@@ -516,7 +299,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
state->dmr_ms_mode = 0;
state->dmr_ms_rc = 0;
state->directmode = 0; //flag off
state->DMRvcL = 0;
}
@@ -525,19 +308,39 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
char ambe_fr4[4][24] = {0}; //shim for playing back direct mode samples where ambe1 is bad, duplicate 2 to 4 and play both to smooth audio
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
char ambe_fr4[4][24]; //shim for crappy tdma direct, need more samples to see if this is really a necessary shim or not
//memcpy of ambe_fr for late entry
char m1[4][24];
char m2[4][24];
char m3[4][24];
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
char cachdata[13] = {0};
//cach
char cachdata[25];
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned int EmbeddedSignallingOk;
char EmbeddedSignalling[16];
uint8_t emb_ok = 0;
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t EmbeddedSignallingOk = 0;
//add time to mirror sync
time_t now;
@@ -555,8 +358,8 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
return curr;
}
state->dmrburstL = 16; //Use 16 for Voice?
state->dmrburstL = 16;
state->currentslot = 0; //force to slot 0
dibit_p = state->dmr_payload_p - 90;
@@ -594,7 +397,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
{
dibit = (dibit ^ 2);
}
//state->dmr_stereo_payload[i+12] = dibit;
state->dmr_stereo_payload[i+12] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
@@ -630,12 +433,11 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
z++;
}
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
@@ -675,86 +477,28 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
z++;
}
//work around to set erroneous PI header values to 0 if K is active
if (state->K > 0 || state->K1 > 0)
{
state->payload_keyid = 0; //just for testing
state->payload_keyidR = 0; //just for testing
}
fprintf (stderr,"%s ", getTime());
fprintf (stderr, "%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if ( (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
}
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR MS/DM MODE | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if ( (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
}
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
}
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
state->dmr_ms_mode = 1;
state->DMRvcL = 0;
fprintf (stderr, "| VC* ");
fprintf (stderr, "\n");
//alg reset
//dmr_alg_reset (opts, state);
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//state->directmode = 0; //flag off for testing samples
//copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when
//we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3));
if (state->directmode == 0)
{
@@ -768,25 +512,24 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
//collect the mi fragment
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
//errors due to skipping other slot
// cach_err = dmr_cach (opts, state, cachdata);
skipDibit (opts, state, 144); //skip to next TDMA slot
dmrMS (opts, state); //bootstrap into full TDMA frame
}
//simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing
void dmrMSData (dsd_opts * opts, dsd_state * state)
{
int i, b, c;
int dibit;
int *dibit_p;
unsigned int burst;
unsigned char MSCach[24] = {0}; //12 dibits = 24 bits
unsigned char MSSync[48] = {0}; //24 dibits = 48 bits
unsigned char MSSlot[20] = {0}; //10 dibits = 20 bits
unsigned char MSBurst[5] = {0};
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
//add time to mirror sync
time_t now;
@@ -804,8 +547,7 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
return curr;
}
b = 0;
c = 0;
//CACH + First Half Payload + Sync = 12 + 54 + 24
dibit_p = state->dmr_payload_p - 90;
@@ -813,94 +555,25 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i] = dibit;
//load cach
if (i < 12)
{
MSCach[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
MSCach[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//load slot, first half
if (i > 60 && i < 66)
{
MSSlot[(b*2)] = (1 & (dibit >> 1)); // bit 1
MSSlot[(b*2)+1] = (1 & dibit); // bit
b++;
}
//load sync
if (i > 66)
{
MSSync[(c*2)] = (1 & (dibit >> 1)); // bit 1
MSSync[(c*2)+1] = (1 & dibit); // bit 0
c++;
}
}
for (i = 0; i < 54; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
state->dmr_stereo_payload[i+90] = dibit;
//load slot, second half
if (i < 6)
{
MSSlot[(b*2)] = (1 & (dibit >> 1)); // bit 1
MSSlot[(b*2)+1] = (1 & dibit); // bit
b++;
}
}
//state->directmode = 0; //flag off for testing
//with FEC fix, now we can (hopefully) feel confident in running MS data
if (1 == 1) //opts->payload == 1 && state->directmode == 0
fprintf (stderr, "%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr, "%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE ");
}
else fprintf (stderr,"Sync: -DMR MS/DM MODE ");
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
}
// decode and correct cach and slot
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
int cach_okay = -1;
if ( Hamming_7_4_decode (MSCach) )
{
cach_okay = 1;
}
if (cach_okay == 1)
{
//fprintf (stderr, "CACH Okay ");
}
else
{
//fprintf (stderr, "CACH ERR ");
if (opts->aggressive_framesync == 1) //may not worry about it on data
{
goto END;
}
}
int slot_okay = -1;
if(Golay_20_8_decode(MSSlot))
{
slot_okay = 1;
}
if (slot_okay == 1)
{
//fprintf (stderr, "Slot Okay ");
}
else goto END;
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
sprintf(state->slot1light, "%s", "");
@@ -910,33 +583,23 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
state->dmr_stereo = 1;
state->dmr_ms_mode = 1;
//with FEC fix, now we can (hopefully) feel confident in running MS data
if (1 == 1) //opts->payload == 1 && state->directmode == 0
{
processDMRdata (opts, state);
}
END:
if (slot_okay != 1 || cach_okay != 1)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
dmr_data_sync (opts, state);
state->dmr_stereo = 0;
state->dmr_ms_mode = 0;
state->directmode = 0; //flag off
//get potential first half payload dibits and store them in the payload for the next repitition, MS voice or data.
skipDibit (opts, state, 144);
//should just be loaded in the dmr_payload_buffer instead now
for (i = 0; i < 144; i++) //66
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i] = dibit;
}
//CACH + First Half Payload = 12 + 54
for (i = 0; i < 66; i++) //66
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i] = dibit;
state->dmr_stereo_payload[i] = dibit;
}
}

369
src/dmr_pdu.c Normal file
View File

@@ -0,0 +1,369 @@
/*-------------------------------------------------------------------------------
* dmr_bs.c
* DMR Data (1/2, 3/4, 1) PDU Decoding
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
//unified DMR multiblock PDU message handler (LRRP, etc)
#include "dsd.h"
char * getTimeL(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
char * getDateL(void) {
char datename[120];
char * curr2;
struct tm * to;
time_t t;
t = time(NULL);
to = localtime( & t);
strftime(datename, sizeof(datename), "%Y/%m/%d", to);
curr2 = strtok(datename, " ");
return curr2;
}
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t DMR_PDU[])
{
int message_legnth = 0;
uint8_t slot = state->currentslot;
uint8_t blocks = state->data_header_blocks[slot] - 1; //subtract 1 for the relevant value in the calc below
uint8_t padding = state->data_header_padding[slot];
uint8_t lrrp_conf = 0;
lrrp_conf = dmr_lrrp_check (opts, state, DMR_PDU);
//maybe one day we will have more things to do here
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
}
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
uint8_t dmr_lrrp_check (dsd_opts * opts, dsd_state * state, uint8_t DMR_PDU[])
{
int i, j;
uint8_t message_len = 0;
uint8_t slot = state->currentslot;
uint8_t blocks = state->data_header_blocks[slot] - 1;
uint8_t padding = state->data_header_padding[slot];
uint8_t lrrp_confidence = 0; //variable to increment based on number of tokens found, the more, the higher the confidence level
//source/dest and ports
uint32_t source = 0;
uint32_t dest = 0;
uint16_t port_s = 0;
uint16_t port_d = 0;
//time
uint16_t year = 0;
uint16_t month = 0;
uint16_t day = 0;
uint16_t hour = 0;
uint16_t minute = 0;
uint16_t second = 0;
//lat-long-radius-alt
uint32_t lat = 0;
uint8_t lat_sign = 0;
uint32_t lon = 0;
uint16_t rad = 0;
uint8_t alt = 0;
double lat_unit = (double)180/(double)4294967295;
double lon_unit = (double)360/(double)4294967295;
//speed/velocity
uint16_t vel = 0;
double velocity = 0;
uint8_t vel_set = 0;
//track, direction, degrees
uint8_t degrees = 0;
uint8_t deg_set = 0;
//triggered information report
uint8_t report = 0;
uint8_t pot_report = 0; //potential report by findind 0x0D and backtracking a few bytes
i = 0; //watch DMR_PDU[10] closely for a 'report' token
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
for (i; i < ( ((blocks+1)*16) - (padding+4) ); i++) //need to change the 16, or entire thing? or who cares lol just keep looking
{
switch(DMR_PDU[i]){
case 0x0C: //LRRP_TriggeredLocationReport
if (source == 0)
{
source = (DMR_PDU[i+1] << 16 ) + (DMR_PDU[i+2] << 8) + DMR_PDU[i+3];
dest = (DMR_PDU[i+5] << 16 ) + (DMR_PDU[i+6] << 8) + DMR_PDU[i+7];
port_s = (DMR_PDU[i+8] << 8) + DMR_PDU[i+9];
port_d = (DMR_PDU[i+10] << 8) + DMR_PDU[i+11];
i += 11;
lrrp_confidence++;
}
break;
//may need more 'report' types to trigger various scenarios
//disable all but 0x1F if issues or falsing happens often
//case 0x1D: //ARRP_TriggeredInformationAnswer_NCDT
//case 0x1E: //ARRP_TriggeredInformationReport = FALSE?
//case 0x25: //ARRP_UnsolicitedInformationReport_NCDT = (0x25, True, "")
case 0x13: //LRRP_UnsolicitedLocationReport_NCDT
case 0x15: //LRRP_LocationProtocolReport_NCDT
case 0x21: //ARRP_TriggeredInformationStopRequest_NCDT
case 0x1F: //ARRP_TriggeredInformationReport_NCDT
if (report == 0)
{
report = DMR_PDU[i];
i += 1;
lrrp_confidence++;
}
break;
//0x0D always seems to follow 0x1F or other 'report' types two/three bytes later
case 0x0D: //message len indicator //LRRP_TriggeredLocationReport_NCDT
if (report > 0 && message_len == 0)
{
message_len = DMR_PDU[i+1];
i += 1;
lrrp_confidence++;
}
else if (message_len == 0)
{
if (i > 3)
{
if (DMR_PDU[i-3] > 0) pot_report = DMR_PDU[i-3];
if (pot_report < 0x27)
{
report = pot_report;
message_len = DMR_PDU[i+1];
i += 1;
//no lrrp_confidence on speculative reporting, but will print it as such.
}
}
}
break;
//RESULT TOKEN or Request ID!
case 0x23: //this value has been seen after the 0x0D message len, appears to be a 2-byte value, was tripping a false on time stamp
i += 2;
break;
//answer and report tokens
case 0x51: //circle-2d
case 0x54: //circle-3d
case 0x55: //circle-3d <- false positives on one sample, so either an earlier token occurred (that we don't have) or just random data?
if (message_len > 0 && lat == 0)
{
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
i += 10; //double check
lrrp_confidence++;
}
break;
case 0x34: //Time Interval Periodic Trigger (timestamp)
case 0x35: //Time Interval Periodic Trigger (timestamp)
if (message_len > 0 && year == 0)
{
year = (DMR_PDU[i+1] << 6) + (DMR_PDU[i+2] >> 2);
month = ((DMR_PDU[i+2] & 0x3) << 2) + ((DMR_PDU[i+3] & 0xC0) >> 6);
day = ((DMR_PDU[i+3] & 0x30) >> 1) + ((DMR_PDU[i+3] & 0x0E) >> 1);
hour = ((DMR_PDU[i+3] & 0x01) << 4) + ((DMR_PDU[i+4] & 0xF0) >> 4);
minute = ((DMR_PDU[i+4] & 0x0F) << 2) + ((DMR_PDU[i+5] & 0xC0) >> 6);
second = (DMR_PDU[i+5] & 0x3F);
i += 5;
//sanity check
if (year > 2000 && year < 2025) lrrp_confidence++;
if (year > 2025 || year < 2000) year = 0; //needs future proofing
}
break;
case 0x66: //point-2d
if (message_len > 0 && lat == 0)
{
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
i += 8; //check
lrrp_confidence++;
}
break;
case 0x69: //point-3d
case 0x6A: //point-3d
if (message_len > 0 && lat == 0)
{
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
alt = DMR_PDU[i+9];
i += 9; //check
lrrp_confidence++;
}
break;
case 0x36: //protocol-version
i += 1;
//lrrp_confidence++;
break;
//speed/velocity
case 0x70: //speed-virt
case 0x6C: //speed-hor
if (message_len > 0 && vel_set == 0)
{
vel = (DMR_PDU[i+1] << 8) + DMR_PDU[i+2]; //don't think this is meant to be shifted, but doing it anyways for comparison
velocity = ( ((double)( (DMR_PDU[i+1] ) + DMR_PDU[i+2] )) / ( (double)128));
vel_set = 1;
i += 2;
lrrp_confidence++;
}
break;
case 0x56: //direction-hor
if (message_len > 0 && deg_set == 0)
{
degrees = DMR_PDU[i+1] * 2;
deg_set = 1;
i += 1;
lrrp_confidence++;
}
break;
//below all unknown
case 0x37: //result
case 0x38: //result
case 0x39: //result 'operations error'? attributes 0x22?
case 0x6B: //unknown-uint8
case 0x65: //lev-conf
default:
//do nothing
break;
}
}
if (report && message_len > 0)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d", lrrp_confidence, message_len);
if (lrrp_confidence >= 3) //find the sweet magical number
{
//now we can open our lrrp file and write to it as well
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
//write current date/time if not present in LRRP data
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
//write data header source (or destination?) if not available in lrrp data
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
}
if (pot_report)
{
fprintf (stderr, "\n");
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
}
if (report)
{
fprintf (stderr, "\n");
fprintf (stderr, " Report: 0x%02X", report);
if (report == 0x1F) fprintf (stderr, " ARRP_TriggeredInformationReport_NCDT "); //customize later when more is learned
if (report == 0x21) fprintf (stderr, " ARRP_TriggeredInformationStopRequest_NCDT ");
if (report == 0x22) fprintf (stderr, " ARRP_TriggeredInformationStopAnswer ");
if (report == 0x25) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
if (report == 0x26) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
if (report == 0x27) fprintf (stderr, " ARRP_InformationProtocolRequest_NCDT ");
if (report == 0x13) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
}
if (source)
{
fprintf (stderr, "\n");
fprintf (stderr, " Source: %08d - %04d", source, port_s);
fprintf (stderr, "\n");
fprintf (stderr, " Destination: %08d - %04d", dest, port_d);
}
if (year)
{
fprintf (stderr, "\n");
fprintf (stderr, " LRRP - Time: ");
fprintf (stderr, " %04d.%02d.%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
}
if (lat)
{
fprintf (stderr, "\n");
if (lat_sign) //print a '-' in front of the value
fprintf (stderr, " LRRP - Lat: -%.5lf", (double)lat * lat_unit);
else fprintf (stderr, " LRRP - Lat: %.5lf", (double)lat * lat_unit);
fprintf (stderr, " Lon: %.5lf", (double)lon * lon_unit);
if (lat_sign)
fprintf (stderr, " (-%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
else fprintf (stderr, " (%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
if (opts->lrrp_file_output == 1)
{
if (lat_sign) fprintf (pFile, "-");
fprintf (pFile, "%.5lf\t", (double)lat * lat_unit);
fprintf (pFile, "%.5lf\t", (double)lon * lon_unit);
}
}
if (rad)
{
fprintf (stderr, "\n");
fprintf (stderr, " LRRP - Radius: %d", rad); //unsure of 'units' or calculation for radius (meters?)
}
if (alt)
{
fprintf (stderr, "\n");
fprintf (stderr, " LRRP - Altitude: %d", alt); //unsure of 'units' or calculation for alt (meters?)
}
if (vel_set)
{
fprintf (stderr, "\n");
fprintf (stderr, " LRRP - Velocity: %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity));
if (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
}
if (deg_set)
{
fprintf (stderr, "\n");
fprintf (stderr, " LRRP - Track: %d Degrees", degrees);
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
}
//close open file
if (opts->lrrp_file_output == 1)
{
fprintf (pFile, "\n");
fclose (pFile);
}
}
}
if (pot_report)
{
fprintf (stderr, "\n");
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
}
fprintf (stderr, "%s", KNRM);
return (lrrp_confidence);
}

136
src/dmr_pi.c Normal file
View File

@@ -0,0 +1,136 @@
/*-------------------------------------------------------------------------------
* dmr_pi.c
* DMR Privacy Indicator and LFSR Function
*
* LFSR code courtesy of https://github.com/mattames/LFSR/
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{
if((IrrecoverableErrors == 0))
{
if (state->currentslot == 0)
{
if (0 == 0)
{
state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2];
}
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
if (state->payload_algid < 0x26)
{
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 1");
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi);
fprintf (stderr, "%s ", KNRM);
}
if (state->payload_algid >= 0x26)
{
if (0 == 0)
{
state->payload_algid = 0;
state->payload_keyid = 0;
state->payload_mi = 0;
}
}
}
if (state->currentslot == 1)
{
if (0 == 0)
{
state->payload_algidR = PI_BYTE[0];
state->payload_keyidR = PI_BYTE[2];
}
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
if (state->payload_algidR < 0x26)
{
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 2");
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
fprintf (stderr, "%s ", KNRM);
}
if (state->payload_algidR >= 0x26)
{
if (0== 0)
{
state->payload_algidR = 0;
state->payload_keyidR = 0;
state->payload_miR = 0;
}
}
}
}
if((IrrecoverableErrors == 0) && CRCCorrect)
{
//fprintf (stderr, " (PI CRC Okay)");
}
else if((IrrecoverableErrors == 0))
{
//fprintf (stderr, " (PI FEC Okay)");
}
else {
fprintf (stderr, "%s", KRED);
fprintf (stderr, (" (PI FEC ERR)"));
fprintf (stderr, "%s ", KNRM);
}
}
void LFSR(dsd_state * state)
{
int lfsr = 0;
if (state->currentslot == 0)
{
lfsr = state->payload_mi;
}
else lfsr = state->payload_miR;
uint8_t cnt = 0;
for(cnt=0;cnt<32;cnt++)
{
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1
int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1;
lfsr = (lfsr << 1) | (bit);
}
if (state->currentslot == 0)
{
if (1 == 1)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
fprintf(stderr, " Next MI: 0x%08X", lfsr);
fprintf (stderr, "%s", KNRM);
}
state->payload_mi = lfsr;
}
if (state->currentslot == 1)
{
if (1 == 1)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
fprintf(stderr, " Next MI: 0x%08X", lfsr);
fprintf (stderr, "%s", KNRM);
}
state->payload_miR = lfsr;
}
}

View File

@@ -1,395 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "dmr_const.h"
#include "p25p1_check_hdu.h"
void ProcessDMR (dsd_opts * opts, dsd_state * state)
{
uint32_t i, j;
uint32_t Frame;
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameL = NULL;
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameR = NULL;
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameRC = NULL; //Reverse Test for MS Backwards
int *errs;
int *errs2;
unsigned long long int k;
int x;
k = 0;
TSVoiceSupFrameL = &state->TS1SuperFrame;
TSVoiceSupFrameR = &state->TS2SuperFrame;
int Pr[256] = {
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
};
if ( (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->currentslot == 0 && state->K > 0 && state->M == 1) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
k = Pr[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 48; j++)
{
x = ( ((k << j) & 0x800000000000) >> 47 );
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
}
}
}
}
if ( (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
(state->currentslot == 1 && state->K > 0 && state->M == 1) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
k = Pr[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 48; j++)
{
x = ( ((k << j) & 0x800000000000) >> 47 );
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
}
}
}
}
if ( (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->currentslot == 0 && state->K1 > 0 && state->M == 1) )
{
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %010llX", k1);
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
fprintf(stderr, " %016llX", k2);
}
if (k4 != 0)
{
len = 255;
fprintf(stderr, " %016llX", k3);
fprintf(stderr, " %016llX", k4);
}
fprintf (stderr, "%s", KNRM);
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
pos = 0;
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 49; j++)
{
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
pos++;
}
}
}
}
if ( (state->currentslot == 1 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
(state->currentslot == 1 && state->K1 > 0 && state->M == 1) )
{
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %010llX", k1);
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
fprintf(stderr, " %016llX", k2);
}
if (k4 != 0)
{
len = 255;
fprintf(stderr, " %016llX", k3);
fprintf(stderr, " %016llX", k4);
}
fprintf (stderr, "%s", KNRM);
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
pos = 0;
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 49; j++)
{
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
pos++;
}
}
}
}
if (state->currentslot == 0)
{
for(Frame = 0; Frame < 6; Frame++) //6
{
/* 1 DMR frame contains 3 AMBE voice samples */
for(i = 0; i < 3; i++)
{
errs = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
errs2 = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
state->errs = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
state->errs2 = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData(opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
//quick enc toggle to determine whether or not to play enc traffic
int enc_bit = 0;
enc_bit = (state->dmr_so >> 6) & 0x1;
if (enc_bit == 1)
{
state->dmr_encL = 1;
}
else state->dmr_encL = 0;
if (state->K != 0 || state->K1 != 0)
{
state->dmr_encL = 0;
}
//quick test to determine whether or not to run the next few steps below
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
{
if (opts->mbe_out_f != NULL)
{
saveAmbe2450Data (opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
state->debug_audio_errors += *errs2;
processAudio(opts, state);
if (opts->wav_out_f != NULL)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1) //opts->audio_out == 1
{
playSynthesizedVoice (opts, state);
}
} //second bracket for the test enc condition
} /* End for(i = 0; i < 3; i++) */
} /* End for(Frame = 0; Frame < 6; Frame++) */
} //End if Current Slot == 0
if (state->currentslot == 1)
{
for(Frame = 0; Frame < 6; Frame++)
{
/* 1 DMR frame contains 3 AMBE voice samples */
for(i = 0; i < 3; i++)
{
errs = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
errs2 = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
state->errs = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
state->errs2 = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData(opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
//quick enc toggle to determine whether or not to play enc traffic
int enc_bit = 0;
enc_bit = (state->dmr_soR >> 6) & 0x1;
if (enc_bit == 1)
{
state->dmr_encR = 1;
}
else state->dmr_encR = 0;
if (state->K != 0 || state->K1 != 0)
{
state->dmr_encR = 0;
}
//quick test to determine whether or not to run the next few steps below
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
{
if (opts->mbe_out_f != NULL)
{
saveAmbe2450Data (opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
state->debug_audio_errors += *errs2;
if (opts->audio_out == 1)
{
processAudio(opts, state);
}
if (opts->wav_out_f != NULL)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
} /* End for(i = 0; i < 3; i++) */
} /* End for(Frame = 0; Frame < 6; Frame++) */
} //End if Current Slot == 1
} /* End ProcessDMR */
/* End of file */

File diff suppressed because it is too large Load Diff

302
src/dmr_utils.c Normal file
View File

@@ -0,0 +1,302 @@
//DMR CRC/Utility Functions
//Original File - dmr_sync.c
//ConvertBitIntoBytes, ComputeCrcCCITT, ComputeCrc5Bit, ComputeAndCorrectFullLinkControlCrc, CRC32, CRC9
//Original Source - https://github.com/LouisErigHerve/dsd
//Additional Functions
//Hamming17123, crc8, crc8ok functions
//Original Souce - https://github.com/boatbod/op25
#include "dsd.h"
// A Hamming (17,12,3) Check for completed SLC message
bool Hamming17123(uint8_t* d)
{
// Calculate the checksum this column should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
// Compare these with the actual bits
unsigned char n = 0x00U;
n |= (c0 != d[12]) ? 0x01U : 0x00U;
n |= (c1 != d[13]) ? 0x02U : 0x00U;
n |= (c2 != d[14]) ? 0x04U : 0x00U;
n |= (c3 != d[15]) ? 0x08U : 0x00U;
n |= (c4 != d[16]) ? 0x10U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[12] = !d[12]; return true;
case 0x02U: d[13] = !d[13]; return true;
case 0x04U: d[14] = !d[14]; return true;
case 0x08U: d[15] = !d[15]; return true;
case 0x10U: d[16] = !d[16]; return true;
// Data bit errors
case 0x1BU: d[0] = !d[0]; return true;
case 0x1FU: d[1] = !d[1]; return true;
case 0x17U: d[2] = !d[2]; return true;
case 0x07U: d[3] = !d[3]; return true;
case 0x0EU: d[4] = !d[4]; return true;
case 0x1CU: d[5] = !d[5]; return true;
case 0x11U: d[6] = !d[6]; return true;
case 0x0BU: d[7] = !d[7]; return true;
case 0x16U: d[8] = !d[8]; return true;
case 0x05U: d[9] = !d[9]; return true;
case 0x0AU: d[10] = !d[10]; return true;
case 0x14U: d[11] = !d[11]; return true;
// No bit errors
case 0x00U: return true;
// Unrecoverable errors
default: return false;
}
}
uint8_t crc8(uint8_t bits[], unsigned int len)
{
uint8_t crc=0;
unsigned int K = 8;
uint8_t poly[9] = {1,0,0,0,0,0,1,1,1}; // crc8 poly
uint8_t buf[256];
if (len+K > sizeof(buf)) {
//fprintf (stderr, "crc8: buffer length %u exceeds maximum %lu\n", len+K, sizeof(buf));
return 0;
}
memset (buf, 0, sizeof(buf));
for (unsigned int i=0; i<len; i++){
buf[i] = bits[i];
}
for (unsigned int i=0; i<len; i++)
if (buf[i])
for (unsigned int j=0; j<K+1; j++)
buf[i+j] ^= poly[j];
for (unsigned int i=0; i<K; i++){
crc = (crc << 1) + buf[len + i];
}
return crc;
}
bool crc8_ok(uint8_t bits[], unsigned int len)
{
uint16_t crc = 0;
for (unsigned int i=0; i < 8; i++) {
crc = (crc << 1) + bits[len+i];
}
return (crc == crc8(bits,len));
}
/*
* @brief : This function compute the CRC-CCITT of the DMR data
* by using the polynomial x^16 + x^12 + x^5 + 1
*
* @param Input : A buffer pointer of the DMR data (80 bytes)
*
* @return The 16 bit CRC
*/
uint16_t ComputeCrcCCITT(uint8_t * DMRData)
{
uint32_t i;
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
/* Polynomial x^16 + x^12 + x^5 + 1
* Normal = 0x1021
* Reciprocal = 0x0811
* Reversed = 0x8408
* Reversed reciprocal = 0x8810 */
uint16_t Polynome = 0x1021;
for(i = 0; i < 80; i++)
{
if(((CRC >> 15) & 1) ^ (DMRData[i] & 1))
{
CRC = (CRC << 1) ^ Polynome;
}
else
{
CRC <<= 1;
}
}
/* Invert the CRC */
CRC ^= 0xFFFF;
/* Return the CRC */
return CRC;
} /* End ComputeCrcCCITT() */
/*
* @brief : This function compute the CRC-24 bit of the full
* link control by using the Reed-Solomon(12,9) FEC
*
* @param FullLinkControlDataBytes : A buffer pointer of the DMR data bytes (12 bytes)
*
* @param CRCComputed : A 32 bit pointer where the computed CRC 24-bit will be stored
*
* @param CRCMask : The 24 bit CRC mask to apply
*
* @return 0 = CRC error
* 1 = CRC is correct
*/
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask)
{
uint32_t i;
rs_12_9_codeword_t VoiceLCHeaderStr;
rs_12_9_poly_t syndrome;
uint8_t errors_found = 0;
rs_12_9_correct_errors_result_t result = RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND;
uint32_t CrcIsCorrect = 0;
for(i = 0; i < 12; i++)
{
VoiceLCHeaderStr.data[i] = FullLinkControlDataBytes[i];
/* Apply CRC mask on each 3 last bytes
* of the full link control */
if(i == 9)
{
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 16);
}
else if(i == 10)
{
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 8);
}
else if(i == 11)
{
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask);
}
else
{
/* Nothing to do */
}
}
/* Check and correct the full link LC control with Reed Solomon (12,9) FEC */
rs_12_9_calc_syndrome(&VoiceLCHeaderStr, &syndrome);
if(rs_12_9_check_syndrome(&syndrome) != 0) result = rs_12_9_correct_errors(&VoiceLCHeaderStr, &syndrome, &errors_found);
/* Reconstitue the CRC */
*CRCComputed = (uint32_t)((VoiceLCHeaderStr.data[9] << 16) & 0xFF0000);
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[10] << 8) & 0x00FF00);
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[11] << 0) & 0x0000FF);
if((result == RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND) ||
(result == RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED))
{
//fprintf(stderr, "CRC OK : 0x%06X\n", *CRCComputed);
CrcIsCorrect = 1;
/* Reconstitue full link control data after FEC correction */
for(i = 0; i < 12; i++)
{
FullLinkControlDataBytes[i] = VoiceLCHeaderStr.data[i];
/* Apply CRC mask on each 3 last bytes
* of the full link control */
if(i == 9)
{
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 16);
}
else if(i == 10)
{
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 8);
}
else if(i == 11)
{
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask);
}
else
{
/* Nothing to do */
}
}
}
else
{
//fprintf(stderr, "CRC ERROR : 0x%06X\n", *CRCComputed);
CrcIsCorrect = 0;
}
/* Return the CRC status */
return CrcIsCorrect;
} /* End ComputeAndCorrectFullLinkControlCrc() */
/*
* @brief : This function compute the 5 bit CRC of the DMR voice burst data
* See ETSI TS 102 361-1 chapter B.3.11
*
* @param Input : A buffer pointer of the DMR data (72 bytes)
*
* @return The 5 bit CRC
*/
uint8_t ComputeCrc5Bit(uint8_t * DMRData)
{
uint32_t i, j, k;
uint8_t Buffer[9];
uint32_t Sum;
uint8_t CRC = 0;
/* Convert the 72 bit into 9 bytes */
k = 0;
for(i = 0; i < 9; i++)
{
Buffer[i] = 0;
for(j = 0; j < 8; j++)
{
Buffer[i] = Buffer[i] << 1;
Buffer[i] = Buffer[i] | DMRData[k++];
}
}
/* Add all 9 bytes */
Sum = 0;
for(i = 0; i < 9; i++)
{
Sum += (uint32_t)Buffer[i];
}
/* Sum MOD 31 = CRC */
CRC = (uint8_t)(Sum % 31);
/* Return the CRC */
return CRC;
} /* End ComputeCrc5Bit() */
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
{
uint32_t Output = 0;
uint32_t i;
for(i = 0; i < BitLength; i++)
{
Output <<= 1;
Output |= (uint32_t)(BufferIn[i] & 1);
}
return Output;
} /* End ConvertBitIntoBytes() */
/*
* @brief : This function compute the CRC-9 of the DMR data
* by using the polynomial x^9 + x^6 + x^4 + x^3 + 1
*
* @param Input : A buffer pointer of the DMR data (80 bytes)
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
*
* @param NbData : The number of bit to compute
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
*
* @return The 9 bit CRC
*/

View File

@@ -1,660 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "dmr_const.h"
void processDMRvoice (dsd_opts * opts, dsd_state * state)
{
// extracts AMBE frames from DMR frame
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned int EmbeddedSignallingOk;
#ifdef DMR_DUMP
int k;
char syncbits[49];
char cachbits[25];12
#endif
/* Init the superframe buffers */
memset(&state->TS1SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
memset(&state->TS2SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
/* Init the color code status */
state->color_code_ok = 0;
mutecurrentslot = 0;
msMode = 0;
dibit_p = state->dibit_buf_p - 144;
for(j = 0; j < 6; j++)
{
// 2nd half of previous slot
for(i = 0; i < 54; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
}
// CACH
for(i = 0; i < 12; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
cachdata[i] = dibit;
if(i == 2)
{
/* Change the current slot only if we are in relayed mode (not in direct mode) */
if(state->directmode == 0) state->currentslot = (1 & (dibit >> 1)); // bit 1
if(state->currentslot == 0)
{
state->slot1light[0] = '[';
state->slot1light[6] = ']';
state->slot2light[0] = ' ';
state->slot2light[6] = ' ';
}
else
{
state->slot2light[0] = '[';
state->slot2light[6] = ']';
state->slot1light[0] = ' ';
state->slot1light[6] = ' ';
}
}
}
cachdata[12] = 0;
//set state-dmrburstL and R here to a value for ncurses
if (state->currentslot == 0)
{
state->dmrburstL = 16; //use 16 for Voice?
}
if (state->currentslot == 1)
{
state->dmrburstR = 16; //Use 16 for Voice?
}
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
fprintf(stderr, "%s ", cachbits);
#endif
// current slot frame 1
w = rW;
x = rX;
y = rY;
z = rZ;
k = 0;
for(i = 0; i < 36; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => RAW voice bit */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
// current slot frame 2 first half
w = rW;
x = rX;
y = rY;
z = rZ;
k = 0;
for(i = 0; i < 18; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => RAW voice bit */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
l = 0;
// signaling data or sync
for(i = 0; i < 24; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => SYNC data */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => SYNC data */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
}
}
sync[24] = 0;
/* Copy the Embedded signaling (1st part) */
for(i = 0; i < 8; i++)
{
EmbeddedSignalling[i] = syncdata[i];
}
/* Copy the Embedded signaling (2nd part) */
for(i = 0; i < 8; i++)
{
EmbeddedSignalling[i + 8] = syncdata[i + 40];
}
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
if(QR_16_7_6_decode(EmbeddedSignalling)) EmbeddedSignallingOk = 1;
else EmbeddedSignallingOk = 0;
/* Get the color code */
cc[0] = EmbeddedSignalling[0];
cc[1] = EmbeddedSignalling[1];
cc[2] = EmbeddedSignalling[2];
cc[3] = EmbeddedSignalling[3];
/* The color code is normally the same for all
* 6 received voice frame, so in case of error
* do not store it except if the last SYNC frame
* has been received. */
if(EmbeddedSignallingOk || ((state->color_code_ok == 0) && (j == 5)))
{
/* Save the color code */
state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]);
state->color_code_ok = EmbeddedSignallingOk;
}
/* Save the Power Control (PI) indicator */
state->PI = (unsigned int)EmbeddedSignalling[4];
state->PI_ok = EmbeddedSignallingOk;
/* Save the Link Control Start Stop (LCSS) indicator */
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->LCSS_ok = EmbeddedSignallingOk;
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
{
mutecurrentslot = 1;
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot1light, "[slot1]");
}
else
{
sprintf(state->slot2light, "[slot2]");
}
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
{
mutecurrentslot = 1;
state->currentslot = 0;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, "[sLoT1]");
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
{
mutecurrentslot = 1;
state->currentslot = 1;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, "[sLoT2]");
}
else if((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) ) //|| (strcmp (sync, DMR_MS_VOICE_SYNC) == 0))
{
mutecurrentslot = 0;
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot1light, "[SLOT1]");
}
else
{
sprintf(state->slot2light, "[SLOT2]");
}
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0)
{
mutecurrentslot = 0;
state->currentslot = 0;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, "[sLoT1]");
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0)
{
mutecurrentslot = 0;
state->currentslot = 1;
state->directmode = 1; /* Direct mode */
sprintf(state->slot2light, "[sLoT2]");
}
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
fprintf(stderr, "%s ", syncbits);
#endif
// current slot frame 2 second half
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
if(mutecurrentslot == 0)
{
if(state->firstframe == 1)
{ // we don't know if anything received before the first sync after no carrier is valid
state->firstframe = 0;
}
else
{
//processMbeFrame (opts, state, NULL, ambe_fr, NULL);
//processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
}
}
// current slot frame 3
w = rW;
x = rX;
y = rY;
z = rZ;
k = 0;
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => RAW voice bit */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
if(mutecurrentslot == 0)
{
//processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
// CACH
for(i = 0; i < 12; i++)
{
dibit = getDibit(opts, state);
cachdata[i] = dibit;
}
cachdata[12] = 0;
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
fprintf(stderr, "%s ", cachbits);
#endif
// next slot
skipDibit (opts, state, 54);
// signaling data or sync
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
syncdata[i] = dibit;
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
syncdata[24] = 0;
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1))
{
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot2light, " slot2 ");
}
else
{
sprintf(state->slot1light, " slot1 ");
}
}
else if(strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
{
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot2light, " SLOT2 ");
}
else
{
sprintf(state->slot1light, " SLOT1 ");
}
}
else if((strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0))
{
state->currentslot = 0;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, " sLoT1 ");
}
else if((strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0))
{
state->currentslot = 1;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, " sLoT2 ");
}
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
fprintf(stderr, "%s ", syncbits);
#endif
if(j == 5) //j == 5
{
// 2nd half next slot
skipDibit (opts, state, 54);
// CACH
skipDibit (opts, state, 12);
// first half current slot
skipDibit (opts, state, 54);
}
/* Extract the 49 bits AMBE of the voice frame */
if(state->currentslot == 0)
{
/* 3 x 49 bit of AMBE sample per voice frame => Slot 1buffer here */
for(i = 0; i < 3; i++)
{
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
mbe_demodulateAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
&(state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
}
}
else
{
/* 3 x 49 bit of AMBE sample per voice frame => Slot 2 buffer here */
for(i = 0; i < 3; i++)
{
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
mbe_demodulateAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
&(state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
}
}
} /* End for(j = 0; j < 6; j++) */
if(opts->errorbars == 1)
{
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
/* Print the color code */
fprintf (stderr, "%s", KCYN);
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
fprintf (stderr, "%s", KNRM);
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
if(state->color_code_ok)
{
fprintf(stderr, "| (CRC OK ) |"); //add line break
state->dmr_color_code = state->color_code; //set here when voice? otherwise is set off of idle.
}
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "| (CRC ERR) |"); //add line break
fprintf (stderr, "%s", KNRM);
}
fprintf(stderr, " VOICE ");
}
/* Perform the SYNC DMR data embedded decoding */
ProcessVoiceBurstSync(opts, state);
/* Perform the DMR voice decoding */
ProcessDMR(opts, state);
//LFSR if required, change condition?
if ( (state->currentslot == 0 && state->payload_algid == 0x21 ) || //&& opts->payload == 1
(state->currentslot == 1 && state->payload_algidR == 0x21 ) ) //&& opts->payload == 1
{
LFSR(state);
}
if(opts->errorbars == 1)
{
fprintf(stderr, "\n");
}
} /* End processDMRvoice() */

View File

@@ -166,7 +166,7 @@ PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and
}
void
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stereo, may use this for NXDN too
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
{
int i, j, k;
unsigned char b;
@@ -174,7 +174,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
err = (unsigned char) state->errs2;
k = 0;
if (opts->dmr_stereo == 0)
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
{
fprintf (stderr, "\n");
}
@@ -183,7 +183,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
fprintf(stderr, " AMBE ");
}
for (i = 0; i < 7; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
for (i = 0; i < 7; i++)
{
b = 0;
for (j = 0; j < 8; j++)
@@ -192,13 +192,13 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
b = b + ambe_d[k];
k++;
}
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
if (opts->payload == 1 && i < 6)
{
fprintf (stderr, "%02X", b);
}
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit, value will be either 0x00 or 0x80?
if (opts->payload == 1 && i == 6)
{
fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit
fprintf (stderr, "%02X", b & 0x80);
}
}
if (state->currentslot == 0)
@@ -207,7 +207,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
}
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
b = ambe_d[48];
if (opts->dmr_stereo == 1) //need to fix the printouts again
if (opts->dmr_stereo == 1 || opts->dmr_mono == 1) //need to fix the printouts again
{
fprintf (stderr, "\n");
}
@@ -226,7 +226,7 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
k = 0;
if (opts->payload == 1) //make opt variable later on to toggle this
if (opts->payload == 1)
{
fprintf(stderr, "\n");
}
@@ -248,12 +248,12 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
b = b & 255;
k++;
}
//new printout on reading MBE files back in
if (opts->payload == 1) //make opt variable later on to toggle this
if (opts->payload == 1)
{
fprintf (stderr, "%02X", x);
}
//
}
if (opts->payload == 1)
{
@@ -273,11 +273,11 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
state->errs = state->errs2;
k = 0;
if (opts->payload == 1) //make opt variable later on to toggle this
if (opts->payload == 1)
{
fprintf(stderr, "\n");
}
//for (i = 0; i < 6; i++)
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
{
b = fgetc (opts->mbe_in_f);
@@ -296,13 +296,13 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
b = b & 255;
k++;
}
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
if (opts->payload == 1 && i < 6)
{
fprintf (stderr, "%02X", x);
}
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
if (opts->payload == 1 && i == 6)
{
fprintf (stderr, "%02X", x & 0x80); //7th octet should only contain 1 bit?
fprintf (stderr, "%02X", x & 0x80);
}
}
if (opts->payload == 1)
@@ -349,82 +349,6 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
}
//temporarily disabled until the compiler warnings can be fixed, use symbol capture or per call instead!
//probably the easiest thing is to just have this run fclose, and have name assignment on open instead.
// void
// closeMbeOutFile (dsd_opts * opts, dsd_state * state)
// {
//
// char shell[255], newfilename[64], ext[5], datestr[32], new_path[1024];
// char tgid[17];
// int sum, i, j;
// int talkgroup;
// struct tm timep;
// int result;
//
// if (opts->mbe_out_f != NULL)
// {
// if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
// {
// sprintf (ext, ".imb");
// //strptime (opts->mbe_out_file, "%s.imb", &timep);
// }
// else
// {
// sprintf (ext, ".amb");
// //strptime (opts->mbe_out_file, "%s.amb", &timep);
// }
//
// if (state->tgcount > 0)
// {
// for (i = 0; i < 16; i++)
// {
// sum = 0;
// for (j = 0; j < state->tgcount; j++)
// {
// sum = sum + state->tg[j][i] - 48;
// }
// tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
// }
// tgid[16] = 0;
// talkgroup = (int) strtol (tgid, NULL, 2);
// }
// else
// {
// talkgroup = 0;
// }
//
// fflush (opts->mbe_out_f);
// fclose (opts->mbe_out_f);
// opts->mbe_out_f = NULL;
// strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
//
// sprintf (newfilename, "%s%s", datestr, ext); //this one
//
// if (state->lastsynctype == 0 || state->lastsynctype == 1)
// {
// sprintf (newfilename, "%s-nac%X-tg%i%s", datestr, state->nac, talkgroup, ext);
// }
//
// //sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename);
// #ifdef _WIN32
// //sprintf (shell, "move %s %s", opts->mbe_out_path, new_path);
// #else
// //sprintf (shell, "mv %s %s", opts->mbe_out_path, new_path);
// #endif
// //result = system (shell);
//
// state->tgcount = 0;
// for (i = 0; i < 25; i++)
// {
// for (j = 0; j < 16; j++)
// {
// state->tg[i][j] = 48;
// }
// }
// }
// }
//much simpler version
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
{
@@ -447,7 +371,6 @@ void
openMbeOutFile (dsd_opts * opts, dsd_state * state)
{
//struct timeval tv;
int i, j;
char ext[5];
@@ -471,10 +394,6 @@ openMbeOutFile (dsd_opts * opts, dsd_state * state)
state->tgcount = 0;
//gettimeofday (&tv, NULL);
//rewrite below line to take getTime function instead?
// sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
@@ -500,7 +419,6 @@ openWavOutFile (dsd_opts * opts, dsd_state * state)
info.samplerate = 8000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
if (opts->wav_out_f == NULL)
@@ -508,18 +426,15 @@ openWavOutFile (dsd_opts * opts, dsd_state * state)
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
return;
}
// state->wav_out_bytes = 0;
}
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
{
SF_INFO info;
info.samplerate = 8000; //8000
info.samplerate = 8000;
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
if (opts->wav_out_f == NULL)
@@ -527,9 +442,6 @@ void openWavOutFileL (dsd_opts * opts, dsd_state * state)
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
return;
}
// state->wav_out_bytes = 0;
}
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
@@ -539,7 +451,6 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
info.samplerate = 8000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
if (opts->wav_out_f == NULL)
@@ -547,9 +458,6 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
return;
}
// state->wav_out_bytes = 0;
}
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
@@ -559,17 +467,12 @@ void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
info.samplerate = 48000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
if (opts->wav_out_raw == NULL)
{
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
return;
}
// state->wav_out_bytes = 0;
}
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
@@ -609,15 +512,12 @@ void closeSymbolOutFile (dsd_opts * opts, dsd_state * state)
//Do something
if (opts->symbol_out == 1)
{
if (opts->symbol_out_file != NULL) //check first, or issuing a second fclose will crash the SOFTWARE
if (opts->symbol_out_file != NULL)
{
fclose(opts->symbol_out_f); //free(): double free detected in tcache 2 (this is a new one) happens when closing more than once
//noCarrier(opts, state); //free(): double free detected in tcache 2 (this is a new one)
fclose(opts->symbol_out_f);
}
opts->symbol_out = 0; //set flag to 1
//sprintf (opts->symbol_out_file, "fme-capture.bin"); //use fme for the symbol bin ext
//openSymbolOutFile (opts, state);
}

View File

@@ -163,13 +163,14 @@ processFrame (dsd_opts * opts, dsd_state * state)
{
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
processDMRvoice (opts, state);
dmrMSBootstrap (opts, state);
}
if (opts->dmr_stereo == 0 && state->synctype == 32)
if (opts->dmr_mono == 1 && state->synctype == 32)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
fprintf (stderr, "%s", KNRM);
// fprintf (stderr, "%s", KRED);
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
// fprintf (stderr, "%s", KNRM);
dmrMSBootstrap (opts, state);
}
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
{
@@ -183,19 +184,20 @@ processFrame (dsd_opts * opts, dsd_state * state)
}
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
{
if (opts->dmr_stereo == 0)
{
closeMbeOutFile (opts, state);
state->err_str[0] = 0;
fprintf (stderr, "%s", KRED);
fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
fprintf (stderr, "%s", KNRM);
}
if (opts->dmr_stereo == 1)
{
dmrMSData (opts, state);
dmrMSData (opts, state);
// if (opts->dmr_stereo == 0)
// {
// closeMbeOutFile (opts, state);
// state->err_str[0] = 0;
// fprintf (stderr, "%s", KRED);
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
// fprintf (stderr, "%s", KNRM);
// }
// if (opts->dmr_stereo == 1)
// {
// dmrMSData (opts, state);
}
// }
}
else
{
@@ -206,7 +208,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
state->payload_miP = 0; //zero out when switching back to data sync
processDMRdata (opts, state);
dmr_data_sync (opts, state);
}
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
if (opts->dmr_stereo == 1)
@@ -215,7 +217,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
state->payload_miP = 0; //zero out when switching back to data sync
processDMRdata (opts, state);
dmr_data_sync (opts, state);
}
}
return;

View File

@@ -889,7 +889,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
sprintf(state->ftype, "DMR ");
if (opts->errorbars == 1 && opts->dmr_stereo == 0)
{
printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
//printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
}
if (state->lastsynctype != 11)
{
@@ -1003,7 +1003,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
sprintf(state->ftype, "DMR ");
if (opts->errorbars == 1 && opts->dmr_stereo == 0)
{
printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
//printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
}
if (state->lastsynctype != 12)
{

View File

@@ -201,6 +201,8 @@ noCarrier (dsd_opts * opts, dsd_state * state)
state->HYTR = 0;
state->DMRvcL = 0;
state->DMRvcR = 0;
state->dropL = 256;
state->dropR = 256;
state->payload_miN = 0;
state->p25vc = 0;
@@ -210,19 +212,6 @@ noCarrier (dsd_opts * opts, dsd_state * state)
state->dmr_lrrp_source[0] = 0;
state->dmr_lrrp_source[1] = 0;
//initialize 12 rate superframe
for (short i = 0; i < 288; i++)
{
state->dmr_12_rate_sf[0][i] = 0;
state->dmr_12_rate_sf[1][i] = 0;
}
//initialize 34 rate superframe
for (short i = 0; i < 288; i++)
{
state->dmr_34_rate_sf[0][i] = 0;
state->dmr_34_rate_sf[1][i] = 0;
}
//initialize data header bits
state->data_header_blocks[0] = 1; //initialize with 1, otherwise we may end up segfaulting when no/bad data header
@@ -268,7 +257,29 @@ noCarrier (dsd_opts * opts, dsd_state * state)
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
sprintf (state->nxdn_call_type, "%s", "");
}
state->dmr_rest_channel = -1; //init on -1
sprintf(state->dmr_branding_sub, "%s", "");
sprintf(state->dmr_branding, "%s", "");
state->dmr_mfid = 0;
//dmr slco stuff
memset(state->dmr_cach_fragment, 0, sizeof(state->dmr_cach_fragment));
state->dmr_cach_counter = 0;
//initialize unified dmr pdu 'superframe'
memset (state->dmr_pdu_sf, 0, sizeof (state->dmr_pdu_sf));
//init confirmed data individual block crc as invalid
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
//embedded signalling
memset(state->dmr_embedded_signalling, 0, sizeof(state->dmr_embedded_signalling));
//late entry mi fragments
memset (state->late_entry_mi_fragment, 0, sizeof (state->late_entry_mi_fragment));
} //nocarrier
void
initOpts (dsd_opts * opts)
@@ -376,6 +387,7 @@ initOpts (dsd_opts * opts)
opts->ncurses_history = 1;
opts->payload = 0;
opts->inverted_dpmr = 0;
opts->dmr_mono = 0;
opts->dmr_stereo = 1;
opts->aggressive_framesync = 1; //more aggressive to combat wonk wonk voice decoding
//see if initializing these values causes issues elsewhere, if so, then disable.
@@ -414,7 +426,7 @@ initOpts (dsd_opts * opts)
opts->p25_is_tuned = 0; //set to 1 if currently on VC, set back to 0 on carrier drop
opts->trunk_hangtime = 1; //1 second hangtime by default before tuning back to CC
}
} //initopts
void
initState (dsd_state * state)
@@ -569,7 +581,6 @@ initState (dsd_state * state)
state->fourv_counter[0] = 0;
state->fourv_counter[1] = 0;
sprintf (state->dmr_branding, "%s", "");
sprintf (state->dmr_callsign[0][0], "%s", "");
sprintf (state->dmr_callsign[0][1], "%s", "");
sprintf (state->dmr_callsign[0][2], "%s", "");
@@ -618,6 +629,8 @@ initState (dsd_state * state)
state->HYTR = 0;
state->DMRvcL = 0;
state->DMRvcR = 0;
state->dropL = 256;
state->dropR = 256;
state->p25vc = 0;
@@ -629,19 +642,6 @@ initState (dsd_state * state)
state->dmr_lrrp_source[0] = 0;
state->dmr_lrrp_source[1] = 0;
//initialize 12 rate superframe
for (short i = 0; i < 288; i++)
{
state->dmr_12_rate_sf[0][i] = 0;
state->dmr_12_rate_sf[1][i] = 0;
}
//initialize 34 rate superframe
for (short i = 0; i < 288; i++)
{
state->dmr_34_rate_sf[0][i] = 0;
state->dmr_34_rate_sf[1][i] = 0;
}
//initialize data header bits
state->data_header_blocks[0] = 1; //initialize with 1, otherwise we may end up segfaulting when no/bad data header
@@ -735,6 +735,30 @@ initState (dsd_state * state)
state->dmr_end_alert[0] = 0;
state->dmr_end_alert[1] = 0;
sprintf (state->dmr_branding, "%s", "");
sprintf (state->dmr_branding_sub, "%s", "");
//initialize unified dmr pdu 'superframe'
memset (state->dmr_pdu_sf, 0, sizeof (state->dmr_pdu_sf));
//init confirmed data individual block crc as invalid
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
//dmr slco stuff
memset(state->dmr_cach_fragment, 0, sizeof(state->dmr_cach_fragment));
state->dmr_cach_counter = 0;
//embedded signalling
memset(state->dmr_embedded_signalling, 0, sizeof(state->dmr_embedded_signalling));
//dmr talker alias new/fixed stuff
memset(state->dmr_alias_format, 0, sizeof(state->dmr_alias_format));
memset(state->dmr_alias_len, 0, sizeof(state->dmr_alias_len));
memset(state->dmr_alias_block_segment, 0, sizeof(state->dmr_alias_block_segment));
//late entry mi fragments
memset (state->late_entry_mi_fragment, 0, sizeof (state->late_entry_mi_fragment));
initialize_p25_heuristics(&state->p25_heuristics);
}
@@ -805,7 +829,7 @@ usage ()
printf (" -fs DMR Stereo BS and MS Simplex only\n");
printf (" -f1 Decode only P25 Phase 1\n");
printf (" -fd Decode only D-STAR\n");
printf (" -fr Decode only DMR (LEH)\n");
printf (" -fr Decode only DMR Mono - Single Slot Voice\n");
printf (" -fx Decode only X2-TDMA\n");
printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n");
printf (" -fn Decode only NXDN96* (12.5 kHz)\n");
@@ -860,7 +884,7 @@ usage ()
printf (" (See channel_map.csv for example)\n");
printf (" -2 <file> Import Group List Allow/Block and Label from csv file (numeral 'two')\n");
printf (" (See group.csv for example)\n");
printf (" -3 Enable Extremely Experimental Trunking Features (NXDN/P25/EDACS for now) with RIGCTL/TCP or RTL Input\n");
printf (" -3 Enable Extremely Experimental Trunking Features (NXDN/P25/EDACS/DMR TIII, Con+) with RIGCTL/TCP or RTL Input\n");
printf (" -5 <udp p> Enable RIGCTL/TCP; Set UDP Port for RIGCTL. (4532 on SDR++)\n");
printf (" -6 <secs> Set Trunking VC/sync loss hangtime in seconds. (default = 1 second)\n");
//printf (" (Currently only available on UDP port 4532)\n");
@@ -1000,29 +1024,6 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state)
fprintf (stderr,"Total audio errors: %i\n", state->debug_audio_errors);
fprintf (stderr,"Total header errors: %i\n", state->debug_header_errors);
fprintf (stderr,"Total irrecoverable header errors: %i\n", state->debug_header_critical_errors);
//debug_print_heuristics(&(state->p25_heuristics));
fprintf (stderr,"\n");
fprintf (stderr,"+P25 BER estimate: %.2f%%\n", get_P25_BER_estimate(&state->p25_heuristics));
fprintf (stderr,"-P25 BER estimate: %.2f%%\n", get_P25_BER_estimate(&state->inv_p25_heuristics));
fprintf (stderr,"\n");
#ifdef TRACE_DSD
if (state->debug_label_file != NULL) {
fclose(state->debug_label_file);
state->debug_label_file = NULL;
}
if(state->debug_label_dibit_file != NULL) {
fclose(state->debug_label_dibit_file);
state->debug_label_dibit_file = NULL;
}
if(state->debug_label_imbe_file != NULL) {
fclose(state->debug_label_imbe_file);
state->debug_label_imbe_file = NULL;
}
#endif
fprintf (stderr,"Exiting.\n");
exit (0);
}
@@ -1103,10 +1104,9 @@ main (int argc, char **argv)
usage ();
exit (0);
case 'a':
//printPortAudioDevices();
//exit(0);
opts.call_alert = 1;
break;
//placeholder until letters get re-arranged (or opt_long switched in)
case '1': //LCN to Frequency CSV Import
strncpy(opts.lcn_in_file, optarg, 1023);
@@ -1169,7 +1169,6 @@ main (int argc, char **argv)
}
break;
case 'P':
//can't handle negative numbers?
sscanf (optarg, "%d", &opts.rtlsdr_ppm_error);
break;
case 'q':
@@ -1177,18 +1176,10 @@ main (int argc, char **argv)
opts.verbose = 0;
break;
case 's':
// opts.errorbars = 0;
// opts.p25enc = 0;
// opts.p25lc = 0;
// opts.p25status = 0;
// opts.p25tg = 0;
// opts.datascope = 1;
// opts.symboltiming = 0;
sscanf (optarg, "%d", &opts.wav_sample_rate);
opts.wav_interpolator = opts.wav_sample_rate / opts.wav_decimator;
state.samplesPerSymbol = state.samplesPerSymbol * opts.wav_interpolator;
state.symbolCenter = state.symbolCenter * opts.wav_interpolator;
//fprintf (stderr, "Are we getting this far?\n");
break;
case 't':
opts.symboltiming = 1;
@@ -1249,21 +1240,6 @@ main (int argc, char **argv)
opts.dmr_mute_encR = 1;
}
state.H = state.K1; //shim still required?
//old handling for 10 Char Key
// sscanf (optarg, "%llX", &state.H);
// if (state.H > 0xFFFFFFFFFF)
// {
// state.H = 0xFFFFFFFFFF;
// }
// opts.dmr_mute_encL = 0;
// opts.dmr_mute_encR = 0;
// if (state.H == 0)
// {
// opts.dmr_mute_encL = 1;
// opts.dmr_mute_encR = 1;
// }
// state.K1 = state.H; //shim
break;
case '4':
@@ -1423,11 +1399,6 @@ main (int argc, char **argv)
opts.serial_dev[1023] = '\0';
break;
// case 'R':
// sscanf (optarg, "%d", &opts.resume);
// fprintf (stderr,"Enabling scan resume after %i TDULC frames\n", opts.resume);
// break;
case 'f':
if (optarg[0] == 'a')
{
@@ -1444,6 +1415,7 @@ main (int argc, char **argv)
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
opts.dmr_mono = 1;
state.dmr_stereo = 0;
sprintf (opts.output_name, "Legacy Auto");
}
@@ -1463,6 +1435,7 @@ main (int argc, char **argv)
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
state.dmr_stereo = 0;
opts.dmr_mono = 0;
state.rf_mod = 0;
sprintf (opts.output_name, "D-STAR");
fprintf (stderr,"Decoding only D-STAR frames.\n");
@@ -1482,6 +1455,7 @@ main (int argc, char **argv)
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
opts.dmr_mono = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "X2-TDMA");
fprintf (stderr,"Decoding only X2-TDMA frames.\n");
@@ -1507,6 +1481,7 @@ main (int argc, char **argv)
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
opts.dmr_mono = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "EDACS/PV");
fprintf (stderr,"Setting symbol rate to 9600 / second\n");
@@ -1530,6 +1505,8 @@ main (int argc, char **argv)
opts.mod_qpsk = 0;
opts.mod_gfsk = 0;
state.rf_mod = 0; //
opts.dmr_stereo = 0;
opts.dmr_mono = 0;
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
sprintf (opts.output_name, "P25P1");
@@ -1557,6 +1534,7 @@ main (int argc, char **argv)
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
state.dmr_stereo = 0;
opts.dmr_mono = 0;
sprintf (opts.output_name, "NXDN48");
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
@@ -1584,6 +1562,7 @@ main (int argc, char **argv)
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
state.dmr_stereo = 0;
opts.dmr_mono = 0;
sprintf (opts.output_name, "YSF");
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
fprintf (stderr,"Decoding only YSF frames.\nNot working yet!\n");
@@ -1608,6 +1587,7 @@ main (int argc, char **argv)
state.rf_mod = 0;
opts.dmr_stereo = 1;
state.dmr_stereo = 0;
opts.dmr_mono = 0;
sprintf (opts.output_name, "P25P2");
fprintf (stderr,"Decoding P25-P2 frames C4FM or OP25 Symbol Captures!\n");
}
@@ -1629,7 +1609,7 @@ main (int argc, char **argv)
opts.mod_gfsk = 0;
state.rf_mod = 0;
opts.dmr_stereo = 1;
//state.dmr_stereo = 1; //manually set to 1
opts.dmr_mono = 0;
opts.pulse_digi_rate_out = 24000;
opts.pulse_digi_out_channels = 2;
sprintf (opts.output_name, "DMR Stereo");
@@ -1655,7 +1635,7 @@ main (int argc, char **argv)
//or consider using an external modulator (GNURadio?)
state.rf_mod = 0;
opts.dmr_stereo = 1;
//state.dmr_stereo = 1;
opts.dmr_mono = 0;
opts.pulse_digi_rate_out = 24000;
opts.pulse_digi_out_channels = 2;
sprintf (opts.output_name, "XDMA");
@@ -1680,6 +1660,7 @@ main (int argc, char **argv)
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
opts.dmr_mono = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "NXDN96");
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
@@ -1702,11 +1683,34 @@ main (int argc, char **argv)
state.rf_mod = 0; //
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
opts.dmr_mono = 1;
opts.dmr_stereo = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "DMR LEH");
fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n");
fprintf (stderr,"Decoding only DMR frames.\n");
state.dmr_stereo = 0; //0
sprintf (opts.output_name, "DMR Mono");
//fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n");
fprintf (stderr,"Decoding only DMR Mono. \nUsing DMR Stereo or XDMA is highly encouraged.\n");
//dmr stereo below
// opts.frame_dstar = 0;
// opts.frame_x2tdma = 0;
// opts.frame_p25p1 = 0;
// opts.frame_p25p2 = 0;
// opts.inverted_p2 = 0;
// opts.frame_nxdn48 = 0;
// opts.frame_nxdn96 = 0;
// opts.frame_dmr = 1;
// opts.frame_dpmr = 0;
// opts.frame_provoice = 0;
// opts.frame_ysf = 0;
// opts.mod_c4fm = 1;
// opts.mod_qpsk = 0;
// opts.mod_gfsk = 0;
// state.rf_mod = 0;
// opts.dmr_stereo = 1;
// state.dmr_stereo = 0;
// opts.pulse_digi_rate_out = 24000;
// opts.pulse_digi_out_channels = 2;
// sprintf (opts.output_name, "DMR Stereo");
// fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
}
else if (optarg[0] == 'm')
{
@@ -1729,6 +1733,7 @@ main (int argc, char **argv)
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
opts.dmr_stereo = 0;
opts.dmr_mono = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "dPMR");
fprintf(stderr, "Notice: dPMR cannot autodetect polarity. \n Use -xd option if Inverted Signal expected.\n");
@@ -1739,10 +1744,11 @@ main (int argc, char **argv)
case 'm':
if (optarg[0] == 'a')
{
opts.mod_c4fm = 1;
opts.mod_qpsk = 1;
opts.mod_gfsk = 1;
state.rf_mod = 0;
// opts.mod_c4fm = 1;
// opts.mod_qpsk = 1;
// opts.mod_gfsk = 1;
// state.rf_mod = 0;
fprintf (stderr,"Don't use the -ma switch.\n");
}
else if (optarg[0] == 'c')
{
@@ -1927,8 +1933,6 @@ main (int argc, char **argv)
opts.audio_out_type = 0;
}
if((strncmp(opts.audio_in_dev, "pulse", 5) == 0))
{
opts.audio_in_type = 0;

View File

@@ -245,7 +245,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
else
{
//stereo slots and slot 0 (left slot)
if (state->currentslot == 0 && opts->dmr_stereo == 1)
if (state->currentslot == 0) //&& opts->dmr_stereo == 1
{
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
@@ -340,7 +340,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
}
//stereo slots and slot 1 (right slot)
if (state->currentslot == 1 && opts->dmr_stereo == 1)
if (state->currentslot == 1) //&& opts->dmr_stereo == 1
{
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
@@ -435,19 +435,19 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
//X2-TDMA? Not sure what still makes it this far to run under Framef
if (opts->dmr_stereo == 0)
{
mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData (opts, state, ambe_d);
}
//only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
{
saveAmbe2450Data (opts, state, ambe_d);
}
}
// if (opts->dmr_stereo == 0)
// {
// mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
// if (opts->payload == 1)
// {
// PrintAMBEData (opts, state, ambe_d);
// }
// //only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
// if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
// {
// saveAmbe2450Data (opts, state, ambe_d);
// }
// }
}
@@ -456,7 +456,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
int enc_bit = 0;
//end enc check
if (opts->dmr_stereo == 1 && state->currentslot == 0)
if ( (opts->dmr_mono == 1 ||opts->dmr_stereo == 1) && state->currentslot == 0) //all mono traffic routed through 'left'
{
enc_bit = (state->dmr_so >> 6) & 0x1;
if (enc_bit == 1)
@@ -494,7 +494,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
}
if (opts->dmr_stereo == 1 && state->currentslot == 1)
if (opts->dmr_stereo == 1 && state->currentslot == 1)
{
enc_bit = (state->dmr_soR >> 6) & 0x1;
if (enc_bit == 0x1)
@@ -533,7 +533,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
if (opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
if (opts->dmr_mono == 0 && opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
{
state->debug_audio_errors += state->errs2;
if (opts->audio_out == 1)