From 3cd1eefad803e0e5e31ada880ec3d3e45491fd63 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Sat, 30 Apr 2022 04:43:03 -0400 Subject: [PATCH 01/10] DMR Stereo TDMA DMR Stereo TDMA --play voice in both TDMA slots with the -T option (dsd -fr -T) --Improved MS voice (mostly working) and data handling (hit or miss) --pretty pretty colors on the console log output --rate 3/4 data decoding partially working --- include/dsd.h | 139 +++++---- src/dmr_bs.c | 462 +++++++++++++++++++++++++++ src/dmr_data.c | 157 +++++++++- src/dmr_ms.c | 672 ++++++++++++++++++++++++++++++++++++++++ src/dmr_process_voice.c | 184 ++++++----- src/dmr_sync.c | 588 ++++++++++++++++++++++++----------- src/dmr_voice.c | 49 ++- src/dpmr_voice.c | 24 +- src/dsd_audio.c | 551 ++++++++++++-------------------- src/dsd_dibit.c | 83 ++--- src/dsd_file.c | 1 - src/dsd_frame.c | 77 ++++- src/dsd_frame_sync.c | 363 ++++++++++++++++++---- src/dsd_main.c | 137 +++++--- src/dsd_mbe.c | 76 +++-- src/dsd_ncurses.c | 70 ++++- src/dsd_symbol.c | 3 +- src/dsd_upsample.c | 68 +++- src/dstar.c | 7 +- src/nxdn_data.c | 11 +- src/nxdn_lib.c | 22 +- src/nxdn_voice.c | 11 +- src/p25p1_hdu.c | 8 +- src/p25p1_ldu1.c | 2 +- src/p25p1_ldu2.c | 12 +- src/p25p1_tdu.c | 2 + src/p25p1_tdulc.c | 4 + src/trellis.cpp | 224 ++++++++++---- src/ysf.c | 287 +++++++++++++++++ 29 files changed, 3291 insertions(+), 1003 deletions(-) create mode 100644 src/dmr_bs.c create mode 100644 src/dmr_ms.c create mode 100644 src/ysf.c diff --git a/include/dsd.h b/include/dsd.h index 48319a0..5fd5ad5 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -17,6 +17,17 @@ * PERFORMANCE OF THIS SOFTWARE. */ + //pretty pretty colors + + #define KNRM "\x1B[0m" + #define KRED "\x1B[31m" + #define KGRN "\x1B[32m" + #define KYEL "\x1B[33m" + #define KBLU "\x1B[34m" + #define KMAG "\x1B[35m" + #define KCYN "\x1B[36m" + #define KWHT "\x1B[37m" + #include "config.h" #include #include @@ -67,13 +78,11 @@ //look into making this not required by doing ifdef, make new c file for methods, and CLI case option for ncurses terminal #include //move this stuff to dsd.h #include //move this stuff to dsd.h -/* - * global variables - */ +// +// static volatile int exitflag; -//borrow from LEH typedef struct { uint8_t RFChannelType; @@ -148,8 +157,6 @@ typedef struct } NxdnElementsContent_t; -//LEH DMR - enum { MODE_UNENCRYPTED, @@ -230,8 +237,8 @@ typedef struct FullLinkControlPDU_t FullLC; } TimeSlotVoiceSuperFrame_t; -//end borrow from LEH -//LEH dPMR + +//dPMR /* Could only be 2 or 4 */ #define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2 @@ -293,11 +300,13 @@ typedef struct int audio_out_type; // 0 for device, 1 for file, 2 for portaudio int split; int playoffset; + int playoffsetR; char mbe_out_dir[1024]; char mbe_out_file[1024]; char mbe_out_path[1024]; FILE *mbe_out_f; float audio_gain; + float audio_gainR; int audio_out; char wav_out_file[1024]; SNDFILE *wav_out_f; @@ -337,27 +346,34 @@ typedef struct int pulse_raw_rate_out; int pulse_digi_rate_in; int pulse_digi_rate_out; + //int pulse_digi_rate_outR; int pulse_raw_in_channels; int pulse_raw_out_channels; int pulse_digi_in_channels; int pulse_digi_out_channels; + //int pulse_digi_out_channelsR; int pulse_flush; pa_simple *pulse_raw_dev_in; pa_simple *pulse_raw_dev_out; pa_simple *pulse_digi_dev_in; pa_simple *pulse_digi_dev_out; + pa_simple *pulse_digi_dev_outR; int use_ncurses_terminal; int reset_state; int payload; char output_name[1024]; - //LEH DMR Stuff + int EncryptionMode; - //LEH dPMR stuff unsigned int dPMR_curr_frame_is_encrypted; int dPMR_next_part_of_superframe; int inverted_dpmr; int frame_dpmr; + // + short int dmr_stereo; + // + int frame_ysf; + int inverted_ysf; //not sure if ysf comes in inverted or not, but signal could if IQ flipped } dsd_opts; @@ -365,6 +381,8 @@ typedef struct { int *dibit_buf; int *dibit_buf_p; + int *dmr_payload_buf; //try loading up a small amount to buffer, 144 or 288? + int *dmr_payload_p; //position for payload buffer int repeat; short *audio_out_buf; short *audio_out_buf_p; @@ -418,6 +436,8 @@ typedef struct int nac; int errs; int errs2; + int errsR; + int errs2R; int mbe_file_type; int optind; int numtdulc; @@ -425,17 +445,25 @@ typedef struct char slot0light[8]; //char slot1light[8]; float aout_gain; + float aout_gainR; float aout_max_buf[200]; + float aout_max_bufR[200]; float *aout_max_buf_p; + float *aout_max_buf_pR; int aout_max_buf_idx; + int aout_max_buf_idxR; int samplesPerSymbol; int symbolCenter; char algid[9]; char keyid[17]; int currentslot; + int hardslot; mbe_parms *cur_mp; mbe_parms *prev_mp; mbe_parms *prev_mp_enhanced; + mbe_parms *cur_mp2; + mbe_parms *prev_mp2; + mbe_parms *prev_mp_enhanced2; int p25kid; int payload_algid; int payload_keyid; @@ -444,6 +472,7 @@ typedef struct unsigned long long int K; unsigned int debug_audio_errors; + unsigned int debug_audio_errorsR; unsigned int debug_header_errors; unsigned int debug_header_critical_errors; @@ -478,10 +507,9 @@ typedef struct unsigned int nxdn_cipher_type; unsigned int nxdn_key; char nxdn_call_type[1024]; - char dmr_callsign[4][99]; //plenty of room in case of overflow; + char dmr_callsign[6][99]; //plenty of room in case of overflow; char dmr_lrrp[6][9999]; - //borrow from LEH NxdnSacchRawPart_t NxdnSacchRawPart[4]; NxdnFacch1RawPart_t NxdnFacch1RawPart[2]; NxdnFacch2RawPart_t NxdnFacch2RawPart; @@ -492,8 +520,7 @@ typedef struct char ambe_ciphered[49]; char ambe_deciphered[49]; - //end borrow - //LEH DMR struff + unsigned int color_code; unsigned int color_code_ok; unsigned int PI; @@ -511,10 +538,13 @@ typedef struct TimeSlotVoiceSuperFrame_t TS2SuperFrame; char dmr_branding[25]; - uint8_t dmr_12_rate_sf[36]; //going three frames deep - uint8_t dmr_34_rate_sf[54]; //going three frames deep + uint8_t dmr_12_rate_sf[2][60]; //going five frames deep by 12 bytes //[slot][value] + uint8_t dmr_34_rate_sf[2][64]; //going four frames deep by 16 bytes //[slot][value] + //char dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame + //char dmr_msmode_payload[144]; + int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame + - //LEH dPMR stuff dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame; //These flags are used to known the DMR frame @@ -539,6 +569,10 @@ typedef struct unsigned char * dpmr_target_id; int dpmr_color_code; + short int dmr_stereo; //need state variable for upsample function + short int dmr_ms_rc; + short int dmr_ms_mode; + #ifdef TRACE_DSD char debug_prefix; char debug_prefix_2; @@ -555,6 +589,10 @@ typedef struct /* * Frame sync patterns */ + +#define FUSION_SYNC "31111311313113131131" //HA! +#define INV_FUSION_SYNC "13333133131331313313" //HA! + #define INV_P25P1_SYNC "333331331133111131311111" #define P25P1_SYNC "111113113311333313133333" @@ -568,14 +606,6 @@ typedef struct #define DSTAR_SYNC "313131313133131113313111" #define INV_DSTAR_SYNC "131313131311313331131333" -//bogus nxdnn data syncs to troubleshoot -/* -#define NXDN_MS_DATA_SYNC "33333313133113131111333333333111" -#define INV_NXDN_MS_DATA_SYNC "11133331313113313133331113333331" -#define INV_NXDN_BS_DATA_SYNC "33331111313113313133331313333333" -#define NXDN_BS_DATA_SYNC "11113333131331131311113133333333" -//end bogus nxdn data syncs -*/ #define NXDN_MS_DATA_SYNC "313133113131111333" #define INV_NXDN_MS_DATA_SYNC "131311331313333111" #define INV_NXDN_BS_DATA_SYNC "131311331313333131" @@ -589,64 +619,52 @@ typedef struct #define DMR_BS_VOICE_SYNC "131111333113313313113313" #define DMR_MS_DATA_SYNC "311131133313133331131113" #define DMR_MS_VOICE_SYNC "133313311131311113313331" -//borrow extra syncs from LEH + +//inversion testing on MS +//#define DMR_MS_DATA_SYNC "133313311131311113313331" +//#define DMR_MS_VOICE_SYNC "311131133313133331131113" + +#define DMR_RC_DATA_SYNC "131331111133133133311313" + #define DMR_DIRECT_MODE_TS1_DATA_SYNC "331333313111313133311111" #define DMR_DIRECT_MODE_TS1_VOICE_SYNC "113111131333131311133333" #define DMR_DIRECT_MODE_TS2_DATA_SYNC "311311111333113333133311" #define DMR_DIRECT_MODE_TS2_VOICE_SYNC "133133333111331111311133" -//end LEH + #define INV_PROVOICE_SYNC "31313111333133133311331133113311" #define PROVOICE_SYNC "13131333111311311133113311331133" #define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311" #define PROVOICE_EA_SYNC "31131311331331111133131311311133" -//LEH dPMR -/* dPMR Frame Sync 1 - 48 bits sequence - * HEX : 57 FF 5F 75 D5 77 - * Binary : 0101 0111 1111 1111 0101 1111 0111 0101 1101 0101 0111 0111 - * Dibit : 1 1 1 3 3 3 3 3 1 1 3 3 1 3 1 1 3 1 1 1 1 3 1 3 */ - #define DPMR_FRAME_SYNC_1 "111333331133131131111313" - -/* dPMR Frame Sync 2 - 24 bits sequence - * HEX : 5F F7 7D - * Binary : 0101 1111 1111 0111 0111 1101 - * Dibit : 1 1 3 3 3 3 1 3 1 3 3 1 */ #define DPMR_FRAME_SYNC_2 "113333131331" - -/* dPMR Frame Sync 3 - 24 bits sequence - * HEX : 7D DF F5 - * Binary : 0111 1101 1101 1111 1111 0101 - * Dibit : 1 3 3 1 3 1 3 3 3 3 1 1 */ #define DPMR_FRAME_SYNC_3 "133131333311" - -/* dPMR Frame Sync 4 - 48 bits sequence - * HEX : FD 55 F5 DF 7F DD - * Binary : 1111 1101 0101 0101 1111 0101 1101 1111 0111 1111 1101 1101 - * Dibit : 3 3 3 1 1 1 1 1 3 3 1 1 3 1 3 3 1 3 3 3 3 1 3 1 */ #define DPMR_FRAME_SYNC_4 "333111113311313313333131" -//#define DPMR_FRAME_SYNC_4 "111333331133131131111313" /* dPMR Frame Sync 1 to 4 - Inverted */ #define INV_DPMR_FRAME_SYNC_1 "333111113311313313333131" #define INV_DPMR_FRAME_SYNC_2 "331111313113" #define INV_DPMR_FRAME_SYNC_3 "311313111133" #define INV_DPMR_FRAME_SYNC_4 "111333331133131131111313" -//#define INV_DPMR_FRAME_SYNC_4 "333111113311313313333131" -// /* * 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 playRawAudio (dsd_opts * opts, dsd_state * state); //added this one HERE HERE +void processAudioR (dsd_opts * opts, dsd_state * state); void openPulseInput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet void openPulseOutput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state); void playSynthesizedVoice (dsd_opts * opts, dsd_state * state); +void playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state); void openAudioOutDevice (dsd_opts * opts, int speed); void openAudioInDevice (dsd_opts * opts); @@ -695,10 +713,11 @@ void processProVoice (dsd_opts * opts, dsd_state * state); void processX2TDMAdata (dsd_opts * opts, dsd_state * state); void processX2TDMAvoice (dsd_opts * opts, dsd_state * state); void processDSTAR_HD (dsd_opts * opts, dsd_state * state); +void processYSF(dsd_opts * opts, dsd_state * state); //YSF short dmr_filter(short sample); short nxdn_filter(short sample); -//borrow from LEH for testing 'improved NXDN detection' + int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr); /* Global functions */ //also borrowed uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength); @@ -711,7 +730,7 @@ void ncursesOpen (); void ncursesPrinter (dsd_opts * opts, dsd_state * state); void ncursesClose (); //endif ncurses -//borrow from LEH + /* NXDN frame decoding functions */ void ProcessNXDNFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted); void ProcessNxdnRCCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted); @@ -722,7 +741,7 @@ void ProcessNXDNIdleData (dsd_opts * opts, dsd_state * state, uint8_t Inverted); void ProcessNXDNFacch1Data (dsd_opts * opts, dsd_state * state, uint8_t Inverted); void ProcessNXDNUdchData (dsd_opts * opts, dsd_state * state, uint8_t Inverted); //end borrow -//borrow from LEH + /* NXDN functions */ void CNXDNConvolution_start(void); void CNXDNConvolution_decode(uint8_t s0, uint8_t s1); @@ -751,7 +770,7 @@ uint16_t CRC12BitNXDN(uint8_t * BufferIn, uint32_t BitLength); uint8_t CRC6BitNXDN(uint8_t * BufferIn, uint32_t BitLength); void ScrambledNXDNVoiceBit(int * LfsrValue, char * BufferIn, char * BufferOut, int NbOfBitToScramble); -//LEH dPMR + void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state); void printdPMRAmbeVoiceSample(dsd_opts * opts, dsd_state * state); void printdPMRRawVoiceFrame (dsd_opts * opts, dsd_state * state); @@ -762,7 +781,7 @@ uint8_t CRC7BitdPMR(uint8_t * BufferIn, uint32_t BitLength); 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]); -//LEH DMR + void ProcessDMR (dsd_opts * opts, dsd_state * state); void DMRDataFrameProcess(dsd_opts * opts, dsd_state * state); void DMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state); @@ -797,6 +816,7 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] 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 ProcessWTFData(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]); void Hamming_7_4_init(); @@ -836,9 +856,6 @@ void QR_16_7_6_encode(unsigned char *origBits, unsigned char *encodedBits); bool QR_16_7_6_decode(unsigned char *rxBits); void InitAllFecFunction(void); - -//end borrow from LEH - void resetState (dsd_state * state); #ifdef __cplusplus @@ -860,6 +877,8 @@ void CDMRTrellisPointsToDibits(const unsigned char* points, signed char* dibits) void CDMRTrellisBitsToTribits(const unsigned char* payload, unsigned char* tribits); bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload); + + #ifdef __cplusplus } #endif diff --git a/src/dmr_bs.c b/src/dmr_bs.c new file mode 100644 index 0000000..7d3c70d --- /dev/null +++ b/src/dmr_bs.c @@ -0,0 +1,462 @@ +#include "dsd.h" +#include "dmr_const.h" + +//A subroutine for processing each TDMA frame individually to allow for +//processing voice and/or data on both BS slots (channels) simultaneously +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 t_ambe_fr[4][24] = {9}; + char t_ambe_fr2[4][24] = {9}; + char t_ambe_fr3[4][24] = {9}; + 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]; + int EmbeddedSignallingOk; + unsigned int internalcolorcode; + int internalslot; + char redundancyA[36]; + char redundancyB[36]; + unsigned short int vc1; + unsigned short int vc2; + + //add time to mirror printFrameSync + time_t now; + char * getTime(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; + } + + //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; + + vc1 = 2; + vc2 = 2; + + short int loop = 1; + short int skipcount = 0; + + //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++) + { + dibit = getDibit(opts, state); + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + cachdata[i] = dibit; + state->dmr_stereo_payload[i] = dibit; + if(i == 2) + { + state->currentslot = (1 & (dibit >> 1)); + internalslot = (1 & (dibit >> 1)); + } + } + + //Setup for first AMBE Frame + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //First AMBE Frame, Full 36 + for(i = 0; i < 36; i++) + { + dibit = getDibit(opts, state); + 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 + + w++; + x++; + y++; + z++; + + } + + //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; + } + // end redundancy test, set B to A + for(i = 0; i < 36; i++) + { + redundancyB[i] = redundancyA[i]; + } + + //Setup for Second AMBE Frame + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //Second AMBE Frame, First Half 18 dibits just before 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+48] = dibit; + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + //short int l = 0; //do I still use this? + // signaling data or sync + for(i = 0; i < 24; i++) + { + dibit = getDibit(opts, state); + 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) // + { + EmbeddedSignalling[2*i] = (1 & (dibit >> 1)); // bit 1 + EmbeddedSignalling[2*i+1] = (1 & dibit); // bit 0 + } + + //Used for ProcessVoiceBurstSync + 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 + } + + } + + sync[24] = 0; + EmbeddedSignallingOk = -1; + if(QR_16_7_6_decode(EmbeddedSignalling)) + { + EmbeddedSignallingOk = 1; + } + + 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); + } + state->dmr_stereo_payload[i+90] = dibit; + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + //Setup for Third AMBE Frame + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //Third AMBE Frame, Full 36 + 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 + + w++; + x++; + y++; + z++; + + } + + //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; + } + if (internalslot == 1) + { + vc2 = 1; + } + } + + //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 "); + //fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | Data ", state->color_code); + 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 "); + //fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | Data ", state->color_code); + 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 6 data frames, drop back to getFrameSync and process subsequent data with processDMRdata + goto SKIP; + } + + if( (strcmp (sync, DMR_BS_DATA_SYNC) != 0) ) //only play voice no data sync // || (strcmp (sync, DMR_MS_DATA_SYNC) != 0) + { + if (EmbeddedSignallingOk == 0) + { + //goto END; + } + skipcount = 0; //reset skip count if processing voice frames + fprintf (stderr,"%s ", getTime()); + if (internalslot == 0) + { + fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | VC%d \n", state->color_code, vc1); + } + + if (internalslot == 1) + { + fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | VC%d \n", state->color_code, vc2); + } + if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues) + { + //process voice burst + ProcessVoiceBurstSync(opts, state); + fprintf (stderr, "\n"); + } + if (internalslot == 1 && vc2 == 6) //presumably when full (and no sync issues) + { + //process voice burst + ProcessVoiceBurstSync(opts, state); + fprintf (stderr, "\n"); + } + 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++; + } + if (internalslot == 1) + { + vc2++; + } + } + + // 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; + } + //using these conditions may cause excessive resyncs IF bad signal, + //but still better than getting stuck in a wonk wonk loop for too long. + //maybe consider making a opt variable for loose or aggressive resyncing for this sort of thing? + if (1 == 1) //set for more aggressive or less aggressive resync during accumulated playback errs + { + //errors caused due to playing MBE files out of sync, break loop + if (state->errs > 2 || state->errsR > 2) + { + //goto END; // + } + //errors caused due to playing MBE files out of sync, break loop + if (state->errs2 > 4 || state->errs2R > 4) + { + 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 + { + goto END; + } + + //since we are in a while loop, run ncursesPrinter here. + if (opts->use_ncurses_terminal == 1) + { + ncursesPrinter(opts, 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->errs2R = 0; + state->errs2 = 0; +} + +//Only process 2nd half voice payload (3rd frame) and then jump to full BS decoding +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}; + 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; + + //add time to mirror printFrameSync + time_t now; + char * getTime(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; + } + + //skip the 18 dibits of the 2nd frame, since we can't play only half a frame + skipDibit (opts, state, 18); + + //Setup for Third AMBE Frame + + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //Third AMBE Frame, Full 36 + for(i = 0; i < 36; i++) + { + dibit = getDibit(opts, state); + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr3[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + fprintf (stderr,"\n%s ", getTime()); + fprintf (stderr,"Sync: +DMR | Frame Sync | DMRSTEREO | VC1 FS \n"); + processMbeFrame (opts, state, NULL, ambe_fr3, NULL); + dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode + +} diff --git a/src/dmr_data.c b/src/dmr_data.c index d086880..d747695 100644 --- a/src/dmr_data.c +++ b/src/dmr_data.c @@ -49,6 +49,12 @@ processDMRdata (dsd_opts * opts, dsd_state * state) bursttype[4] = 0; dibit_p = state->dibit_buf_p - 90; + //using the estimate_symbol method for the dmr_payload_p buffer causes sync + //issues with P25, so only do it when frame_p25p1 == 0, or -fr option + if (opts->frame_p25p1 != 1) + { + dibit_p = state->dmr_payload_p - 90; + } // CACH for (i = 0; i < 12; i++) @@ -59,6 +65,11 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) + { + dibit = (int)state->dmr_stereo_payload[i]; + } + //fprintf(stderr, "%X", dibit); cachdata[i] = dibit; if (i == 2) { @@ -79,6 +90,15 @@ processDMRdata (dsd_opts * opts, dsd_state * state) state->slot1light[0] = ' '; state->slot1light[6] = ' '; } + //method to only see data from one slot or the other when data occupies both slots or gets cooked by sync + state->hardslot = 9; //0 to only listen to slot 0 voice, 1 for slot 1, 9 for both + if(state->hardslot != 9 && state->hardslot != state->currentslot) + //if(1==1) + { + fprintf (stderr, " Current Slot = %d", state->currentslot + 1); + fprintf (stderr, "\n"); //line break after breaking out of jail + //goto JUMP; + } } } cachdata[12] = 0; @@ -107,6 +127,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) + { + 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 @@ -119,6 +143,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) + { + dibit = (int)state->dmr_stereo_payload[61]; //61, not i+61 + } cc[0] = (1 & (dibit >> 1)); // bit 1 cc[1] = (1 & dibit); // bit 0 SlotType[0] = (1 & (dibit >> 1)); // bit 1 @@ -130,6 +158,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) + { + 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 @@ -147,6 +179,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) //state + { + dibit = (int)state->dmr_stereo_payload[63]; //(int) + } // burst = (unsigned int)((1 & (dibit >> 1)) << 3); // burst |= (unsigned int)((1 & (dibit >> 0)) << 2); // bursttype[0] = (1 & (dibit >> 1)) + 48; // bit 1 @@ -160,6 +196,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) //state + { + dibit = (int)state->dmr_stereo_payload[64]; //(int) + } // burst |= (unsigned int)((1 & (dibit >> 1)) << 1); // burst |= (unsigned int)((1 & (dibit >> 0)) << 0); // bursttype[2] = (1 & (dibit >> 1)) + 48; // bit 1 @@ -174,6 +214,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) + { + dibit = (int)state->dmr_stereo_payload[65]; + } SlotType[8] = (1 & (dibit >> 1)); // bit 1 SlotType[9] = (1 & dibit); // bit 0 @@ -186,6 +230,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { dibit = (dibit ^ 2); } + if (state->dmr_stereo == 1) + { + dibit = (int)state->dmr_stereo_payload[i+66]; //double check these i+ values make sure we are on the right ones + } syncdata[2*i] = (1 & (dibit >> 1)); // bit 1 syncdata[(2*i)+1] = (1 & dibit); // bit 0 sync[i] = (dibit | 1) + 48; @@ -216,6 +264,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state) sprintf(state->slot2light, "[slot2]"); } } + else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0) { state->currentslot = 0; @@ -236,11 +285,18 @@ processDMRdata (dsd_opts * opts, dsd_state * state) // Slot type - Second part - Parity bit for (i = 0; i < 5; i++) { - dibit = getDibit(opts, state); + if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method + { + 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 + } SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1 SlotType[(i*2) + 11] = (1 & dibit); // bit 0 } @@ -250,8 +306,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) else SlotTypeOk = 0; /* Slot Type parity checked => Fill the color code */ - state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]); + //state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]); + state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0); state->color_code_ok = SlotTypeOk; + //fprintf(stderr, "| Color Code=%02d ", (int)state->color_code); /* Reconstitute the burst type */ burst = (unsigned int)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]); @@ -263,13 +321,55 @@ processDMRdata (dsd_opts * opts, dsd_state * state) bursttype[3] = SlotType[7] + '0'; bursttype[4] = '\0'; + /* //delete this chunk later + if (state->dmr_stereo == 3) //may not need this any longer + { + burst = (state->dmr_stereo_payload[63] << 2) | (state->dmr_stereo_payload[64]); + //this whole split for getting burst and burst type is makes no sense + //why not just use burst + bursttype[0] = (((state->dmr_stereo_payload[63] >> 1) & 1) + '0'); + bursttype[1] = (((state->dmr_stereo_payload[63] >> 0) & 1) + '0'); + bursttype[2] = (((state->dmr_stereo_payload[64] >> 1) & 1) + '0'); + bursttype[3] = (((state->dmr_stereo_payload[64] >> 0) & 1) + '0'); + bursttype[4] = '\0'; + //fprintf(stderr, "BURST1 = %04b ", burst); + } + //figure out why the sync is wrong when dumping the payload to processDMRdata for MS Data + //I think its a conversion issue with the payload(chat) from the dibit buffer (int *) + //actually, its the dibit buffer storing as 1 and 3 only (GFSK) when sync drops + //made a seperate dmr buffer, but still, when sync loss and regain, takes a few syncs to calibrate center/umid/lmid properly + //getting accurate MS data AND leading BS data may be impossible without saving the symbols, calibrating, then going back + //and running the buffer payload, but I honestly don't know right now, probably easier to start from stratch than fix this + if (state->dmr_ms_mode == 7) + { + burst = (unsigned int)((SlotType[4] << 3) | (SlotType[5] << 2) | (SlotType[6] << 1) | SlotType[7]); + bursttype[0] = SlotType[4] + '0'; + bursttype[1] = SlotType[5] + '0'; + bursttype[2] = SlotType[6] + '0'; + bursttype[3] = SlotType[7] + '0'; + bursttype[4] = '\0'; + //fprintf(stderr, "BURST1 = %04b ", burst); + } + + if (state->dmr_stereo == 3) + { + fprintf(stderr, "BURST2 = %b%b%b%b Slot Dump ", SlotType[4], SlotType[5], SlotType[6], SlotType[7] ); + fprintf(stderr, " Slot Dump = "); + for (i = 0; i < 20; i++) + { + fprintf(stderr, "%b", SlotType[i]); + } + fprintf(stderr, "\n "); + } + */ + if (strcmp (bursttype, "0000") == 0) { sprintf(state->fsubtype, " PI Header "); } else if (strcmp (bursttype, "0001") == 0) { - sprintf(state->fsubtype, " VOICE Header "); + sprintf(state->fsubtype, " VOICE LC Header "); } else if (strcmp (bursttype, "0010") == 0) { @@ -316,31 +416,55 @@ processDMRdata (dsd_opts * opts, dsd_state * state) //trellis for (i = 0; i < 49; i++) { - dibit = getDibit(opts, state); + if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method + { + 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 + } trellisdibits[i+49] = dibit; info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1 info[(2*i) + 99] = (1 & dibit); // bit 0 } // Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit) - skipDibit (opts, state, 12 + 49 + 5); + if (state->dmr_stereo == 0) + { + skipDibit (opts, state, 12 + 49 + 5); + } if (opts->errorbars == 1) { /* Print the color code */ - fprintf(stderr, "| Color Code=%02d ", (int)state->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, "| "); //if(state->color_code_ok) fprintf(stderr, "(OK) |"); - if(state->color_code_ok) fprintf(stderr, "(CRC OK ) |"); //add line break - else fprintf(stderr, "(CRC ERR) |"); //add line break + 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 burst type: %s", bursttype); + fprintf(stderr, " Unknown"); } else { @@ -368,6 +492,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state) /* 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; } @@ -375,6 +500,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state) /* 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; } @@ -386,7 +512,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state) break; } - /* Burst = MBC */ + /* Burst = MBC Continuation*/ case 0b0101: { //ProcessMBCData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType); @@ -410,14 +536,15 @@ processDMRdata (dsd_opts * opts, dsd_state * state) /* Burst = RATE 3/4 DATA */ case 0b1000: { - //disabled until a viable viterbi/trellis decoding solution is worked out - //Process34Data(opts, state, trellisdibits, (uint8_t *)syncdata, (uint8_t *)SlotType); + //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_color_code = state->color_code; //try setting this on idle if crc ok break; } @@ -428,10 +555,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state) break; } - /* Burst = WTF 1101 Type DATA */ + /* Burst = Unified Single Block DATA */ case 0b1101: { - //ProcessWTFData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType); + //ProcessUnifiedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType); break; } @@ -448,5 +575,5 @@ processDMRdata (dsd_opts * opts, dsd_state * state) { fprintf(stderr, "\n"); } - + //JUMP: } /* End processDMRdata() */ diff --git a/src/dmr_ms.c b/src/dmr_ms.c new file mode 100644 index 0000000..e8dc69d --- /dev/null +++ b/src/dmr_ms.c @@ -0,0 +1,672 @@ +#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 t_ambe_fr[4][24] = {9}; + char t_ambe_fr2[4][24] = {9}; + char t_ambe_fr3[4][24] = {9}; + 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]; + int EmbeddedSignallingOk; + unsigned int internalcolorcode; + int internalslot; + int activeslot; + char redundancyA[36]; + char redundancyB[36]; + unsigned short int vc1; + unsigned short int vc2; + //add time to mirror printFrameSync + time_t now; + char * getTime(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; + } + + //Init the color code status + state->color_code_ok = 0; + + vc1 = 2; + vc2 = 2; + + short int loop = 1; + short int skipcount = 0; + + //Hardset variables for MS + state->currentslot = 0; + internalslot = 0; + activeslot = 0; + + //Run Loop while the getting is good + while (loop == 1) { + + // No CACH in MS Mode? + for(i = 0; i < 12; i++) + { + dibit = getDibit(opts, state); + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + cachdata[i] = dibit; + state->dmr_stereo_payload[i] = dibit; + if(i == 2) + { + //state->currentslot = (1 & (dibit >> 1)); + //internalslot = (1 & (dibit >> 1)); + } + } + + //Setup for first AMBE Frame + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //First AMBE Frame, Full 36 + for(i = 0; i < 36; i++) + { + dibit = getDibit(opts, state); + 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 + + w++; + x++; + y++; + z++; + + } + + //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; + x = rX; + y = rY; + z = rZ; + + //Second AMBE Frame, First Half 18 dibits just before 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+48] = dibit; + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + // signaling data or sync + for(i = 0; i < 24; i++) + { + dibit = getDibit(opts, state); + 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) // + { + 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 + } + + } + + sync[24] = 0; + EmbeddedSignallingOk = -1; + if(QR_16_7_6_decode(EmbeddedSignalling)) + { + EmbeddedSignallingOk = 1; + } + + 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); + } + state->dmr_stereo_payload[i+90] = dibit; + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + 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 + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //Third AMBE Frame, Full 36 + 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 + + w++; + x++; + y++; + z++; + + //end early to allow next sync without skipping a voice superframe + //only use if only doing 6 VC and nothing else, otherwise, will lose sync + if (vc1 == 6 && i == 30) + { + //i = 36; + } + + } + + if ( (strcmp (sync, DMR_MS_VOICE_SYNC) == 0) ) // + { + if (internalslot == 0) + { + vc1 = 1; + fprintf (stderr, "MS Slot 1 Voice Sync \n"); + //activeslot = 0; + + } + if (internalslot == 1) + { + vc2 = 1; + //fprintf (stderr, "MS Slot 2 Voice Sync \n"); + //activeslot = 1; + } + state->dmr_ms_mode = 1; //set to 1 here?? + } + + state->dmr_ms_mode = 1; //set to 1 here? + //testing to see if we need to do something to get MS mode working properly, + //get proper sync, skip frames, etc. + if (state->dmr_ms_mode == 1 ) //&& vc1 < 7 + { + //do something + fprintf (stderr, "MS MODE "); + } + + //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 + { + 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); + //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); + //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; + } + + //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) ) + { + 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); + 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); + fprintf (stderr,"Sync: +DMR "); + //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; + } + + if( (strcmp (sync, DMR_MS_DATA_SYNC) != 0) && internalslot == activeslot && vc1 < 7) //only play voice no MS Data Sync and vc1 below 6 (no voice resync) + { + + skipcount = 0; //reset skip count if processing voice frames + fprintf (stderr,"%s ", getTime()); + if (internalslot == 0) + { + fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | VC%d \n", state->color_code, vc1); + } + + if (internalslot == 1) + { + fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | VC%d \n", state->color_code, vc2); + } + if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues) + { + //process voice burst + ProcessVoiceBurstSync(opts, state); + fprintf (stderr, "\n"); + } + if (internalslot == 1 && vc2 == 6) //presumably when full (and no sync issues) + { + //process voice burst + //ProcessVoiceBurstSync(opts, state); + //fprintf (stderr, "\n"); + } + + 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++; + } + if (internalslot == 1) + { + vc2++; + } + } + 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 slot? + state->dmr_ms_rc = 0; + + //since we are in a repetitive loop, run ncursesPrinter here + if (opts->use_ncurses_terminal == 1) + { + ncursesPrinter(opts, state); + } + + } // end while loop + //reset these variables when exiting MS mode. + END: + //get first half payload dibits and store them in the payload for the next repitition + skipDibit (opts, state, 144); + //CACH + First Half Payload = 12 + 54 + for (i = 0; i < 66; i++) //66 + { + dibit = getDibit(opts, state); + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + state->dmr_stereo_payload[i] = dibit; + //fprintf(stderr, "%X", state->dmr_stereo_payload[i]); + } + + state->dmr_stereo = 0; + state->dmr_ms_mode = 0; + state->dmr_ms_rc = 0; + +} + +//Only process 2nd half voice payload (3rd frame) and then jump to full MS Voice decoding. +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}; + 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; + + //add time to mirror sync + time_t now; + char * getTime(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; + } + + dibit_p = state->dmr_payload_p - 90; + //payload buffer tests + //CACH + First Half Payload + Sync = 12 + 54 + 24 + //fprintf(stderr, "\n Full DMR Stereo Dump Dibits 90 From Buffer\n"); + for (i = 0; i < 90; i++) //90 + { + state->dmr_stereo_payload[i] = *dibit_p; + dibit_p++; + //fprintf(stderr, "%X", state->dmr_stereo_payload[i]); + } + //end payload buffer test + + for(i = 0; i < 12; i++) + { + dibit = state->dmr_stereo_payload[i]; + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + cachdata[i] = dibit; + } + + //Setup for first AMBE Frame + + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //First AMBE Frame, Full 36 + for(i = 0; i < 36; i++) + { + dibit = state->dmr_stereo_payload[i+12]; + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + //state->dmr_stereo_payload[i+12] = dibit; + ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + //Setup for Second AMBE Frame + + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling + for(i = 0; i < 18; i++) + { + dibit = state->dmr_stereo_payload[i+48]; + 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 + + w++; + x++; + y++; + 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); + } + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + //Setup for Third AMBE Frame + + //Interleave Schedule + w = rW; + x = rX; + y = rY; + z = rZ; + + //Third AMBE Frame, Full 36 + for(i = 0; i < 36; i++) + { + dibit = getDibit(opts, state); + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr3[*y][*z] = (1 & dibit); // bit 0 + + w++; + x++; + y++; + z++; + + } + + fprintf (stderr, "MS MODE "); + fprintf (stderr,"%s ", getTime()); + fprintf (stderr,"Sync: +DMR | Color Code=XX | DMRSTEREO | VC1 FS \n"); + processMbeFrame (opts, state, NULL, ambe_fr, NULL); + processMbeFrame (opts, state, NULL, ambe_fr2, NULL); + processMbeFrame (opts, state, NULL, ambe_fr3, NULL); + skipDibit (opts, state, 144); //skip to next TDMA slot + dmrMS (opts, state); //bootstrap into full TDMA frame + +} + +void dmrMSData (dsd_opts * opts, dsd_state * state) +{ + int i; + int dibit; + int *dibit_p; + unsigned int burst; + + //add time to mirror sync + time_t now; + char * getTime(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; + } + + //CACH + First Half Payload + Sync = 12 + 54 + 24 + //fprintf(stderr, "\n Full DMR Stereo Dump Dibits 90 From Buffer\n"); + for (i = 0; i < 90; i++) //90 + { + //fprintf(stderr, "%X", state->dmr_stereo_payload[i]); + } + + for (i = 0; i < 54; i++) + { + dibit = getDibit(opts, state); + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + state->dmr_stereo_payload[i+90] = dibit; + + } + //fprintf(stderr, "\n"); + //print nice pretty lines + fprintf (stderr, "MS MODE "); + fprintf (stderr, "%s ", getTime()); + //fprintf (stderr, "Sync: +MS DATA | Color Code=XX | DMRSTEREO | Data "); + fprintf (stderr, "Sync: +DMR "); + //fprintf (stderr, "\n "); + + //sprintf for slot 1, doesn't matter, just makes print out of data look uniform setting ahead of time + sprintf(state->slot1light, "[slot1]"); + sprintf(state->slot2light, " slot2 "); + + //process data + state->dmr_stereo = 1; + state->dmr_ms_mode = 1; + processDMRdata (opts, state); + state->dmr_stereo = 0; + state->dmr_ms_mode = 0; + + //get potential first half payload dibits and store them in the payload for the next repitition, MS voice or data. + skipDibit (opts, state, 144); + //CACH + First Half Payload = 12 + 54 + for (i = 0; i < 66; i++) //66 + { + dibit = getDibit(opts, state); + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + state->dmr_stereo_payload[i] = dibit; + //fprintf(stderr, "%X", state->dmr_stereo_payload[i]); + } + +} diff --git a/src/dmr_process_voice.c b/src/dmr_process_voice.c index da3519f..9c3e2af 100644 --- a/src/dmr_process_voice.c +++ b/src/dmr_process_voice.c @@ -19,6 +19,26 @@ #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 BP[256] = { 0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903, 0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306, @@ -54,71 +74,53 @@ int BP[256] = { 0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255 }; // -void ProcessDMR (dsd_opts * opts, dsd_state * state) -{ - uint32_t i, j; - uint32_t Frame; - TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; - TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameL = NULL; - TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameR = NULL; - - int *errs; - int *errs2; - int *errsR; - int *errs2R; - unsigned long long int k; - int x; - k = 0; - - if(state->currentslot == 0) - { - TSVoiceSupFrame = &state->TS1SuperFrame; - } - else - { - TSVoiceSupFrame = &state->TS2SuperFrame; - } - //TSVoiceSupFrameL = &state->TS1SuperFrame; - //TSVoiceSupFrameR = &state->TS2SuperFrame; - -// -if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0) +if (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0) { + fprintf (stderr, "%s", KYEL); fprintf(stderr, " BPK %lld", state->K); + fprintf (stderr, "%s", KNRM); + k = BP[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 < 49; j++) - { - k = BP[state->K]; - k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k ); + { x = ( ((k << j) & 0x800000000000) >> 47 ); - TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x; - } + TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x; + } + } + } +} + +if (state->currentslot == 1 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0) +{ + fprintf (stderr, "%s", KYEL); + fprintf(stderr, " BPK %lld", state->K); + fprintf (stderr, "%s", KNRM); + k = BP[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 < 49; j++) + { + x = ( ((k << j) & 0x800000000000) >> 47 ); + TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x; + } } } } // - for(Frame = 0; Frame < 6; Frame++) +if (state->currentslot == 0) +{ + for(Frame = 0; Frame < 6; Frame++) //6 { /* 1 DMR frame contains 3 AMBE voice samples */ - //fprintf (stderr, "\n 1AMBE "); for(i = 0; i < 3; i++) { - - errs = (int*)&(TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs1[i]); - errs2 = (int*)&(TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs2[i]); - state->errs = TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement - state->errs2 = TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement - - mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str, - TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i], - state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); - - /* - if (state->currentslot == 0) - { errs = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]); errs2 = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]); state->errs = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement @@ -128,45 +130,16 @@ if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0) TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i], state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); - state->debug_audio_errors += *errs2; - //state->debug_audio_errors += *errs2R; - - processAudio(opts, state); - playSynthesizedVoice (opts, state); - } - if (state->currentslot == 1) - { - errsR = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]); - errs2R = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]); - state->errs = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement - state->errs2 = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement - //state->audio_out_temp_bufR = state->audio_out_temp_buf; - - mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errsR, errs2R, state->err_strR, - TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i], - state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); - - state->debug_audio_errors += *errs2R; - processAudio(opts, state); - playSynthesizedVoice (opts, state); - } - - mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, errsR, errs2R, state->err_strR, - TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i], - state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); - */ - if (opts->mbe_out_f != NULL) { - saveAmbe2450Data (opts, state, TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]); + saveAmbe2450Data (opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]); } if (opts->errorbars == 1) { - fprintf(stderr, "%s", state->err_str); + //fprintf(stderr, "%s", state->err_str); } state->debug_audio_errors += *errs2; - //state->debug_audio_errors += *errs2R; processAudio(opts, state); //playSynthesizedVoice (opts, state); @@ -176,14 +149,59 @@ if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0) writeSynthesizedVoice (opts, state); } - if (opts->audio_out == 1 && opts->p25enc != 1) //play only if not equal to enc value, going to set p25enc to 1 if enc detected?? + //play only if not equal to enc value, going to set p25enc to 1 if enc detected?? + if (opts->audio_out == 1 && opts->p25enc != 1) { playSynthesizedVoice (opts, state); } } /* End for(i = 0; i < 3; i++) */ } /* End for(Frame = 0; Frame < 6; Frame++) */ -} /* End ProcessDMREncryption() */ - + } //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->mbe_out_f != NULL) + { + saveAmbe2450Data (opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]); + } + if (opts->errorbars == 1) + { + //fprintf(stderr, "%s", state->err_str); + } + + state->debug_audio_errors += *errs2; + + processAudio(opts, state); + //playSynthesizedVoice (opts, state); + + if (opts->wav_out_f != NULL) + { + writeSynthesizedVoice (opts, state); + } + + //play only if not equal to enc value, going to set p25enc to 1 if enc detected?? + if (opts->audio_out == 1 && opts->p25enc != 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 */ diff --git a/src/dmr_sync.c b/src/dmr_sync.c index ade6d1d..17f3e3a 100644 --- a/src/dmr_sync.c +++ b/src/dmr_sync.c @@ -22,6 +22,8 @@ //#define PRINT_TERMINAISON_LC_BYTES //#define PRINT_VOICE_BURST_BYTES +//Need to work in a way to seperate data bursts on seperate slots, having data pouring into both slots simultaneously +//Need to make seperate superframe arrays for each void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98], uint8_t syncdata[48], uint8_t SlotType[20]) { //NEED TRELLIS DECODER HERE @@ -36,8 +38,9 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; uint8_t R[3]; uint8_t BPTCReservedBits = 0; + short int slot = 0; - + slot = (short int)state->currentslot; /* Check the current time slot */ if(state->currentslot == 0) { @@ -63,35 +66,46 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] unsigned char tdibits_to_bits[196]; if (1 == 2) { - fprintf (stderr, "\n Raw Trellis Dibits to Bits\n "); + //fprintf (stderr, "\n Raw Trellis Dibits to Bits\n "); for (i = 0; i < 98; i++) { tdibits_to_bits[i * 2] = (tdibits[i] >> 0) & 1; tdibits_to_bits[(i * 2) + 1] = (tdibits[1] >> 1) & 1; - fprintf (stderr, "%d%d", tdibits_to_bits[i * 2], tdibits_to_bits[(i * 2) + 1]); - } - } - if (opts->payload == 1) - { - //fprintf (stderr, "\n Raw Trellis Dibits\n "); - for (i = 0; i < 98; i++) - { - //fprintf (stderr, "#%d [%X] ", i, tdibits[i]); - //tdibits_reverse[97-i] = tdibits[i]; - //tdibits_reverse[97-i] = ((tdibits[i] & 1)<<1) | ((tdibits[i] & 2)>>1); - //tdibits_inverted[i] = tdibits[i] ^ 2; + //fprintf (stderr, "%d%d", tdibits_to_bits[i * 2], tdibits_to_bits[(i * 2) + 1]); } } + //fprintf (stderr, "\n Raw Trellis Dibits\n "); + for (i = 0; i < 98; i++) + { + //fprintf (stderr, "#%d [%X] ", i, tdibits[i]); + tdibits_reverse[97-i] = tdibits[i]; + //tdibits_reverse[97-i] = ((tdibits[i] & 1)<<1) | ((tdibits[i] & 2)>>1); + //tdibits_inverted[i] = tdibits[i] ^ 2; + } + + unsigned char TrellisReturn[18]; - CDMRTrellisDecode(tdibits, TrellisReturn); //figure out how this works!! + //CDMRTrellisDecode(tdibits, TrellisReturn); //figure out how this works!! + CDMRTrellisDecode(tdibits_reverse, TrellisReturn); //NEEDS REVERSE DIBITS! if (opts->payload == 1) { + fprintf (stderr,"%s", KCYN); fprintf (stderr, "\nFull 3/4 Rate Trellis Payload\n "); for (i = 0; i < 18; i++) { fprintf (stderr, "[%02X]", TrellisReturn[i]); } + fprintf (stderr, "\n Hex to Ascii - "); + for (i = 0; i < 18; i++) + { + if (TrellisReturn[i] <= 0x7E && TrellisReturn[i] >=0x20) + { + fprintf (stderr, "%c", TrellisReturn[i]); + } + else fprintf (stderr, "."); + } + fprintf (stderr,"%s", KNRM); //change back to normal } /* Extract the BPTC 196,96 DMR data */ //IrrecoverableErrors = BPTC_196x96_Extract_Data(DeInteleavedData, DmrDataBit, R); @@ -100,7 +114,7 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] // /* 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); - /* + for(i = 0, j = 0; i < 18; i++, j+=8) { DmrDataBit[j + 0] = (TrellisReturn[i] >> 7) & 0x01; @@ -112,8 +126,9 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] DmrDataBit[j + 6] = (TrellisReturn[i] >> 1) & 0x01; DmrDataBit[j + 7] = (TrellisReturn[i] >> 0) & 0x01; } - */ + //the reverse card + /* for(i = 0, j = 0; i < 18; i++, j+=8) { DmrDataBit[j + 0] = (TrellisReturn[17-i] >> 0) & 0x01; @@ -125,12 +140,14 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] DmrDataBit[j + 6] = (TrellisReturn[17-i] >> 6) & 0x01; DmrDataBit[j + 7] = (TrellisReturn[17-i] >> 7) & 0x01; } + */ /* Convert the 96 bit of voice LC Header data into 12 bytes */ k = 0; - for(i = 0; i < 18; i++) + for(i = 0; i < 16; i++) //16, or 18? seems to have two extra bytes in front currently { - state->dmr_34_rate_sf[i] = state->dmr_34_rate_sf[i+18]; //shift middle to left 12 to load up new round - state->dmr_34_rate_sf[i+18] = state->dmr_34_rate_sf[i+36]; //shift far right to middle 12, going three frames deep; + state->dmr_34_rate_sf[slot][i] = state->dmr_34_rate_sf[slot][i+16]; //shift middle to left 12 to load up new round + state->dmr_34_rate_sf[slot][i+16] = state->dmr_34_rate_sf[slot][i+32]; //shift far right middle to left middle 12, going four frames deep; + state->dmr_34_rate_sf[slot][i+32] = state->dmr_34_rate_sf[slot][i+48]; DmrDataByte[i] = 0; for(j = 0; j < 8; j++) { @@ -139,10 +156,25 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] DmrDataByte[i] = DmrDataByte[i] | (DmrDataBit[k] & 0x01); k++; } - //state->dmr_34_rate_sf[i+36] = DmrDataByte[i]; //copy byte to right hand side of shift frame - state->dmr_34_rate_sf[i+36] = TrellisReturn[i]; + //state->dmr_34_rate_sf[i+36] = ~DmrDataByte[i]; //copy byte to right hand side of shift frame + //state->dmr_34_rate_sf[i+36] = ~TrellisReturn[i]& 0xFF; + state->dmr_34_rate_sf[slot][i+48] = TrellisReturn[i+2]; //plus two to skip the first two bytes } - + if (opts->payload == 2) + { + fprintf (stderr, "%s",KGRN); + fprintf (stderr, "\n Rate 3/4 Superframe - Slot [%d]\n ",slot+1); + for (i = 0; i < (16*4); i++) + { + fprintf (stderr, "[%02X]", state->dmr_34_rate_sf[slot][i]); + if (i == 15 || i == 31 || i == 47) + { + fprintf (stderr, "\n "); //line break and two spaces after each 16 bytes + } + } + fprintf (stderr, "%s ", KNRM); + } + //fprintf (stderr, "%s ", KNRM); /* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */ CRCExtracted = 0; //for(i = 0; i < 24; i++) @@ -167,8 +199,9 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] /* Convert corrected 12 bytes into 96 bits */ - //Headers and Addresses + + /* if ( (state->dmr_34_rate_sf[0] & 0x3F) == 0x05) //0x3F?? or == 0x05 { fprintf (stderr, "\n IP4 Header"); @@ -185,27 +218,41 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] fprintf (stderr, " [%08d]", (state->dmr_34_rate_sf[5] <<16 ) + (state->dmr_34_rate_sf[6] << 8) + state->dmr_34_rate_sf[7] ); fprintf (stderr, " - Port %05d", (state->dmr_34_rate_sf[10] << 8) + state->dmr_34_rate_sf[11]); } + */ //LRRP - if (state->dmr_34_rate_sf[0] == 0x0 && state->dmr_34_rate_sf[4] == 0x0D) //Start LRRP now + if ( (state->dmr_34_rate_sf[slot][0] & 0x7F) == 0x45) //Start LRRP now { - fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); - for (short i = 6; i <= 36; i++) + fprintf (stderr, "%s ", KMAG); + //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? + //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); + for (short i = 1; i < 64; i++) //find way to get padding so we only go as deep as we need to! { - if ( state->dmr_34_rate_sf[i] == 0x34 ) //timestamp + if ( state->dmr_34_rate_sf[slot][i] == 0x0C) //Source and Destination info + { + fprintf (stderr, "\n Source:"); + fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_34_rate_sf[slot][i+0] & 0x3F), state->dmr_34_rate_sf[slot][i+1], state->dmr_34_rate_sf[slot][i+2], state->dmr_34_rate_sf[slot][i+3]); //strip first two bits off 1st byte + fprintf (stderr, " [%08d]", (state->dmr_34_rate_sf[slot][i+1] <<16 ) + (state->dmr_34_rate_sf[slot][i+2] << 8) + state->dmr_34_rate_sf[slot][i+3] ); + fprintf (stderr, " - Port %05d", (state->dmr_34_rate_sf[slot][i+8] << 8) + state->dmr_34_rate_sf[slot][i+9]); + fprintf (stderr, "\n Destination:"); + fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_34_rate_sf[slot][i+4] & 0x3F), state->dmr_34_rate_sf[slot][i+5], state->dmr_34_rate_sf[slot][6], state->dmr_34_rate_sf[slot][i+7]); //strip first two bits off 4th byte?? + fprintf (stderr, " [%08d]", (state->dmr_34_rate_sf[slot][i+5] <<16 ) + (state->dmr_34_rate_sf[slot][i+6] << 8) + state->dmr_34_rate_sf[slot][i+7] ); + fprintf (stderr, " - Port %05d", (state->dmr_34_rate_sf[slot][i+10] << 8) + state->dmr_34_rate_sf[slot][i+11]); + } + if ( state->dmr_34_rate_sf[slot][i] == 0x34 ) //timestamp { fprintf (stderr, "\n LRRP - Timestamp: "); - fprintf (stderr, "%4d.", (state->dmr_34_rate_sf[i+1] << 6) + (state->dmr_34_rate_sf[i+2] >> 2) ); //4 digit year - fprintf (stderr, "%02d.", ( ((state->dmr_34_rate_sf[i+2] & 0x3) << 2) + ((state->dmr_34_rate_sf[i+3] & 0xC0) >> 6)) ); //2 digit month - fprintf (stderr, "%02d", ( (state->dmr_34_rate_sf[i+3] & 0x30) >> 1) + ((state->dmr_34_rate_sf[i+3] & 0x0E) >> 1) ); //2 digit day - fprintf (stderr, " %02d:",( (state->dmr_34_rate_sf[i+3] & 0x01) << 4) + ((state->dmr_34_rate_sf[i+4] & 0xF0) >> 4) ); //2 digit hour - fprintf (stderr, "%02d:",( (state->dmr_34_rate_sf[i+4] & 0x0F) << 2) + ((state->dmr_34_rate_sf[i+5] & 0xC0) >> 6) ); //2 digit minute - fprintf (stderr, "%02d", ( (state->dmr_34_rate_sf[i+5] & 0x3F) << 0) ); //2 digit second + fprintf (stderr, "%4d.", (state->dmr_34_rate_sf[slot][i+1] << 6) + (state->dmr_34_rate_sf[slot][i+2] >> 2) ); //4 digit year + fprintf (stderr, "%02d.", ( ((state->dmr_34_rate_sf[slot][i+2] & 0x3) << 2) + ((state->dmr_34_rate_sf[slot][i+3] & 0xC0) >> 6)) ); //2 digit month + fprintf (stderr, "%02d", ( (state->dmr_34_rate_sf[slot][i+3] & 0x30) >> 1) + ((state->dmr_34_rate_sf[slot][i+3] & 0x0E) >> 1) ); //2 digit day + fprintf (stderr, " %02d:",( (state->dmr_34_rate_sf[slot][i+3] & 0x01) << 4) + ((state->dmr_34_rate_sf[slot][i+4] & 0xF0) >> 4) ); //2 digit hour + fprintf (stderr, "%02d:",( (state->dmr_34_rate_sf[slot][i+4] & 0x0F) << 2) + ((state->dmr_34_rate_sf[slot][i+5] & 0xC0) >> 6) ); //2 digit minute + fprintf (stderr, "%02d", ( (state->dmr_34_rate_sf[slot][i+5] & 0x3F) << 0) ); //2 digit second } - if ( state->dmr_34_rate_sf[i] == 0x51 ) //lattitude and longitude + if ( state->dmr_34_rate_sf[slot][i] == 0x51 ) //lattitude and longitude { fprintf (stderr, "\n LRRP -"); fprintf (stderr, " Lat: "); - if (state->dmr_34_rate_sf[i+1] & 0x80) //first bit indicates a sign, or hemisphere? + if (state->dmr_34_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { fprintf (stderr, "-"); } @@ -213,47 +260,79 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] long int lrrplon; double lat_unit = (double)180/(double)4294967295; double lon_unit = (double)360/(double)4294967295; - lrrplat = ( ( ((state->dmr_34_rate_sf[i+1] & 0x7F ) << 24 ) + (state->dmr_34_rate_sf[i+2] << 16) + (state->dmr_34_rate_sf[i+3] << 8) + state->dmr_34_rate_sf[i+4]) * 1 ); - lrrplon = ( ( (state->dmr_34_rate_sf[i+5] << 24 ) + (state->dmr_34_rate_sf[i+6] << 16) + (state->dmr_34_rate_sf[i+7] << 8) + state->dmr_34_rate_sf[i+8]) * 1 ); + lrrplat = ( ( ((state->dmr_34_rate_sf[slot][i+1] & 0x7F ) << 24 ) + (state->dmr_34_rate_sf[slot][i+2] << 16) + (state->dmr_34_rate_sf[slot][i+3] << 8) + state->dmr_34_rate_sf[slot][i+4]) * 1 ); + lrrplon = ( ( (state->dmr_34_rate_sf[slot][i+5] << 24 ) + (state->dmr_34_rate_sf[slot][i+6] << 16) + (state->dmr_34_rate_sf[slot][i+7] << 8) + state->dmr_34_rate_sf[slot][i+8]) * 1 ); fprintf (stderr, "%.5lf ", ((double)lrrplat) * lat_unit); fprintf (stderr, " Lon: "); - if (state->dmr_34_rate_sf[i+5] & 0x80) //first bit indicates a sign, or hemisphere? + if (state->dmr_34_rate_sf[slot][i+5] & 0x80) //first bit indicates a sign, or hemisphere? + { + //fprintf (stderr, "-"); + } + fprintf (stderr, "%.5lf", (lrrplon * lon_unit) ); + sprintf ( state->dmr_lrrp[3], "Lat: %.5lf Lon: %.5lf", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + //print for easy copy/paste into browser? + fprintf (stderr, " ("); + if (state->dmr_34_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { fprintf (stderr, "-"); } - fprintf (stderr, "%.5lf", (lrrplon * lon_unit) ); + fprintf (stderr, "%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); } - if ( state->dmr_34_rate_sf[i] == 0x6C ) + if ( state->dmr_34_rate_sf[slot][i] == 0x6C ) { //either Plus is wrong, or I'm wrong on higher velocities exceeding 0xFF. //fprintf (stderr, "\n LRRP - Vi %02X Vf %02X Velocity Units (hex)", state->dmr_34_rate_sf[i+1], state->dmr_34_rate_sf[i+2]); - double velocity = ( ((double)( (state->dmr_34_rate_sf[i+1] ) + state->dmr_34_rate_sf[i+2] )) / ( (double)128)); + double velocity = ( ((double)( (state->dmr_34_rate_sf[slot][i+1] ) + state->dmr_34_rate_sf[slot][i+2] )) / ( (double)128)); //fprintf (stderr, "\n LRRP - %.4lf Meters Per Second", velocity); fprintf (stderr, "\n LRRP - %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity) ); + sprintf ( state->dmr_lrrp[1], "Vel: %.4lf kph", (3.6 * velocity)); } - if ( state->dmr_34_rate_sf[i] == 0x56 ) + if ( state->dmr_34_rate_sf[slot][i] == 0x56 ) { //check for appropriate terminology here - Heading, bearing, course, or track? - fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_34_rate_sf[i+1] * 2); + fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_34_rate_sf[slot][i+1] * 2); + sprintf ( state->dmr_lrrp[2], "Dir: %d Deg", state->dmr_34_rate_sf[slot][i+1] * 2); } } } + fprintf (stderr, "%s ", KNRM); //Full if (opts->payload == 2) { - fprintf (stderr, "\nFull 3/4 Rate Payload DmrDataByte (reverse bits)\n "); + fprintf (stderr, "\nFull 3/4 Rate Payload DmrDataByte (reverse)\n "); for (i = 0; i < 18; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + + fprintf (stderr, "\nFull 3/4 Rate Payload DmrDataByte (reverse inverted)\n "); + for (i = 0; i < 18; i++) + { + fprintf (stderr, "[%02X]", ~DmrDataByte[i] & 0xFF); + } } } +//This is Data Header, why did I name it Data Data void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]) { + short int slot = 0; + slot = (short int)state->currentslot; + + //clear out the 3/4 rate data superframe so we don't repeat old info + for (short int i = 0; i < (16*4); i++) + { + state->dmr_34_rate_sf[slot][i] = 0; + } + //clear out the 1/2 rate data superframe so we don't repeat old info + for (short int i = 0; i < (12*3); i++) + { + state->dmr_12_rate_sf[slot][i] = 0; + } + //Placeholder uint32_t i, j, k; uint32_t CRCExtracted = 0; @@ -317,18 +396,18 @@ void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint for(i = 0; i < 16; i++) { CRCExtracted = CRCExtracted << 1; - CRCExtracted = CRCExtracted | (uint32_t)(DmrDataBit[i + 72] & 1); - //CRCExtracted = CRCExtracted | (uint32_t)(DmrDataBit[i + 80] & 1); //80-96 for PI header + //CRCExtracted = CRCExtracted | (uint32_t)(DmrDataBit[i + 72] & 1); + CRCExtracted = CRCExtracted | (uint32_t)(DmrDataBit[i + 80] & 1); //80-96 for PI header } //Look into whether or not we need to run these CRC checks for this header information //and see if its applied the same or differently /* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */ - CRCExtracted = CRCExtracted ^ 0x969696; //does this mask get applied here though for PI? - //CRCExtracted = CRCExtracted ^ 0x6969; + //CRCExtracted = CRCExtracted ^ 0x969696; //does this mask get applied here though for PI? + CRCExtracted = CRCExtracted ^ 0xCCCC; /* Check/correct the full link control data and compute the Reed-Solomon (12,9) CRC */ - CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x969696); + CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0xCCCC); //CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x6969); /* Convert corrected 12 bytes into 96 bits */ @@ -347,15 +426,16 @@ void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint //test if((IrrecoverableErrors == 0) && CRCCorrect) { - fprintf (stderr, "\n(Data CRC Okay)"); + //fprintf (stderr, "\n(Data CRC Okay)"); } else if((IrrecoverableErrors == 0)) { - fprintf (stderr, "\n(Data FEC Okay)"); + //fprintf (stderr, "\n(Data FEC Okay)"); } - else fprintf (stderr, ("\n(Data CRC Fail, FEC Fail)")); + else {}//fprintf (stderr, ("\n(Data CRC Fail, FEC Fail)")); // + fprintf (stderr, "%s ", KMAG); if (DmrDataByte[0] == 0x43) { fprintf (stderr, "\n IP4 Source IP:"); @@ -374,14 +454,17 @@ void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint fprintf (stderr, " Destination:"); fprintf (stderr, " %d", (DmrDataByte[5] <<16 ) + (DmrDataByte[6] <<8 ) + DmrDataByte[7] ); } + fprintf (stderr, "%s ", KNRM); //Full if (opts->payload == 1) { - fprintf (stderr, "\nFull Data Rate Payload "); + fprintf (stderr, "%s", KCYN); + fprintf (stderr, "\nFull Data Header Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } } @@ -497,11 +580,13 @@ void Process1Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t //Full if (opts->payload == 1) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nFull Rate 1 Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } } @@ -579,11 +664,11 @@ void ProcessMBChData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint //and see if its applied the same or differently /* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */ //CRCExtracted = CRCExtracted ^ 0x969696; //does this mask get applied here though for PI? - //CRCExtracted = CRCExtracted ^ 0x6969; + CRCExtracted = CRCExtracted ^ 0xAAAA; /* Check/correct the full link control data and compute the Reed-Solomon (12,9) CRC */ //CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x969696); - //CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x6969); + CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0xAAAA); /* Convert corrected 12 bytes into 96 bits */ for(i = 0, j = 0; i < 12; i++, j+=8) @@ -617,11 +702,13 @@ void ProcessMBChData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint //Full if (opts->payload == 1) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nFull MBC Header Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } } @@ -737,11 +824,13 @@ void ProcessMBCData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8 //Full if (opts->payload == 1) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nFull MBC Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } } @@ -857,11 +946,13 @@ void ProcessWTFData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8 //Full if (opts->payload == 1) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nFull WTF Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } } @@ -881,6 +972,8 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; uint8_t R[3]; uint8_t BPTCReservedBits = 0; + short int slot = 0; + slot = (short int)state->currentslot; /* Check the current time slot */ if(state->currentslot == 0) @@ -909,8 +1002,10 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ k = 0; for(i = 0; i < 12; i++) { - state->dmr_12_rate_sf[i] = state->dmr_12_rate_sf[i+12]; //shift middle 12 to leftmost 12 to load up new round - state->dmr_12_rate_sf[i+12] = state->dmr_12_rate_sf[i+24]; //shift far right 12 to middle 12, going three frames deep; + state->dmr_12_rate_sf[slot][i] = state->dmr_12_rate_sf[slot][i+12]; //shift middle 12 to leftmost 12 to load up new round + state->dmr_12_rate_sf[slot][i+12] = state->dmr_12_rate_sf[slot][i+24]; //shift middle right 12 to middle left 12, going four frames deep; + state->dmr_12_rate_sf[slot][i+24] = state->dmr_12_rate_sf[slot][i+36]; //shift far right 12 to middle right 12, going four frames deep; + state->dmr_12_rate_sf[slot][i+36] = state->dmr_12_rate_sf[slot][i+48]; DmrDataByte[i] = 0; for(j = 0; j < 8; j++) { @@ -918,7 +1013,22 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ DmrDataByte[i] = DmrDataByte[i] | (DmrDataBit[k] & 0x01); k++; } - state->dmr_12_rate_sf[i+24] = DmrDataByte[i]; //copy byte to right hand side of shift frame + state->dmr_12_rate_sf[slot][i+48] = DmrDataByte[i]; //copy byte to right hand side of shift frame + } + + if (opts->payload == 2) + { + fprintf (stderr, "%s",KGRN); + fprintf (stderr, "\n Rate 1/2 Superframe - Slot [%d]\n ",slot+1); + for (i = 0; i < (12*5); i++) + { + fprintf (stderr, "[%02X]", state->dmr_12_rate_sf[slot][i]); + if (i == 11 || i == 23 || i == 35 || i == 47) //line break and two spaces after each 16 bytes + { + fprintf (stderr, "\n "); + } + } + fprintf (stderr, "%s ", KNRM); } /* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */ @@ -944,13 +1054,18 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ //test if((IrrecoverableErrors == 0) && CRCCorrect) { - fprintf (stderr, "\n(1/2 Rate CRC Okay)"); + //fprintf (stderr, "\n(1/2 Rate CRC Okay)"); } else if((IrrecoverableErrors == 0)) { - fprintf (stderr, "\n(1/2 Rate FEC Okay)"); + //fprintf (stderr, "\n(1/2 Rate FEC Okay)"); + } + else + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, ("\n(1/2 Rate CRC Fail, FEC Fail)")); + fprintf (stderr, "%s", KNRM); } - else fprintf (stderr, ("\n(1/2 Rate CRC Fail, FEC Fail)")); /* Convert corrected 12 bytes into 96 bits */ for(i = 0, j = 0; i < 12; i++, j+=8) @@ -964,45 +1079,64 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ DmrDataBit[j + 6] = (DmrDataByte[i] >> 1) & 0x01; DmrDataBit[j + 7] = (DmrDataByte[i] >> 0) & 0x01; } - - //Headers and Addresses - if ( (state->dmr_12_rate_sf[0] & 0x3F) == 0x05) //0x05, or 0x45? + //Headers Source Destination + if ( (state->dmr_12_rate_sf[slot][36] & 0x7F) == 0x45) //Start Source Destination early in case not enough 1/2 data frames { - fprintf (stderr, "\n IP4 Header"); - } - //ARS? - if ( (state->dmr_12_rate_sf[0] & 0x3F) == 0x0C) - { - fprintf (stderr, "\n Source:"); - fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_12_rate_sf[0] & 0x3F), state->dmr_12_rate_sf[1], state->dmr_12_rate_sf[2], state->dmr_12_rate_sf[3]); //strip first two bits off 1st byte - fprintf (stderr, " [%08d]", (state->dmr_12_rate_sf[1] <<16 ) + (state->dmr_12_rate_sf[2] << 8) + state->dmr_12_rate_sf[3] ); - fprintf (stderr, " - Port %05d", (state->dmr_12_rate_sf[8] << 8) + state->dmr_12_rate_sf[9]); - fprintf (stderr, "\n Destination:"); - fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_12_rate_sf[4] & 0x3F), state->dmr_12_rate_sf[5], state->dmr_12_rate_sf[6], state->dmr_12_rate_sf[7]); //strip first two bits off 4th byte?? - fprintf (stderr, " [%08d]", (state->dmr_12_rate_sf[5] <<16 ) + (state->dmr_12_rate_sf[6] << 8) + state->dmr_12_rate_sf[7] ); - fprintf (stderr, " - Port %05d", (state->dmr_12_rate_sf[10] << 8) + state->dmr_12_rate_sf[11]); + fprintf (stderr, "%s ", KMAG); + //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? + //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); + for (short i = 36; i < 60; i++) //find way to get padding so we only go as deep as we need to! + { + if ( state->dmr_12_rate_sf[slot][i] == 0x0C) //Source and Destination info + { + fprintf (stderr, "\n Source:"); + fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_12_rate_sf[slot][i+0] & 0x3F), state->dmr_12_rate_sf[slot][i+1], state->dmr_12_rate_sf[slot][i+2], state->dmr_12_rate_sf[slot][i+3]); //strip first two bits off 1st byte + fprintf (stderr, " [%08d]", (state->dmr_12_rate_sf[slot][i+1] <<16 ) + (state->dmr_12_rate_sf[slot][i+2] << 8) + state->dmr_12_rate_sf[slot][i+3] ); + fprintf (stderr, " - Port %05d", (state->dmr_12_rate_sf[slot][i+8] << 8) + state->dmr_12_rate_sf[slot][i+9]); + fprintf (stderr, "\n Destination:"); + fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_12_rate_sf[slot][i+4] & 0x3F), state->dmr_12_rate_sf[slot][i+5], state->dmr_12_rate_sf[slot][6], state->dmr_12_rate_sf[slot][i+7]); //strip first two bits off 4th byte?? + fprintf (stderr, " [%08d]", (state->dmr_12_rate_sf[slot][i+5] <<16 ) + (state->dmr_12_rate_sf[slot][i+6] << 8) + state->dmr_12_rate_sf[slot][i+7] ); + fprintf (stderr, " - Port %05d", (state->dmr_12_rate_sf[slot][i+10] << 8) + state->dmr_12_rate_sf[slot][i+11]); + } + } + fprintf (stderr, "%s ", KNRM); } //LRRP - if (state->dmr_12_rate_sf[0] == 0x0 && state->dmr_12_rate_sf[4] == 0x0D) //Start LRRP now + if ( (state->dmr_12_rate_sf[slot][0] & 0x7F) == 0x45) //Start LRRP now { - //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_12_rate_sf[5]); - for (short i = 6; i <= 36; i++) + fprintf (stderr, "%s ", KMAG); + //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? + //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); + for (short i = 1; i < 60; i++) //find way to get padding so we only go as deep as we need to! { - if ( state->dmr_12_rate_sf[i] == 0x34 ) //timestamp + /* + if ( state->dmr_12_rate_sf[slot][i] == 0x0C) //Source and Destination info + { + fprintf (stderr, "\n Source:"); + fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_12_rate_sf[slot][i+0] & 0x3F), state->dmr_12_rate_sf[slot][i+1], state->dmr_12_rate_sf[slot][i+2], state->dmr_12_rate_sf[slot][i+3]); //strip first two bits off 1st byte + fprintf (stderr, " [%08d]", (state->dmr_12_rate_sf[slot][i+1] <<16 ) + (state->dmr_12_rate_sf[slot][i+2] << 8) + state->dmr_12_rate_sf[slot][i+3] ); + fprintf (stderr, " - Port %05d", (state->dmr_12_rate_sf[slot][i+8] << 8) + state->dmr_12_rate_sf[slot][i+9]); + fprintf (stderr, "\n Destination:"); + fprintf (stderr, " %03d.%03d.%03d.%03d", (state->dmr_12_rate_sf[slot][i+4] & 0x3F), state->dmr_12_rate_sf[slot][i+5], state->dmr_12_rate_sf[slot][6], state->dmr_12_rate_sf[slot][i+7]); //strip first two bits off 4th byte?? + fprintf (stderr, " [%08d]", (state->dmr_12_rate_sf[slot][i+5] <<16 ) + (state->dmr_12_rate_sf[slot][i+6] << 8) + state->dmr_12_rate_sf[slot][i+7] ); + fprintf (stderr, " - Port %05d", (state->dmr_12_rate_sf[slot][i+10] << 8) + state->dmr_12_rate_sf[slot][i+11]); + } + */ + if ( state->dmr_12_rate_sf[slot][i] == 0x34 ) //timestamp { fprintf (stderr, "\n LRRP - Timestamp: "); - fprintf (stderr, "%4d.", (state->dmr_12_rate_sf[i+1] << 6) + (state->dmr_12_rate_sf[i+2] >> 2) ); //4 digit year - fprintf (stderr, "%02d.", ( ((state->dmr_12_rate_sf[i+2] & 0x3) << 2) + ((state->dmr_12_rate_sf[i+3] & 0xC0) >> 6)) ); //2 digit month - fprintf (stderr, "%02d", ( (state->dmr_12_rate_sf[i+3] & 0x30) >> 1) + ((state->dmr_12_rate_sf[i+3] & 0x0E) >> 1) ); //2 digit day - fprintf (stderr, " %02d:",( (state->dmr_12_rate_sf[i+3] & 0x01) << 4) + ((state->dmr_12_rate_sf[i+4] & 0xF0) >> 4) ); //2 digit hour - fprintf (stderr, "%02d:",( (state->dmr_12_rate_sf[i+4] & 0x0F) << 2) + ((state->dmr_12_rate_sf[i+5] & 0xC0) >> 6) ); //2 digit minute - fprintf (stderr, "%02d", ( (state->dmr_12_rate_sf[i+5] & 0x3F) << 0) ); //2 digit second + fprintf (stderr, "%4d.", (state->dmr_12_rate_sf[slot][i+1] << 6) + (state->dmr_12_rate_sf[slot][i+2] >> 2) ); //4 digit year + fprintf (stderr, "%02d.", ( ((state->dmr_12_rate_sf[slot][i+2] & 0x3) << 2) + ((state->dmr_12_rate_sf[slot][i+3] & 0xC0) >> 6)) ); //2 digit month + fprintf (stderr, "%02d", ( (state->dmr_12_rate_sf[slot][i+3] & 0x30) >> 1) + ((state->dmr_12_rate_sf[slot][i+3] & 0x0E) >> 1) ); //2 digit day + fprintf (stderr, " %02d:",( (state->dmr_12_rate_sf[slot][i+3] & 0x01) << 4) + ((state->dmr_12_rate_sf[slot][i+4] & 0xF0) >> 4) ); //2 digit hour + fprintf (stderr, "%02d:",( (state->dmr_12_rate_sf[slot][i+4] & 0x0F) << 2) + ((state->dmr_12_rate_sf[slot][i+5] & 0xC0) >> 6) ); //2 digit minute + fprintf (stderr, "%02d", ( (state->dmr_12_rate_sf[slot][i+5] & 0x3F) << 0) ); //2 digit second } - if ( state->dmr_12_rate_sf[i] == 0x51 ) //lattitude and longitude + if ( state->dmr_12_rate_sf[slot][i] == 0x51 ) //lattitude and longitude { fprintf (stderr, "\n LRRP -"); fprintf (stderr, " Lat: "); - if (state->dmr_12_rate_sf[i+1] & 0x80) //first bit indicates a sign, or hemisphere? + if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { fprintf (stderr, "-"); } @@ -1010,46 +1144,64 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ long int lrrplon; double lat_unit = (double)180/(double)4294967295; double lon_unit = (double)360/(double)4294967295; - lrrplat = ( ( ((state->dmr_12_rate_sf[i+1] & 0x7F ) << 24 ) + (state->dmr_12_rate_sf[i+2] << 16) + (state->dmr_12_rate_sf[i+3] << 8) + state->dmr_12_rate_sf[i+4]) * 1 ); - lrrplon = ( ( (state->dmr_12_rate_sf[i+5] << 24 ) + (state->dmr_12_rate_sf[i+6] << 16) + (state->dmr_12_rate_sf[i+7] << 8) + state->dmr_12_rate_sf[i+8]) * 1 ); + lrrplat = ( ( ((state->dmr_12_rate_sf[slot][i+1] & 0x7F ) << 24 ) + (state->dmr_12_rate_sf[slot][i+2] << 16) + (state->dmr_12_rate_sf[slot][i+3] << 8) + state->dmr_12_rate_sf[slot][i+4]) * 1 ); + lrrplon = ( ( (state->dmr_12_rate_sf[slot][i+5] << 24 ) + (state->dmr_12_rate_sf[slot][i+6] << 16) + (state->dmr_12_rate_sf[slot][i+7] << 8) + state->dmr_12_rate_sf[slot][i+8]) * 1 ); fprintf (stderr, "%.5lf ", ((double)lrrplat) * lat_unit); fprintf (stderr, " Lon: "); - if (state->dmr_12_rate_sf[i+5] & 0x80) //first bit indicates a sign, or hemisphere? + if (state->dmr_12_rate_sf[slot][i+5] & 0x80) //first bit indicates a sign, or hemisphere? { //fprintf (stderr, "-"); } fprintf (stderr, "%.5lf", (lrrplon * lon_unit) ); - //sprintf ( state->dmr_lrrp[3], "Lat: %.5lf Lon: %.5lf", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + sprintf ( state->dmr_lrrp[3], "Lat: %.5lf Lon: %.5lf", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + //print for easy copy/paste into browser? + fprintf (stderr, " ("); + if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? + { + fprintf (stderr, "-"); + } + fprintf (stderr, "%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); } - if ( state->dmr_12_rate_sf[i] == 0x6C ) + if ( state->dmr_12_rate_sf[slot][i] == 0x6C ) { //either Plus is wrong, or I'm wrong on higher velocities exceeding 0xFF. - //fprintf (stderr, "\n LRRP - Vi %02X Vf %02X Velocity Units (hex)", state->dmr_12_rate_sf[i+1], state->dmr_12_rate_sf[i+2]); - double velocity = ( ((double)( (state->dmr_12_rate_sf[i+1] ) + state->dmr_12_rate_sf[i+2] )) / ( (double)128)); + //fprintf (stderr, "\n LRRP - Vi %02X Vf %02X Velocity Units (hex)", state->dmr_34_rate_sf[i+1], state->dmr_34_rate_sf[i+2]); + double velocity = ( ((double)( (state->dmr_12_rate_sf[slot][i+1] ) + state->dmr_12_rate_sf[slot][i+2] )) / ( (double)128)); //fprintf (stderr, "\n LRRP - %.4lf Meters Per Second", velocity); fprintf (stderr, "\n LRRP - %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity) ); - //sprintf ( state->dmr_lrrp[1], "Vel: %.4lf kph", (3.6 * velocity)); + sprintf ( state->dmr_lrrp[1], "Vel: %.4lf kph", (3.6 * velocity)); } - if ( state->dmr_12_rate_sf[i] == 0x56 ) + if ( state->dmr_12_rate_sf[slot][i] == 0x56 ) { //check for appropriate terminology here - Heading, bearing, course, or track? - fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_12_rate_sf[i+1] * 2); - //sprintf ( state->dmr_lrrp[2], "Dir: %d Deg", state->dmr_12_rate_sf[i+1] * 2); + fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_12_rate_sf[slot][i+1] * 2); + sprintf ( state->dmr_lrrp[2], "Dir: %d Deg", state->dmr_12_rate_sf[slot][i+1] * 2); } } } - + fprintf (stderr, "%s ", KNRM); //Full if (opts->payload == 1) { - fprintf (stderr, "\nFull 1/2 Rate Payload "); + fprintf (stderr,"%s", KCYN); + fprintf (stderr, "\n Full 1/2 Rate Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "\n Hex to Ascii - "); + for (i = 0; i < 12; i++) + { + if (DmrDataByte[i] <= 0x7E && DmrDataByte[i] >=0x20) + { + fprintf (stderr, "%c", DmrDataByte[i]); + } + else fprintf (stderr, "."); + } + fprintf (stderr,"%s", KNRM); } } @@ -1127,22 +1279,27 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t //and see if its applied the same or differently /* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */ //CRCExtracted = CRCExtracted ^ 0x969696; //does this mask get applied here though for PI? - CRCExtracted = CRCExtracted ^ 0x6969; + CRCExtracted = CRCExtracted ^ 0xA5A5; /* Check/correct the full link control data and compute the Reed-Solomon (12,9) CRC */ //CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x969696); - CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x6969); + CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0xA5A5); //test if((IrrecoverableErrors == 0) && CRCCorrect) { - fprintf (stderr, "\n(CSBK CRC Okay)"); + //fprintf (stderr, "\n(CSBK CRC Okay)"); } else if((IrrecoverableErrors == 0)) { - fprintf (stderr, "\n(CSBK FEC Okay)"); + //fprintf (stderr, "\n(CSBK FEC Okay)"); + } + else + { + //fprintf (stderr, "%s", KRED); + //fprintf (stderr, ("\n(CSBK CRC Fail, FEC Fail)")); + //fprintf (stderr, "%s", KNRM); } - else fprintf (stderr, ("\n(CSBK CRC Fail, FEC Fail)")); /* Convert corrected 12 bytes into 96 bits */ for(i = 0, j = 0; i < 12; i++, j+=8) @@ -1170,8 +1327,10 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t uint8_t nb3 = DmrDataByte[4] & 0x3F; //extract(csbk, 34, 40); uint8_t nb4 = DmrDataByte[5] & 0x3F; //extract(csbk, 42, 48); uint8_t nb5 = DmrDataByte[6] & 0x3F; //extract(csbk, 50, 56); + fprintf (stderr, "%s", KMAG); fprintf (stderr, "\nMotoTRBO Connect Plus Neighbors\n"); fprintf(stderr, " NB1(%02x), NB2(%02x), NB3(%02x), NB4(%02x), NB5(%02x)", nb1, nb2, nb3, nb4, nb5); + fprintf (stderr, "%s", KNRM); sprintf(state->dmr_branding, " MotoTRBO Connect Plus "); } //ConnectPLUS Section // @@ -1181,8 +1340,10 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t uint32_t grpAddr = ( (DmrDataByte[5] << 16) + (DmrDataByte[6] << 8) + DmrDataByte[7] ); //extract(csbk, 40, 64); uint8_t lcn = ( (DmrDataByte[8] & 0xF0 ) >> 4 ) ; //extract(csbk, 64, 68); 0xF0 LCN not report same as 'plus'? uint8_t tslot = ( (DmrDataByte[8] & 0x08 ) >> 3 ); //csbk[68]; TS seems fine + fprintf (stderr, "%s", KGRN); fprintf (stderr, "\nMotoTRBO Connect Plus Channel Grant\n"); fprintf(stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot); + fprintf (stderr, "%s", KNRM); sprintf(state->dmr_branding, " MotoTRBO Connect Plus "); } @@ -1192,8 +1353,10 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t uint32_t grpAddr = ( (DmrDataByte[5] << 16) + (DmrDataByte[6] << 8) + DmrDataByte[7] ); //extract(csbk, 40, 64); uint8_t lcn = ( (DmrDataByte[8] & 0xF0 ) >> 4 ) ; //extract(csbk, 64, 68); 0xF0 LCN not report same as 'plus'? uint8_t tslot = ( (DmrDataByte[8] & 0x08 ) >> 3 ); //csbk[68]; TS seems fine + fprintf (stderr, "%s", KRED); fprintf (stderr, "\nMotoTRBO 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); + fprintf (stderr, "%s", KNRM); sprintf(state->dmr_branding, " MotoTRBO Connect Plus "); } @@ -1203,8 +1366,10 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t uint32_t grpAddr = ( (DmrDataByte[5] << 16) + (DmrDataByte[6] << 8) + DmrDataByte[7] ); //extract(csbk, 40, 64); uint8_t lcn = ( (DmrDataByte[8] & 0xF0 ) >> 4 ) ; //extract(csbk, 64, 68); 0xF0 LCN not report same as 'plus'? uint8_t tslot = ( (DmrDataByte[8] & 0x08 ) >> 3 ); //csbk[68]; TS seems fine + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nMotoTRBO Connect Plus Data Channel Grant\n"); //Data I have doesn't show any values for lcn or tslot fprintf(stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot); + fprintf (stderr, "%s", KNRM); sprintf(state->dmr_branding, " MotoTRBO Connect Plus "); } @@ -1292,7 +1457,7 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t //possible Site identifier, CSBK Aloha? if (csbk_o == 0x19) //DmrDataByte[0] == 0x99? { - fprintf (stderr, "\n CSBK Aloha?"); + //fprintf (stderr, "\n CSBK Aloha?"); //fprintf (stderr, "\n Site ID %d-%d.%d", (DmrDataByte[2] & 0xF0 >> 4), DmrDataByte[2] & 0xF, DmrDataByte[3]); //fprintf (stderr, " DCC %d", DmrDataByte[1]); //sprintf ( state->dmr_callsign[0], "Site ID %d-%d.%d", (DmrDataByte[2] & 0xF0 >> 4), DmrDataByte[2] & 0xF, DmrDataByte[3]); @@ -1330,11 +1495,13 @@ void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t //Full if (opts->payload == 1) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nFull CSBK Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } } @@ -1434,19 +1601,37 @@ void ProcessDmrPIHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], u state->payload_mi = ( ((DmrDataByte[3]) << 24) + ((DmrDataByte[4]) << 16) + ((DmrDataByte[5]) << 8) + (DmrDataByte[6]) ); if (1 == 1) //have it always print? { + fprintf (stderr, "%s ", KYEL); fprintf (stderr, "\n 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); } //test if((IrrecoverableErrors == 0) && CRCCorrect) { - fprintf (stderr, " (PI CRC Okay)"); + //fprintf (stderr, " (PI CRC Okay)"); } else if((IrrecoverableErrors == 0)) { - fprintf (stderr, " (PI FEC Okay)"); + //fprintf (stderr, " (PI FEC Okay)"); + } + else { + fprintf (stderr, "%s ", KRED); + fprintf (stderr, (" (PI CRC Fail, FEC Fail)")); + fprintf (stderr, "%s ", KNRM); + } + + //Full + if (opts->payload == 1) + { + fprintf (stderr, "%s", KCYN); + fprintf (stderr, "\nFull PI Header Payload "); + for (i = 0; i < 12; i++) + { + fprintf (stderr, "[%02X]", DmrDataByte[i]); + } + fprintf (stderr, "%s", KNRM); } - else fprintf (stderr, (" (PI CRC Fail, FEC Fail)")); } @@ -1575,6 +1760,7 @@ void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[19 } } /* Print the destination ID (TG) and the source ID */ + fprintf (stderr, "%s ", KGRN); fprintf(stderr, "\n TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; @@ -1591,13 +1777,16 @@ void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[19 * a PI Header will be sent with the encryption mode and DSD will upgrade automatically * the new encryption mode */ opts->EncryptionMode = MODE_BASIC_PRIVACY; - + fprintf (stderr, "%s ", KRED); fprintf(stderr, "Encrypted "); + //fprintf (stderr, "%s ", KNRM); } else { opts->EncryptionMode = MODE_UNENCRYPTED; + fprintf (stderr, "%s ", KGRN); fprintf(stderr, "Clear/Unencrypted "); + //fprintf (stderr, "%s ", KNRM); } /* Check the "Reserved" bits */ @@ -1618,10 +1807,11 @@ void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[19 else fprintf(stderr, "No Priority "); /* We should never go here */ } fprintf(stderr, "Call "); + fprintf (stderr, "%s ", KNRM); - if(TSVoiceSupFrame->FullLC.DataValidity) fprintf(stderr, "(CRC OK ) "); - else if(IrrecoverableErrors == 0) fprintf(stderr, "RAS (FEC OK/CRC ERR)"); - else fprintf(stderr, "(FEC FAIL/CRC ERR)"); + if(TSVoiceSupFrame->FullLC.DataValidity) {}//fprintf(stderr, "(CRC OK ) "); + else if(IrrecoverableErrors == 0) {}//fprintf(stderr, "RAS (FEC OK/CRC ERR)"); + else {}//fprintf(stderr, "(FEC FAIL/CRC ERR)"); #ifdef PRINT_VOICE_LC_HEADER_BYTES fprintf(stderr, "\n"); @@ -1657,12 +1847,15 @@ void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[19 //Full if (opts->payload == 1) { - fprintf (stderr, "\nFull Voice Header Payload "); + fprintf (stderr, "%s", KCYN); + fprintf (stderr, "\nFull Voice LC Header Payload "); for (i = 0; i < 12; i++) { fprintf (stderr, "[%02X]", DmrDataByte[i]); } + fprintf (stderr, "%s", KNRM); } + } /* End ProcessDmrVoiceLcHeader() */ @@ -1797,9 +1990,12 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 //tlc if((IrrecoverableErrors == 0) && CRCCorrect) //amateur DMR seems to only set radio ID up here I think, figure out best way to set without messing up other DMR types { - fprintf(stderr, "\n TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf (stderr, "%s \n", KRED); + fprintf (stderr, " SLOT %d", state->currentslot+1); + fprintf(stderr, " TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); - fprintf(stderr, "(CRC OK ) "); + fprintf (stderr, "%s ", KNRM); + //fprintf(stderr, "(CRC OK ) "); if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode == 0) //other opcodes may convey callsigns, names, etc. /* if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0xAF || @@ -1811,7 +2007,7 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 { state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - state->dmr_color_code = state->color_code; + //state->dmr_color_code = state->color_code; state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; } @@ -1824,9 +2020,12 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 } else if(IrrecoverableErrors == 0) { - fprintf(stderr, "\n TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf (stderr, "%s \n", KRED); + fprintf (stderr, " SLOT %d", state->currentslot+1); + fprintf(stderr, " TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); fprintf(stderr, "RAS (FEC OK/CRC ERR)"); //tlc + fprintf (stderr, "%s ", KNRM); if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode == 0) //other opcodes may convey callsigns, names, etc. /* if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0xAF || @@ -1838,7 +2037,7 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 { state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - state->dmr_color_code = state->color_code; + //state->dmr_color_code = state->color_code; state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; } @@ -1849,7 +2048,7 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; //state->dmr_color_code = state->color_code; } - else fprintf(stderr, "\n(FEC FAIL/CRC ERR)"); + else {} //fprintf(stderr, "\n(FEC FAIL/CRC ERR)"); #ifdef PRINT_TERMINAISON_LC_BYTES fprintf(stderr, "\n"); @@ -1881,6 +2080,17 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 fprintf(stderr, "Hamming Irrecoverable Errors = %u\n", IrrecoverableErrors); #endif /* PRINT_TERMINAISON_LC_BYTES */ + + if (opts->payload == 1) + { + fprintf (stderr, "%s", KCYN); + fprintf (stderr, "\nFull TLC Payload "); + for (i = 0; i < 12; i++) + { + fprintf (stderr, "[%02X]", DmrDataByte[i]); + } + fprintf (stderr, "%s", KNRM); + } } /* End ProcessDmrTerminaisonLC() */ @@ -1993,7 +2203,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) /* Check the CRC values */ if((IrrecoverableErrors == 0))// && CRCCorrect) { - /* Data ara correct */ + /* Data is correct */ //fprintf(stderr, "\nLink Control (LC) Data CRCs are correct !!! Number of error = %u\n", NbOfError); /* CRC is correct so consider the Full LC data as correct/valid */ @@ -2005,87 +2215,83 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) TSVoiceSupFrame->FullLC.DataValidity = 0; } - //GPS, not sure if in right place or not though - if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode == 0x08) - { - long int lrrplat; - long int lrrplon; - double lat_unit = (double)180/(double)4294967295; //damned floats screweing me over lol - double lon_unit = (double)360/(double)4294967295; - //lrrplat = ( ( ((DmrDataByte[0] & 0x3F) << 24 ) + (DmrDataByte[1] << 16) + (DmrDataByte[2] << 8) + DmrDataByte[3]) * 1 ); //(0x0B4/0xFFFFFFFF) - //lrrplon = ( ( (DmrDataByte[4] << 24 ) + (DmrDataByte[5] << 16) + (DmrDataByte[6] << 8) + DmrDataByte[7]) * 1 ); //(0x168/0xFFFFFFFF) //0x3F, or whole thing?; - - //fprintf (stderr, " Standard GPS Coordinates?? Check Payload.\n"); - //fprintf (stderr, "Figure out what to put here"); - - } - //ASCII HEX cheat sheet // 20 - space; 09 - Horizontal Tab; 0A Line Feed; 0D - Carriage Feed; 00 - Null; // 30 - 39 are 0-9 numerals // 41 - 5A are upper case A-Z letters // 61-7A are lower case a-z letters - // + // 7B-7F misc characters (colon, semicolon, etc) // let's assume that something has to indicate end of valid characters? + + //Embedded Alias if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x03 && TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x08 && opts->payload == 1) { - //fprintf (stderr, "Call Sign %X [ ", TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3); sprintf (state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control? for (i = 0; i < 10; i++) { - if ( - //(LC_DataBytes[i] > 0x29 && LC_DataBytes[i] < 0x3A) || //numerals - //(LC_DataBytes[i] > 0x40 && LC_DataBytes[i] < 0x5B) || //uppers - //(LC_DataBytes[i] > 0x60 && LC_DataBytes[i] < 0x7B) || //lowers - //(LC_DataBytes[i] == 0x20) ) //space - (LC_DataBytes[i] > 0x19 && LC_DataBytes[i] < 0x7F) ) //full range of 'normal' characters? + //full range of alphanumerical characters? + if ( (LC_DataBytes[i] > 0x19 && LC_DataBytes[i] < 0x7F) ) { - //fprintf (stderr, "%c", LC_DataBytes[i]); sprintf ( state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3] + strlen(state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3]) , "%c", LC_DataBytes[i]); } } - //fprintf (stderr, "]\n"); if (opts->use_ncurses_terminal == 1) { - fprintf (stderr, "\n Alias Header and Blocks: [%s%s%s%s%s]", state->dmr_callsign[0], state->dmr_callsign[1], state->dmr_callsign[2], state->dmr_callsign[3], state->dmr_callsign[4] ); + fprintf (stderr, "%s", KCYN); + if (state->dmr_stereo == 0) + { + fprintf(stderr, "\n"); + } + fprintf (stderr, " SLOT %d", state->currentslot+1); + fprintf (stderr, " Embedded Alias Header and Blocks: [%s%s%s%s%s]", state->dmr_callsign[0], state->dmr_callsign[1], state->dmr_callsign[2], state->dmr_callsign[3], state->dmr_callsign[4] ); + fprintf (stderr, "%s ", KNRM); + } + } + + if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode == 0x08 && opts->payload == 1) //Embedded GPS + { + sprintf (state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control? + for (i = 0; i < 10; i++) + { + //full range of alphanumerical characters? + if ( (LC_DataBytes[i] > 0x19 && LC_DataBytes[i] < 0x7F) ) + { + sprintf ( state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3] + + strlen(state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3]) , "%c", LC_DataBytes[i]); + } + } + if (opts->use_ncurses_terminal == 1) + { + fprintf (stderr, "%s", KCYN); + if (state->dmr_stereo == 0) + { + fprintf(stderr, "\n"); + } + fprintf (stderr, " SLOT %d", state->currentslot+1); + fprintf (stderr, " Embedded GPS: [%s]", state->dmr_callsign[5] ); + fprintf (stderr, "%s ", KNRM); } } /* Print the destination ID (TG) and the source ID */ - if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x09) //voice burst, was > 0x07 + if((IrrecoverableErrors == 0) && CRCCorrect) { - //fprintf(stderr, " TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); - //fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); - //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; - //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - } - - //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; - //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; - - if((IrrecoverableErrors == 0) && CRCCorrect) //voice burst - { - //fprintf(stderr, " TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); - //fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); - //fprintf(stderr, "(CRC OK ) "); - //only set on good CRC? if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x09) //other opcodes may convey callsigns, names, etc. was > 0x07 - /* - if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0xAF || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x04 || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x05 || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x06 || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x07 ) //work out why amateur sets sourceid on other codes, but pro only does on whatever - */ { - fprintf(stderr, "\n TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf (stderr, "%s", KGRN); + if (state->dmr_stereo == 0) + { + fprintf(stderr, "\n"); + } + fprintf (stderr, " SLOT %d", state->currentslot+1); + fprintf(stderr, " TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); - fprintf(stderr, "(CRC OK ) "); + fprintf (stderr, "%s ", KNRM); + //fprintf(stderr, "(CRC OK ) "); state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - state->dmr_color_code = state->color_code; + //state->dmr_color_code = state->color_code; //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; } @@ -2101,21 +2307,22 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) //fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); //fprintf(stderr, "RAS (FEC OK/CRC ERR)"); //voice burst //or set if was corrected - if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x07) //other opcodes may convey callsigns, names, etc. - /* - if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0xAF || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x04 || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x05 || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x06 || - TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x07 ) //other opcodes may convey callsigns, names, etc. - */ + if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x07) { - fprintf(stderr, "\n TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf (stderr, "%s", KGRN); + if (state->dmr_stereo == 0) + { + fprintf(stderr, "\n"); + } + fprintf (stderr, " SLOT %d", state->currentslot+1); + fprintf(stderr, " TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); + fprintf (stderr, "%s ", KRED); fprintf(stderr, "RAS (FEC OK/CRC ERR) "); + fprintf (stderr, "%s ", KNRM); state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - state->dmr_color_code = state->color_code; + //state->dmr_color_code = state->color_code; //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; } @@ -2125,7 +2332,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; //state->dmr_color_code = state->color_code; } - else fprintf(stderr, "\n(FEC FAIL/CRC ERR)"); + else {} //fprintf(stderr, "\n(FEC FAIL/CRC ERR)"); #ifdef PRINT_VOICE_BURST_BYTES //fprintf(stderr, "\n"); @@ -2159,13 +2366,16 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) //Full if (opts->payload == 1) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "\nFull Voice Burst Payload "); //for (i = 0; i < 8; i++) for (i = 0; i < 10; i++) { fprintf (stderr, "[%02X]", LC_DataBytes[i]); //amateur DMR hams seem to use this area to send callsigns and names using 04,05,06,07 opcodes } - //fprintf (stderr, "\n"); + fprintf (stderr, "%s", KNRM); + + } } /* End ProcessVoiceBurstSync() */ diff --git a/src/dmr_voice.c b/src/dmr_voice.c index a25e9ff..85f38b5 100644 --- a/src/dmr_voice.c +++ b/src/dmr_voice.c @@ -115,6 +115,16 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) state->slot1light[0] = ' '; state->slot1light[6] = ' '; } + //method to only listen to a chosen slot when voices occupy both slots simultaneously + //method supersceeded by DMRSTEREO + //state->hardslot = 9; //0 to only listen to slot 0 voice, 1 for slot 1, 9 for both + //if(state->hardslot != 9 && state->hardslot != state->currentslot) + //if(1==1) + //{ + //fprintf (stderr, " Current Slot = %d", state->currentslot + 1); + //fprintf (stderr, "\n"); //line break after breaking out of jail + //goto JUMP; + //} } } cachdata[12] = 0; @@ -370,10 +380,23 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) sprintf(state->slot2light, "[sLoT2]"); } - if((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) + //Comment out Below Lines, or switch if/else if if these cause issues. + /* + else if((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) { msMode = 1; + if strcmp (sync, DMR_MS_VOICE_SYNC) == 0) + { + //quickDMRVoice (opts, state); //or make handler for msvoice seperate + //goto JUMP; //quit early when returned here + } } + else if((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) + { + //quickDMRVoice (opts, state); //or make handler for msvoice seperate + //goto JUMP; //quit early when returned here + } + */ // TODO : To be removed // if((j == 0) && (opts->errorbars == 1)) @@ -574,8 +597,9 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) fprintf(stderr, "%s ", syncbits); #endif - if(j == 5) + if(j == 5) //j == 5 { + // 2nd half next slot skipDibit (opts, state, 54); @@ -617,6 +641,7 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) } } + } /* End for(j = 0; j < 6; j++) */ if(opts->errorbars == 1) @@ -624,14 +649,25 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) 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 - else fprintf(stderr, "(CRC ERR) |"); //add line break + 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 e: \n"); - fprintf(stderr, " VOICE "); + fprintf(stderr, " VOICE "); } /* Perform the SYNC DMR data embedded decoding */ @@ -642,9 +678,10 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) /* Print DMR frame (if needed) */ //DMRVoiceFrameProcess(opts, state); //HERE HERE, was enabled, disabled so I could build - + //JUMP: if(opts->errorbars == 1) { fprintf(stderr, "\n"); } + } /* End processDMRvoice() */ diff --git a/src/dpmr_voice.c b/src/dpmr_voice.c index 406e05d..7a04856 100644 --- a/src/dpmr_voice.c +++ b/src/dpmr_voice.c @@ -163,10 +163,10 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); - if (opts->inverted_dpmr == 1) - { - dibit = (dibit ^ 2); - } + if (opts->inverted_dpmr == 1) + { + dibit = (dibit ^ 2); + } ambe_fr[j + 4][*w][*x] = (1 & (dibit >> 1)); // bit 1 ambe_fr[j + 4][*y][*z] = (1 & dibit); // bit 0 @@ -467,13 +467,17 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state) //fprintf(stderr, "| TG=%s", CalledID); if(state->dPMRVoiceFS2Frame.CalledIDOk) { + fprintf (stderr, "%s", KGRN); fprintf(stderr, "\n| TG=%s", CalledID); + fprintf (stderr, "%s", KNRM); //strcpy (state->dpmr_target_id, CalledID); //HERE HERE state->dpmr_target_id = CalledID; //fprintf(stderr, " (CRC OK) "); if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1)) { + fprintf (stderr, "%s", KGRN); fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]); + fprintf (stderr, "%s", KNRM); state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0]; } @@ -488,15 +492,19 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state) //fprintf(stderr, "| Src=%s", CallingID); if(state->dPMRVoiceFS2Frame.CallingIDOk) { + fprintf (stderr, "%s", KGRN); fprintf(stderr, "| Src=%s", CallingID); + fprintf (stderr, "%s", KNRM); //strcpy (state->dpmr_caller_id, CallingID); state->dpmr_caller_id = CallingID; - fprintf(stderr, " (CRC OK) \n"); + //fprintf(stderr, " (CRC OK) \n"); } else { + fprintf (stderr, "%s", KRED); fprintf(stderr, " (CRC ERR) \n"); + fprintf (stderr, "%s", KNRM); } #endif /* (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2) */ @@ -583,7 +591,7 @@ void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferO LFSRValue >>= 1; } - /* There are 72 bit to encrypt for voice and 288 bit for data */ + /* There are 72 bit to descramble for voice and 288 bit for data */ for(i = 0; i < NbOfBitToScramble; i++) { BufferOut[i] = (BufferIn[i] ^ S[0]) & 0x01; @@ -613,8 +621,8 @@ void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferO } /* End ScrambleDPmrBit() */ -/* Scrambler used for dPMR encryption / decryption, - * see ETSI TS 102 658 chapter 7.3 for the +/* Scrambler used for dPMR scrambling / descrambling, + * see ETSI TS 102 658 chapter 7.4 for the * polynomial description. * It is a X^9 + X^5 + 1 polynomial. */ void DeInterleave6x12DPmrBit(uint8_t * BufferIn, uint8_t * BufferOut) diff --git a/src/dsd_audio.c b/src/dsd_audio.c index b70e310..136280a 100644 --- a/src/dsd_audio.c +++ b/src/dsd_audio.c @@ -21,8 +21,6 @@ pa_sample_spec ss; pa_sample_spec tt; pa_sample_spec zz; pa_sample_spec cc; -//pa_channel_map channel_map; - void openPulseOutput(dsd_opts * opts) { @@ -34,68 +32,39 @@ void openPulseOutput(dsd_opts * opts) tt.format = PA_SAMPLE_S16NE; tt.channels = opts->pulse_digi_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle - //fprintf (stderr,"digi rate out = %d\n", opts->pulse_digi_rate_out); - //pa_channel_map_init_stereo(&channel_map); - //ss + if (opts->monitor_input_audio == 1) - //if (opts->frame_dmr == 1 && opts->audio_in_type != 3) { - opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, "Raw Audio", &ss, NULL, NULL, NULL); - //opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD FME", PA_STREAM_PLAYBACK, NULL, "DMR/MOTOTRBO Right", &ss, NULL, NULL, NULL); + opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, "Raw Audio Out", &ss, NULL, NULL, NULL); } //tt pa_channel_map* left = 0; //NULL and 0 are same in this context pa_channel_map* right = 0; //NULL and 0 are same in this context - //pa_channel_map* right; - //pa_channel_map_init(right); - opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, opts->output_name, &tt, left, NULL, NULL); - //opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD FME", PA_STREAM_PLAYBACK, NULL, "Digi Audio Out LEFT", &tt, left, NULL, NULL); - //opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, "Digi Audio Out RIGHT", &tt, right, NULL, NULL); + + if (opts->dmr_stereo == 0) + { + opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, opts->output_name, &tt, left, NULL, NULL); + + } + if (opts->dmr_stereo == 1) + { + opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, "DMR/MOTOTRBO LEFT", &tt, left, NULL, NULL); + opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, "DMR/MOTOTRBO RIGHT", &tt, right, NULL, NULL); + } + } void openPulseInput(dsd_opts * opts) { - zz.format = PA_SAMPLE_S16NE; - zz.channels = opts->pulse_raw_in_channels; - zz.rate = opts->pulse_raw_rate_in; //48000 - cc.format = PA_SAMPLE_S16NE; cc.channels = opts->pulse_digi_in_channels; cc.rate = opts->pulse_digi_rate_in; //48000 - //zz - if (opts->monitor_input_audio == 1) - { - //opts->pulse_raw_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, NULL, "Raw Audio In", &zz, NULL, NULL, NULL); - } - //cc opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, NULL, opts->output_name, &cc, NULL, NULL, NULL); } -//playRawAudio may be obsoleted now that I can run these correctly right inside of dsd_symbol -void playRawAudio(dsd_opts * opts, dsd_state * state) -{ - short obuf, outl, i, sample2, something; - something = state->samplesPerSymbol / 1; //1 for buffer steal loops, 5 for rtl loop - //something = 1; - if (opts->audio_in_type == 0 && opts->audio_out_type == 0){ //hack, but might as well for this particular type since its nearly perfect - for (i=0; i < something; i++){ - //read (opts->audio_in_fd, &sample2, 2); //reading here seems to get same speed as gfsk modulation - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); - //pa_simple_read(opts->pulse_raw_dev_in, &state->pulse_raw_out_buffer, 2, NULL ); - //obuf = state->pulse_raw_out_buffer / 4; //dividing here 'quietens' it down a little bit, helps with clicking and clipping - //obuf = state->input_sample_buffer; - //obuf = state->pulse_raw_out_buffer; - //write (opts->audio_out_fd, (void*)&obuf, 2); - //pa_simple_write(opts->pulse_raw_dev_out, (void*)&obuf, 2, NULL); - //pa_simple_write(opts->pulse_digi_dev_out, (void*)&obuf, 2, NULL); - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); - } - } -} //end playRawAudio - void processAudio (dsd_opts * opts, dsd_state * state) @@ -109,7 +78,6 @@ processAudio (dsd_opts * opts, dsd_state * state) // detect max level max = 0; state->audio_out_temp_buf_p = state->audio_out_temp_buf; - //state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; for (n = 0; n < 160; n++) { aout_abs = fabsf (*state->audio_out_temp_buf_p); @@ -118,24 +86,18 @@ processAudio (dsd_opts * opts, dsd_state * state) max = aout_abs; } state->audio_out_temp_buf_p++; - //state->audio_out_temp_buf_pR++; } *state->aout_max_buf_p = max; - //*state->aout_max_buf_pR = max; + state->aout_max_buf_p++; - //state->aout_max_buf_pR++; + state->aout_max_buf_idx++; - //state->aout_max_buf_idxR++; + if (state->aout_max_buf_idx > 24) { state->aout_max_buf_idx = 0; state->aout_max_buf_p = state->aout_max_buf; } - //if (state->aout_max_buf_idxR > 24) - // { - // state->aout_max_buf_idxR = 0; - // state->aout_max_buf_pR = state->aout_max_bufR; - // } // lookup max history for (i = 0; i < 25; i++) @@ -241,6 +203,145 @@ processAudio (dsd_opts * opts, dsd_state * state) state->audio_out_idx2++; } } + +} + +void +processAudioR (dsd_opts * opts, dsd_state * state) +{ + + int i, n; + float aout_abs, max, gainfactor, gaindelta, maxbuf; + if (opts->audio_gainR == (float) 0) + { + // detect max level + max = 0; + state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; + for (n = 0; n < 160; n++) + { + aout_abs = fabsf (*state->audio_out_temp_buf_pR); + if (aout_abs > max) + { + max = aout_abs; + } + state->audio_out_temp_buf_pR++; + } + *state->aout_max_buf_pR = max; + + state->aout_max_buf_pR++; + + state->aout_max_buf_idxR++; + + if (state->aout_max_buf_idxR > 24) + { + state->aout_max_buf_idxR = 0; + state->aout_max_buf_pR = state->aout_max_bufR; + } + + // lookup max history + for (i = 0; i < 25; i++) + { + maxbuf = state->aout_max_bufR[i]; + if (maxbuf > max) + { + max = maxbuf; + } + } + + // determine optimal gain level + if (max > (float) 0) + { + gainfactor = ((float) 30000 / max); + } + else + { + gainfactor = (float) 50; + } + if (gainfactor < state->aout_gainR) + { + state->aout_gainR = gainfactor; + gaindelta = (float) 0; + } + else + { + if (gainfactor > (float) 50) + { + gainfactor = (float) 50; + } + gaindelta = gainfactor - state->aout_gainR; + if (gaindelta > ((float) 0.05 * state->aout_gainR)) + { + gaindelta = ((float) 0.05 * state->aout_gainR); + } + } + gaindelta /= (float) 160; + } + else + { + gaindelta = (float) 0; + } + + if(opts->audio_gainR >= 0) + { + // adjust output gain + state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; + for (n = 0; n < 160; n++) + { + *state->audio_out_temp_buf_pR = (state->aout_gainR + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_pR); + state->audio_out_temp_buf_pR++; + } + state->aout_gainR += ((float) 160 * gaindelta); + } + + // copy audio data to output buffer and upsample if necessary + state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; + if (opts->split == 0) //shouldn't need splitR + { + for (n = 0; n < 160; n++) + { + upsample (state, *state->audio_out_temp_buf_pR); + state->audio_out_temp_buf_pR++; + state->audio_out_float_buf_pR += 6; + state->audio_out_idxR += 6; + state->audio_out_idx2R += 6; + } + state->audio_out_float_buf_pR -= (960 + opts->playoffsetR); + // copy to output (short) buffer + for (n = 0; n < 960; n++) + { + if (*state->audio_out_float_buf_pR > 32767.0F) + { + *state->audio_out_float_buf_pR = 32767.0F; + } + else if (*state->audio_out_float_buf_pR < -32768.0F) + { + *state->audio_out_float_buf_pR = -32768.0F; + } + *state->audio_out_buf_pR = (short) *state->audio_out_float_buf_pR; + state->audio_out_buf_pR++; + state->audio_out_float_buf_pR++; + } + state->audio_out_float_buf_pR += opts->playoffsetR; + } + else + { + for (n = 0; n < 160; n++) + { + if (*state->audio_out_temp_buf_pR > 32767.0F) + { + *state->audio_out_temp_buf_pR = 32767.0F; + } + else if (*state->audio_out_temp_buf_pR < -32768.0F) + { + *state->audio_out_temp_buf_pR = -32768.0F; + } + *state->audio_out_buf_pR = (short) *state->audio_out_temp_buf_pR; + state->audio_out_buf_pR++; + state->audio_out_temp_buf_pR++; + state->audio_out_idxR++; + state->audio_out_idx2R++; + } + } } void @@ -310,183 +411,77 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) ssize_t result; if (state->audio_out_idx > opts->delay) - { - // output synthesized speech to sound card + { + // output synthesized speech to sound card if(opts->audio_out_type == 2) { -#ifdef USE_PORTAUDIO - PaError err = paNoError; - do - { - long available = Pa_GetStreamWriteAvailable( opts->audio_out_pa_stream ); - if(available < 0) - err = available; - //fprintf (stderr,"Frames available: %d\n", available); - if( err != paNoError ) - break; - if(available > SAMPLE_RATE_OUT * PA_LATENCY_OUT) - { - //It looks like this might not be needed for very small latencies. However, it's definitely needed for a bit larger ones. - //When PA_LATENCY_OUT == 0.500 I get output buffer underruns if I don't use this. With PA_LATENCY_OUT <= 0.100 I don't see those happen. - //But with PA_LATENCY_OUT < 0.100 I run the risk of choppy audio and stream errors. - fprintf (stderr,"\nSyncing voice output stream\n"); - err = Pa_StopStream( opts->audio_out_pa_stream ); - if( err != paNoError ) - break; - } - - err = Pa_IsStreamActive( opts->audio_out_pa_stream ); - if(err == 0) - { - fprintf (stderr,"Start voice output stream\n"); - err = Pa_StartStream( opts->audio_out_pa_stream ); - } - else if(err == 1) - { - err = paNoError; - } - if( err != paNoError ) - break; - - err = Pa_WriteStream( opts->audio_out_pa_stream, (state->audio_out_buf_p - state->audio_out_idx), state->audio_out_idx ); - if( err != paNoError ) - break; - } while(0); - - if( err != paNoError ) - { - fprintf (stderr, "An error occured while using the portaudio output stream\n" ); - fprintf (stderr, "Error number: %d\n", err ); - fprintf (stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - } - -#endif + //go F yourself PA } else - if (opts->monitor_input_audio == 1){ - //pa_simple_flush(opts->pulse_raw_dev_in, NULL); - //pa_simple_flush(opts->pulse_raw_dev_out, NULL); - //state->pulse_raw_out_buffer = 0; - //pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL); - - } //two slot audio testing, still need to seperate channels first internally, but this will play them out of different streams - /* - if(state->currentslot == 0 && opts->audio_in_type != 3) + + if(state->currentslot == 0 && opts->audio_in_type != 3 && opts->dmr_stereo == 1) { pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. - } - if(state->currentslot == 1 && opts->audio_in_type != 3) - { - pa_simple_write(opts->pulse_raw_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. - //pa_simple_write(opts->pulse_raw_dev_out, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works. + state->audio_out_idx = 0; } - if (opts->audio_in_type == 3) + //if input type is rtl dongle or no dmr stereo...do I need it this way still? + if (opts->audio_in_type == 3 || opts->dmr_stereo == 0) { pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. + state->audio_out_idx = 0; } - */ - pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. - state->audio_out_idx = 0; } if (state->audio_out_idx2 >= 800000) - { - state->audio_out_float_buf_p = state->audio_out_float_buf + 100; - state->audio_out_buf_p = state->audio_out_buf + 100; - memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); - memset (state->audio_out_buf, 0, 100 * sizeof (short)); - state->audio_out_idx2 = 0; - } + { + state->audio_out_float_buf_p = state->audio_out_float_buf + 100; + state->audio_out_buf_p = state->audio_out_buf + 100; + memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); + memset (state->audio_out_buf, 0, 100 * sizeof (short)); + state->audio_out_idx2 = 0; + } + } -#ifdef USE_PORTAUDIO -int getPADevice(char* dev, int input, PaStream** stream) +void +playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state) { - int devnum = atoi(dev + 3); - fprintf (stderr,"Using portaudio device %d.\n", devnum); + ssize_t result; - PaError err; + if (state->audio_out_idxR > opts->delay) + { + // output synthesized speech to sound card + if(opts->audio_out_type == 2) + { + //go F yourself PA + } + else - int numDevices = Pa_GetDeviceCount(); - if( numDevices < 0 ) - { - fprintf (stderr, "ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices ); - err = numDevices; - goto error; - } - if( devnum >= numDevices) - { - fprintf (stderr, "ERROR: Requested device %d is larger than number of devices.\n", devnum ); - return(1); - } + //two slot audio testing, still need to seperate channels first internally, but this will play them out of different streams + if(opts->dmr_stereo == 1) //state->currentslot == 1 && opts->audio_in_type != 3 && opts->dmr_stereo == 1 + { + pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works. + state->audio_out_idxR = 0; + } + } - const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( devnum ); - - /* print device name */ -#ifdef WIN32 - { /* Use wide char on windows, so we can show UTF-8 encoded device names */ - wchar_t wideName[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH-1); - wprintf ( L"Name = %s\n", wideName ); - } -#else - fprintf (stderr, "Name = %s\n", deviceInfo->name ); -#endif - if((input == 1) && (deviceInfo->maxInputChannels == 0)) - { - fprintf (stderr, "ERROR: Requested device %d is not an input device.\n", devnum ); - return(1); - } - if((input == 0) && (deviceInfo->maxOutputChannels == 0)) - { - fprintf (stderr, "ERROR: Requested device %d is not an output device.\n", devnum ); - return(1); - } - - //Create stream parameters - PaStreamParameters parameters; - parameters.device = devnum; - parameters.channelCount = 1; /* mono */ - parameters.sampleFormat = paInt16; //Shorts - parameters.suggestedLatency = (input == 1) ? PA_LATENCY_IN : PA_LATENCY_OUT; - parameters.hostApiSpecificStreamInfo = NULL; - - //Open stream - err = Pa_OpenStream( - stream, - (input == 1) ? ¶meters : NULL, - (input == 0) ? ¶meters : NULL, - (input == 1) ? SAMPLE_RATE_IN : SAMPLE_RATE_OUT, - PA_FRAMES_PER_BUFFER, - paClipOff, - NULL /*callback*/, - NULL ); - if( err != paNoError ) goto error; - - return 0; - -error: - fprintf (stderr, "An error occured while initializing a portaudio stream\n" ); - fprintf (stderr, "Error number: %d\n", err ); - fprintf (stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - return err; + if (state->audio_out_idx2R >= 800000) + { + state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; + state->audio_out_buf_pR = state->audio_out_bufR + 100; + memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); + memset (state->audio_out_bufR, 0, 100 * sizeof (short)); + state->audio_out_idx2R = 0; + } } -#endif void openAudioOutDevice (dsd_opts * opts, int speed) { - // get info of device/file - /* - if(strncmp(opts->audio_out_dev, "pulse", 5) == 0) - { - opts->audio_out_type == 0; - } - */ if(strncmp(opts->audio_out_dev, "pa:", 3) == 0) { opts->audio_out_type = 2; @@ -518,70 +513,6 @@ openAudioOutDevice (dsd_opts * opts, int speed) fprintf (stderr,"Error, %s is not a device. use -w filename for wav output.\n", opts->audio_out_dev); exit(1); } -/* -#ifdef SOLARIS - sample_info_t aset, aget; - - opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY); - if (opts->audio_out_fd == -1) - { - fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev); - //exit (1); - } - - // get current - ioctl (opts->audio_out_fd, AUDIO_GETINFO, &aset); - aset.record.sample_rate = speed; - aset.play.sample_rate = speed; - aset.record.channels = 1; - aset.play.channels = 1; - aset.record.precision = 16; - aset.play.precision = 16; - aset.record.encoding = AUDIO_ENCODING_LINEAR; - aset.play.encoding = AUDIO_ENCODING_LINEAR; - - if (ioctl (opts->audio_out_fd, AUDIO_SETINFO, &aset) == -1) - { - fprintf (stderr,"Error setting sample device parameters\n"); - exit (1); - } -#endif - -#if defined(BSD) && !defined(__APPLE__) - - int fmt; - - opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY); - if (opts->audio_out_fd == -1) - { - fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev); - opts->audio_out = 0; - //exit(1); - } - - fmt = 0; - if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0) - { - fprintf (stderr,"ioctl reset error \n"); - } - fmt = speed; - if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0) - { - fprintf (stderr,"ioctl speed error \n"); - } - fmt = 0; - if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0) - { - fprintf (stderr,"ioctl stereo error \n"); - } - fmt = AFMT_S16_LE; - if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0) - { - fprintf (stderr,"ioctl setfmt error \n"); - } - -#endif -*/ } fprintf (stderr,"Audio Out Device: %s\n", opts->audio_out_dev); } @@ -661,90 +592,8 @@ openAudioInDevice (dsd_opts * opts) { // this is a device, use old handling, pulse audio now opts->audio_in_type = 0; -/* -#ifdef SOLARIS - sample_info_t aset, aget; - int rgain; - rgain = 64; - - if (opts->split == 1) - { - opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY); - } - else - { - opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR); - } - if (opts->audio_in_fd == -1) - { - fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev); - //exit(1); - } - - // get current - ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset); - - aset.record.sample_rate = SAMPLE_RATE_IN; - aset.play.sample_rate = SAMPLE_RATE_IN; - aset.record.channels = 1; - aset.play.channels = 1; - aset.record.precision = 16; - aset.play.precision = 16; - aset.record.encoding = AUDIO_ENCODING_LINEAR; - aset.play.encoding = AUDIO_ENCODING_LINEAR; - aset.record.port = AUDIO_LINE_IN; - aset.record.gain = rgain; - - if (ioctl (opts->audio_in_fd, AUDIO_SETINFO, &aset) == -1) - { - fprintf (stderr,"Error setting sample device parameters\n"); - exit (1); - } -#endif -*/ -/* -#if defined(BSD) && !defined(__APPLE__) - int fmt; - - if (opts->split == 1) - { - opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY); - } - else - { - opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR); - } - - if (opts->audio_in_fd == -1) - { - fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev); - opts->audio_out = 0; - } - - fmt = 0; - if (ioctl (opts->audio_in_fd, SNDCTL_DSP_RESET) < 0) - { - fprintf (stderr,"ioctl reset error \n"); - } - fmt = SAMPLE_RATE_IN; - if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SPEED, &fmt) < 0) - { - fprintf (stderr,"ioctl speed error \n"); - } - fmt = 0; - if (ioctl (opts->audio_in_fd, SNDCTL_DSP_STEREO, &fmt) < 0) - { - fprintf (stderr,"ioctl stereo error \n"); - } - fmt = AFMT_S16_LE; - if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SETFMT, &fmt) < 0) - { - fprintf (stderr,"ioctl setfmt error \n"); - } -#endif -*/ - } + } } if (opts->split == 1) diff --git a/src/dsd_dibit.c b/src/dsd_dibit.c index 76672c3..1ef3820 100644 --- a/src/dsd_dibit.c +++ b/src/dsd_dibit.c @@ -185,9 +185,16 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol) } if (state->dibit_buf_p > state->dibit_buf + 900000) - { - state->dibit_buf_p = state->dibit_buf + 200; - } + { + state->dibit_buf_p = state->dibit_buf + 200; + } + + //dmr buffer + if (state->dmr_payload_p > state->dmr_payload_buf + 900000) + { + state->dmr_payload_p = state->dmr_payload_buf + 200; + } + //dmr buffer end } static int @@ -211,6 +218,7 @@ invert_dibit(int dibit) } static int digitize (dsd_opts* opts, dsd_state* state, int symbol) +//int digitize (dsd_opts* opts, dsd_state* state, int symbol) { // determine dibit state if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18)) @@ -252,8 +260,10 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) return (0); // +1 } } + else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) || - (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) || (state->synctype == 17)) + (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) || + (state->synctype == 17) ) { // 1 -P25p1 @@ -263,7 +273,6 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) // 11 -DMR (inverted signal voice frame) // 13 -DMR (inverted signal data frame) // 17 -NXDN (inverted data frame) - // int valid; int dibit; @@ -361,64 +370,12 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) } state->last_dibit = dibit; - //test loading up a shift register and printing it to stderr - //di-bit shift register for hex values - //check for which dibit is equal to which half-nibble - if (dibit == 0){state->hexbuf = 0x0;} //+1 - if (dibit == 1){state->hexbuf = 0x1;} //+3 - if (dibit == 2){state->hexbuf = 0x2;} //-1 - if (dibit == 3){state->hexbuf = 0x3;} //-3 - state->sr_0 = (state->sr_0 << 2) | (state->sr_1 >> 62); - state->sr_1 = (state->sr_1 << 2) | (state->sr_2 >> 62); - state->sr_2 = (state->sr_2 << 2) | (state->sr_3 >> 62); - state->sr_3 = (state->sr_3 << 2) | (state->sr_4 >> 62); - state->sr_4 = (state->sr_4 << 2) | (state->sr_5 >> 62); - state->sr_5 = (state->sr_5 << 2) | (state->sr_6 >> 62); - state->sr_6 = state->sr_6 << 2; - state->sr_6 |= state->hexbuf; - if (1 == 2) //disable this later on - { - fprintf (stderr, "\nsr_0 = %16llX \n", state->sr_0); - fprintf (stderr, "sr_1 = %16llX \n", state->sr_1); - fprintf (stderr, "sr_2 = %16llX \n", state->sr_2); - fprintf (stderr, "sr_3 = %16llX \n", state->sr_3); - fprintf (stderr, "sr_4 = %16llX \n", state->sr_4); - fprintf (stderr, "sr_5 = %16llX \n", state->sr_5); - fprintf (stderr, "sr_6 = %16llX \n", state->sr_6); - /* - printw ("sr_0 = %16llX \n", sr_0); - printw ("sr_1 = %16llX \n", sr_1); - printw ("sr_2 = %16llX \n", sr_2); - printw ("sr_3 = %16llX \n", sr_3); - printw ("sr_4 = %16llX \n", sr_4); - printw ("sr_5 = %16llX \n", sr_5); - printw ("sr_6 = %16llX \n", sr_6); - */ - } - - if ( state->sr_5 & 0xFFFFF == 0xDDF5D) //disable this later on - { - fprintf (stderr, "\nsr_0 = %16llX \n", state->sr_0); - fprintf (stderr, "sr_1 = %16llX \n", state->sr_1); - fprintf (stderr, "sr_2 = %16llX \n", state->sr_2); - fprintf (stderr, "sr_3 = %16llX \n", state->sr_3); - fprintf (stderr, "sr_4 = %16llX \n", state->sr_4); - fprintf (stderr, "sr_5 = %16llX \n", state->sr_5); - fprintf (stderr, "sr_6 = %16llX \n", state->sr_6); - /* - printw ("sr_0 = %16llX \n", sr_0); - printw ("sr_1 = %16llX \n", sr_1); - printw ("sr_2 = %16llX \n", sr_2); - printw ("sr_3 = %16llX \n", sr_3); - printw ("sr_4 = %16llX \n", sr_4); - printw ("sr_5 = %16llX \n", sr_5); - printw ("sr_6 = %16llX \n", sr_6); - */ - } - - *state->dibit_buf_p = dibit; state->dibit_buf_p++; + //dmr buffer + *state->dmr_payload_p = dibit; + state->dmr_payload_p++; + //dmr buffer end return dibit; } } @@ -447,9 +404,7 @@ get_dibit_and_analog_signal (dsd_opts* opts, dsd_state* state, int* out_analog_s // state->sbuf[state->sidx] = symbol; - // - //state->pulse_raw_out_buffer = symbol; - // + if (out_analog_signal != NULL) { *out_analog_signal = symbol; diff --git a/src/dsd_file.c b/src/dsd_file.c index 64b96ff..de168b0 100644 --- a/src/dsd_file.c +++ b/src/dsd_file.c @@ -76,7 +76,6 @@ saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d) 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 { b = 0; - for (j = 0; j < 8; j++) { b = b << 1; diff --git a/src/dsd_frame.c b/src/dsd_frame.c index ea83287..0992492 100644 --- a/src/dsd_frame.c +++ b/src/dsd_frame.c @@ -35,14 +35,20 @@ printFrameInfo (dsd_opts * opts, dsd_state * state) } if (state->nac != 0) { + fprintf (stderr, "%s", KCYN); fprintf (stderr,"nac: [%4X] ", state->nac); + fprintf (stderr, "%s", KNRM); } if (opts->verbose > 1) { + fprintf (stderr, "%s", KGRN); fprintf (stderr,"src: [%8i] ", state->lastsrc); + fprintf (stderr, "%s", KNRM); } + fprintf (stderr, "%s", KGRN); fprintf (stderr,"tg: [%5i] ", state->lasttg); + fprintf (stderr, "%s", KNRM); } void @@ -168,8 +174,8 @@ processFrame (dsd_opts * opts, dsd_state * state) processDSTAR_HD (opts, state); return; } - - else if ((state->synctype >= 10) && (state->synctype <= 13)) + //Start DMR Types + else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC { state->nac = 0; state->lastsrc = 0; @@ -182,21 +188,63 @@ processFrame (dsd_opts * opts, dsd_state * state) //fprintf (stderr,"inlvl: %2i%% ", level); } } - if ((state->synctype == 11) || (state->synctype == 12)) + if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes { - if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) + if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL) && opts->dmr_stereo == 0) { openMbeOutFile (opts, state); } sprintf (state->fsubtype, " VOICE "); - processDMRvoice (opts, state); + if (opts->dmr_stereo == 0 && state->synctype < 32) // -T option for DMR (TDMA) stereo + { + processDMRvoice (opts, state); + } + if (opts->dmr_stereo == 0 && state->synctype == 32) + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, "Please use the -T option to handle MS Mode with DMR Stereo TDMA Handling\n"); + fprintf (stderr, "%s", KNRM); + } + if (opts->dmr_stereo == 1) + { + state->dmr_stereo = 1; //set the state to 1 when handling pure voice frames + if (state->synctype > 31 ) + { + dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only) + } + else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap + } } + 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 the -T option to handle MS Mode with DMR Stereo TDMA Handling\n"); + fprintf (stderr, "%s", KNRM); + } + if (opts->dmr_stereo == 1) + { + dmrMSData (opts, state); + } + } else + { + if (opts->dmr_stereo == 0) { closeMbeOutFile (opts, state); state->err_str[0] = 0; processDMRdata (opts, state); } + //switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata + if (opts->dmr_stereo == 1) + { + state->dmr_stereo = 0; //set the state to zero for handling pure data frames + processDMRdata (opts, state); + } + } return; } else if ((state->synctype >= 2) && (state->synctype <= 5)) @@ -244,7 +292,20 @@ processFrame (dsd_opts * opts, dsd_state * state) processProVoice (opts, state); return; } - //LEH dPMR + //ysf + else if ((state->synctype == 30) || (state->synctype == 31)) + { + //Do stuff + //fprintf(stderr, "YSF Sync! \n"); + if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) + { + //openMbeOutFile (opts, state); + } + //sprintf(state->fsubtype, " VOICE "); + processYSF(opts, state); + return; + } + //dPMR else if ((state->synctype == 20) || (state->synctype == 24)) { /* dPMR Frame Sync 1 */ @@ -255,7 +316,6 @@ processFrame (dsd_opts * opts, dsd_state * state) /* dPMR Frame Sync 2 */ fprintf(stderr, "dPMR Frame Sync 2 "); - //state->rf_mod = GFSK_MODE; state->nac = 0; state->lastsrc = 0; state->lasttg = 0; @@ -264,7 +324,6 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - //fprintf(stderr, "inlvl: %2i%% ", level); } } state->nac = 0; @@ -290,7 +349,7 @@ processFrame (dsd_opts * opts, dsd_state * state) fprintf(stderr, "dPMR Frame Sync 4 "); } //dPMR - else + else //P25 { // Read the NAC, 12 bits j = 0; diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index 85e1a80..be29dc5 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -17,9 +17,8 @@ #include "dsd.h" #include -//#include -//borrowed from LEH for 'improved NXDN sync detection' +//borrowed from DSDcc for 'improved NXDN sync detection' int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr) { int Compare = -1; @@ -50,7 +49,6 @@ int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr) return Compare; } /* End strncmperr() */ -//end LEH time_t now; char * getTime(void) //get pretty hh:mm:ss timestamp @@ -140,15 +138,21 @@ getFrameSync (dsd_opts * opts, dsd_state * state) * 26 = -dPMR Frame Sync 3 * 27 = -dPMR Frame Sync 4 * 28 = +NXDN (sync only) - * 29 = -NXDN (sync only + * 29 = -NXDN (sync only) + * 30 = +YSF + * 31 = -YSF + * 32 = DMR MS Voice + * 33 = DMR MS Data + * 34 = DMR RC Data */ int i, j, t, o, dibit, sync, symbol, synctest_pos, lastt; char synctest[25]; - char synctest12[13]; //LEH DPMR + char synctest12[13]; //dPMR char synctest18[19]; char synctest32[33]; + char synctest20[21]; //YSF char modulation[8]; char *synctest_p; char synctest_buf[10240]; @@ -169,6 +173,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) synctest12[12] = 0; synctest18[18] = 0; synctest32[32] = 0; + synctest20[20] = 0; synctest_pos = 0; synctest_p = synctest_buf + 10; sync = 0; @@ -246,9 +251,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } if (state->dibit_buf_p > state->dibit_buf + 900000) - { - state->dibit_buf_p = state->dibit_buf + 200; - } + { + state->dibit_buf_p = state->dibit_buf + 200; + } //determine dibit state if (symbol > 0) @@ -264,6 +269,49 @@ getFrameSync (dsd_opts * opts, dsd_state * state) dibit = 51; // '3' } + //digitize test for storing dibits in buffer correctly for dmr recovery + + if (state->dmr_payload_p > state->dmr_payload_buf + 900000) + { + state->dmr_payload_p = state->dmr_payload_buf + 200; + } + + int valid; + //running estimate_symbol causes an issue with P25 syncing properly + if (opts->frame_p25p1 != 1) + { + valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit); + } + + if (opts->frame_dmr == 1 && valid == 0) //opts->dmr_stereo + { + if (symbol > state->center) + { + if (symbol > state->umid) + { + *state->dmr_payload_p = 1; // +3 + } + else + { + *state->dmr_payload_p = 0; // +1 + } + } + else + { + if (symbol < state->lmid) + { + *state->dmr_payload_p = 3; // -3 + } + else + { + *state->dmr_payload_p = 2; // -1 + } + } + state->dmr_payload_p++; + + } + // end digitize and dmr buffer testing + *synctest_p = dibit; if (t >= 18) { @@ -502,7 +550,53 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } } } + //YSF sync + strncpy(synctest20, (synctest_p - 19), 20); //double check make sure this is right + if(opts->frame_ysf == 1) //(opts->frame_ysf == 1 + { + if (0 == 0) //opts->inverted_ysf == 0 + { + if (strcmp(synctest20, FUSION_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + fprintf (stderr, "\nYSF FUSION SYNC \n"); + opts->inverted_ysf = 0; //should we set this here? + state->lastsynctype = 30; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (30); + } + } + } + if(opts->frame_ysf == 1) //(opts->frame_ysf == 1 + { + if (0 == 0) //opts->inverted_ysf == 1 + { + if (strcmp(synctest20, INV_FUSION_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + fprintf (stderr, "\nINVERTED YSF FUSION SYNC \n"); + opts->inverted_ysf = 1; //should we set this here? + state->lastsynctype = 31; + if ( opts->monitor_input_audio == 1) + { + + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (31); + } + } + } + //end YSF sync //dPMR sync strncpy(synctest, (synctest_p - 23), 24); strncpy(synctest12, (synctest_p - 11), 12); @@ -512,7 +606,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { if(strcmp(synctest, DPMR_FRAME_SYNC_1) == 0) { - fprintf (stderr, "+dPMR FS1\n"); + //fprintf (stderr, "+dPMR FS1\n"); } if(strcmp(synctest12, DPMR_FRAME_SYNC_2) == 0) { @@ -528,22 +622,26 @@ getFrameSync (dsd_opts * opts, dsd_state * state) printFrameSync (opts, state, "+dPMR ", synctest_pos + 1, modulation); } state->lastsynctype = 21; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } return (21); } if(strcmp(synctest12, DPMR_FRAME_SYNC_3) == 0) { - fprintf (stderr, "+dPMR FS3 \n"); + //fprintf (stderr, "+dPMR FS3 \n"); } if(strcmp(synctest, DPMR_FRAME_SYNC_4) == 0) { - fprintf (stderr, "+dPMR FS4 \n"); + //fprintf (stderr, "+dPMR FS4 \n"); } } if (opts->inverted_dpmr == 1) { if(strcmp(synctest, INV_DPMR_FRAME_SYNC_1) == 0) { - fprintf (stderr, "-dPMR FS1 \n"); + //fprintf (stderr, "-dPMR FS1 \n"); } if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_2) == 0) { @@ -560,15 +658,19 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } state->lastsynctype = 25; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } return (25); } if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_3) == 0) { - fprintf (stderr, "-dPMR FS3 \n"); + //fprintf (stderr, "-dPMR FS3 \n"); } if(strcmp(synctest, INV_DPMR_FRAME_SYNC_4) == 0) { - fprintf (stderr, "-dPMR FS4 \n"); + //fprintf (stderr, "-dPMR FS4 \n"); } } } @@ -576,7 +678,97 @@ getFrameSync (dsd_opts * opts, dsd_state * state) //New DMR Sync if (opts->frame_dmr == 1) { - if ((strcmp (synctest, DMR_MS_DATA_SYNC) == 0) || (strcmp (synctest, DMR_BS_DATA_SYNC) == 0)) + + if(strcmp (synctest, DMR_MS_DATA_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + //state->directmode = 0; + //fprintf (stderr, "DMR MS Data"); + if (0 == 0) //opts->inverted_dmr + { + // data frame + sprintf(state->ftype, "DMR MS"); + if (opts->errorbars == 1) + { + //printFrameSync (opts, state, "+DMR MS Data", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 33) //33 + { + //state->firstframe = 1; + } + state->lastsynctype = 33; //33 + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (33); //33 + } + } + //not sure if this should be here, RC data should only be present in vc6? + if(strcmp (synctest, DMR_RC_DATA_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + //state->directmode = 0; + //fprintf (stderr, "DMR RC DATA\n"); + state->dmr_ms_rc == 1; //set flag for RC data, then process accordingly and reset back to 0 afterwards + if (0 == 0) //opts->inverted_dmr + { + // voice frame + sprintf(state->ftype, "DMR RC"); + if (opts->errorbars == 1) + { + //printFrameSync (opts, state, "+DMR RC Data", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 34) + { + //state->firstframe = 1; + } + state->lastsynctype = 34; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (34); + } + } + + if(strcmp (synctest, DMR_MS_VOICE_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + //state->directmode = 0; + //fprintf (stderr, "DMR MS VOICE\n"); + if (0 == 0) //opts->inverted_dmr + { + // voice frame + sprintf(state->ftype, "DMR MS"); + if (opts->errorbars == 1) + { + //printFrameSync (opts, state, "+DMR MS Voice", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 32) + { + //state->firstframe = 1; + } + state->lastsynctype = 32; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (32); + } + } + + //if ((strcmp (synctest, DMR_MS_DATA_SYNC) == 0) || (strcmp (synctest, DMR_BS_DATA_SYNC) == 0)) + if (strcmp (synctest, DMR_BS_DATA_SYNC) == 0) { state->carrier = 1; state->offset = synctest_pos; @@ -594,7 +786,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 10; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (10); @@ -614,7 +805,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 11; if ( opts->monitor_input_audio == 1) { - pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (11); } @@ -638,7 +829,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 10; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (10); @@ -649,7 +839,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf(state->ftype, "DMR "); if (opts->errorbars == 1) { - printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); + //printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); } if (state->lastsynctype != 11) { @@ -658,7 +848,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 11; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (11); @@ -678,12 +867,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf(state->ftype, "DMR "); if (opts->errorbars == 1) { - printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation); + //printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation); } state->lastsynctype = 10; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (10); @@ -694,7 +882,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf(state->ftype, "DMR "); if (opts->errorbars == 1) { - printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); + //printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); } if (state->lastsynctype != 11) { @@ -703,13 +891,14 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 11; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (11); } } /* End if(strcmp (synctest, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0) */ - if((strcmp (synctest, DMR_MS_VOICE_SYNC) == 0) || (strcmp (synctest, DMR_BS_VOICE_SYNC) == 0)) + //if((strcmp (synctest, DMR_MS_VOICE_SYNC) == 0) || (strcmp (synctest, DMR_BS_VOICE_SYNC) == 0)) + if(strcmp (synctest, DMR_BS_VOICE_SYNC) == 0) { state->carrier = 1; state->offset = synctest_pos; @@ -729,8 +918,13 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->firstframe = 1; } state->lastsynctype = 12; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } return (12); } + else { // inverted data frame @@ -742,7 +936,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 13; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (13); @@ -771,7 +964,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 12; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (12); @@ -811,7 +1003,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 12; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (12); @@ -827,7 +1018,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 13; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (13); @@ -835,7 +1026,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } //End if(strcmp (synctest, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) } //End if (opts->frame_dmr == 1) - //end LEH DMR Sync + //end DMR Sync if (opts->frame_provoice == 1) { strncpy (synctest32, (synctest_p - 31), 32); @@ -855,7 +1046,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 14; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (14); @@ -875,7 +1066,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 15; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (15); @@ -885,8 +1076,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) { strncpy (synctest18, (synctest_p - 17), 18); - //if ((strcmp (synctest18, NXDN_BS_VOICE_SYNC) == 0) || (strcmp (synctest18, NXDN_MS_VOICE_SYNC) == 0)) //or is this the double up test? - //borrowing from LEH here WOW! This works so much better, its not even a joke if ((strncmperr (synctest18, NXDN_BS_VOICE_SYNC, 18, 1) == 0) || (strncmperr (synctest18, NXDN_MS_VOICE_SYNC, 18, 1) == 0)) { now = time(NULL); @@ -903,7 +1092,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf (state->ftype, "NXDN48 "); //get rid of spaces if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } if (opts->errorbars == 1) @@ -917,7 +1106,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf (state->ftype, "NXDN96"); if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } if (opts->errorbars == 1) @@ -967,7 +1156,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 9; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (9); @@ -1009,7 +1198,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 16; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (16); @@ -1052,7 +1241,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 17; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (17); @@ -1082,7 +1271,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 6; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (6); @@ -1102,7 +1291,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 7; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (7); @@ -1122,7 +1311,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 18; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (18); @@ -1142,7 +1331,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 19; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); + pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (19); @@ -1212,7 +1401,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state) return (4); } - else if ((state->lastsynctype == 11) && ((strcmp (synctest, DMR_BS_VOICE_SYNC) != 0) || (strcmp (synctest, DMR_MS_VOICE_SYNC) != 0))) + + //dmr desync handling, causes issues with TDMA stereo (MS especially), consider removing + //may have been because we had BS Voice twice on here, seems okay, continue testing + /* + else if ((state->lastsynctype == 11) && ((strcmp (synctest, DMR_BS_VOICE_SYNC) != 0) )) { state->carrier = 1; state->offset = synctest_pos; @@ -1221,17 +1414,18 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf (state->ftype, "(DMR) "); if (opts->errorbars == 1) { - printFrameSync (opts, state, "(-DMR) ", synctest_pos + 1, modulation); + //printFrameSync (opts, state, "(-DMR) ", synctest_pos + 1, modulation); } state->lastsynctype = -1; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (11); } - else if ((state->lastsynctype == 12) && ((strcmp (synctest, DMR_BS_DATA_SYNC) != 0) || (strcmp (synctest, DMR_MS_DATA_SYNC) != 0))) + */ + + else if ( (state->lastsynctype == 32) && (strcmp (synctest, DMR_MS_VOICE_SYNC) != 0) ) { state->carrier = 1; state->offset = synctest_pos; @@ -1240,18 +1434,76 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf (state->ftype, "(DMR) "); if (opts->errorbars == 1) { - printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation); + //printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation); } state->lastsynctype = -1; if ( opts->monitor_input_audio == 1) { - //pa_simple_drain(opts->pulse_raw_dev_out, NULL); pa_simple_flush(opts->pulse_raw_dev_out, NULL); } - return (12); + return (32); //32 } - } - } + + else if ( (state->lastsynctype == 33) && (strcmp (synctest, DMR_MS_DATA_SYNC) != 0) ) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf (state->ftype, "(DMR) "); + if (opts->errorbars == 1) + { + //printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation); + } + state->lastsynctype = -1; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (33); //33 + } + else if ( (state->lastsynctype == 34) && (strcmp (synctest, DMR_RC_DATA_SYNC) != 0) ) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf (state->ftype, "(DMR) "); + if (opts->errorbars == 1) + { + //printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation); + } + state->lastsynctype = -1; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (34); //34 + } + + else if ((state->lastsynctype == 11) && ((strcmp (synctest, DMR_BS_VOICE_SYNC) != 0) )) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf (state->ftype, "(DMR) "); + if (opts->errorbars == 1) + { + //printFrameSync (opts, state, "(-DMR) ", synctest_pos + 1, modulation); + } + state->lastsynctype = -1; + if ( opts->monitor_input_audio == 1) + { + pa_simple_flush(opts->pulse_raw_dev_out, NULL); + } + return (11); //11 + } + + //DMR desync + + } //if t == 24 + } // t >= 18 if (exitflag == 1) { @@ -1294,12 +1546,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state) return (-1); } } - if ( opts->monitor_input_audio == 1 && (time(NULL) - now) > 1 ) //okay, still something going on, latency issues - //if ( opts->monitor_input_audio == 1) - { - //playRawAudio(opts, state); //this is on line 21 in dsd_audio.c - } + } return (-1); } +//1111 diff --git a/src/dsd_main.c b/src/dsd_main.c index 9fa5f92..361ba6d 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -26,17 +26,6 @@ #include "provoice_const.h" #include "git_ver.h" -//pretty pretty colors - -#define KNRM "\x1B[0m" -#define KRED "\x1B[31m" -#define KGRN "\x1B[32m" -#define KYEL "\x1B[33m" -#define KBLU "\x1B[34m" -#define KMAG "\x1B[35m" -#define KCYN "\x1B[36m" -#define KWHT "\x1B[37m" - int pretty_colors() { fprintf (stderr,"%sred\n", KRED); @@ -84,6 +73,10 @@ noCarrier (dsd_opts * opts, dsd_state * state) { state->dibit_buf_p = state->dibit_buf + 200; memset (state->dibit_buf, 0, sizeof (int) * 200); + //dmr buffer + state->dmr_payload_p = state->dibit_buf + 200; + memset (state->dmr_payload_buf, 0, sizeof (int) * 200); + //dmr buffer end if (opts->mbe_out_f != NULL) { closeMbeOutFile (opts, state); @@ -93,7 +86,7 @@ noCarrier (dsd_opts * opts, dsd_state * state) state->carrier = 0; state->max = 15000; state->min = -15000; - state->center = 0; + state->center = 0; //quick disable for dmr testing with buffer state->err_str[0] = 0; state->err_strR[0] = 0; sprintf (state->fsubtype, " "); @@ -110,15 +103,26 @@ noCarrier (dsd_opts * opts, dsd_state * state) sprintf (state->slot1light, " slot1 "); state->firstframe = 0; if (opts->audio_gain == (float) 0) - { - state->aout_gain = 25; - } + { + state->aout_gain = 25; + } + + if (opts->audio_gainR == (float) 0) + { + state->aout_gainR = 25; + } memset (state->aout_max_buf, 0, sizeof (float) * 200); state->aout_max_buf_p = state->aout_max_buf; state->aout_max_buf_idx = 0; + + memset (state->aout_max_bufR, 0, sizeof (float) * 200); + state->aout_max_buf_pR = state->aout_max_bufR; + state->aout_max_buf_idxR = 0; + sprintf (state->algid, "________"); sprintf (state->keyid, "________________"); mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced); + mbe_initMbeParms (state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2); } void @@ -151,11 +155,13 @@ initOpts (dsd_opts * opts) #endif opts->split = 0; opts->playoffset = 0; + opts->playoffsetR = 0; opts->mbe_out_dir[0] = 0; opts->mbe_out_file[0] = 0; opts->mbe_out_path[0] = 0; opts->mbe_out_f = NULL; opts->audio_gain = 0; + opts->audio_gainR = 0; opts->audio_out = 1; opts->wav_out_file[0] = 0; opts->wav_out_f = NULL; @@ -163,7 +169,7 @@ initOpts (dsd_opts * opts) opts->serial_baud = 115200; sprintf (opts->serial_dev, "/dev/ttyUSB0"); opts->resume = 0; - opts->frame_dstar = 1; + opts->frame_dstar = 1; opts->frame_x2tdma = 1; opts->frame_p25p1 = 1; opts->frame_nxdn48 = 0; @@ -200,13 +206,14 @@ initOpts (dsd_opts * opts) opts->pulse_digi_in_channels = 1; //2 opts->pulse_digi_out_channels = 1; //2 - sprintf (opts->output_name, "Default Channel"); + sprintf (opts->output_name, "Auto Detect"); opts->pulse_flush = 1; //set 0 to flush, 1 for flushed opts->use_ncurses_terminal = 0; opts->payload = 0; opts->EncryptionMode = MODE_UNENCRYPTED; opts->inverted_dpmr = 0; + opts->dmr_stereo = 0; //change currently only in dsd_frame.c } void @@ -218,13 +225,18 @@ initState (dsd_state * state) state->dibit_buf = malloc (sizeof (int) * 1000000); state->dibit_buf_p = state->dibit_buf + 200; memset (state->dibit_buf, 0, sizeof (int) * 200); + //dmr buffer -- double check this set up + state->dmr_payload_buf = malloc (sizeof (int) * 1000000); + state->dmr_payload_p = state->dmr_payload_buf + 200; + memset (state->dmr_payload_buf, 0, sizeof (int) * 200); + //dmr buffer end state->repeat = 0; state->audio_out_buf = malloc (sizeof (short) * 1000000); state->audio_out_bufR = malloc (sizeof (short) * 1000000); memset (state->audio_out_buf, 0, 100 * sizeof (short)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_buf_p = state->audio_out_buf + 100; - state->audio_out_buf_pR = state->audio_out_buf + 100; + state->audio_out_buf_pR = state->audio_out_bufR + 100; state->audio_out_float_buf = malloc (sizeof (float) * 1000000); state->audio_out_float_bufR = malloc (sizeof (float) * 1000000); memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); @@ -296,6 +308,11 @@ initState (dsd_state * state) memset (state->aout_max_buf, 0, sizeof (float) * 200); state->aout_max_buf_p = state->aout_max_buf; state->aout_max_buf_idx = 0; + + memset (state->aout_max_bufR, 0, sizeof (float) * 200); + state->aout_max_buf_pR = state->aout_max_bufR; + state->aout_max_buf_idxR = 0; + state->samplesPerSymbol = 10; state->symbolCenter = 4; sprintf (state->algid, "________"); @@ -304,10 +321,17 @@ initState (dsd_state * state) state->cur_mp = malloc (sizeof (mbe_parms)); state->prev_mp = malloc (sizeof (mbe_parms)); state->prev_mp_enhanced = malloc (sizeof (mbe_parms)); + + state->cur_mp2 = malloc (sizeof (mbe_parms)); + state->prev_mp2 = malloc (sizeof (mbe_parms)); + state->prev_mp_enhanced2 = malloc (sizeof (mbe_parms)); + mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced); + mbe_initMbeParms (state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2); state->p25kid = 0; state->debug_audio_errors = 0; + state->debug_audio_errorsR = 0; state->debug_header_errors = 0; state->debug_header_critical_errors = 0; @@ -340,6 +364,7 @@ initState (dsd_state * state) sprintf (state->dmr_lrrp[5], ""); state->K = 0; + state->dmr_stereo = 0; #ifdef TRACE_DSD state->debug_sample_index = 0; @@ -380,7 +405,7 @@ usage () fprintf (stderr," -g Audio output gain (default = 0 = auto, disable = -1)\n"); fprintf (stderr," -w Output synthesized speech to a .wav file\n"); //fprintf (stderr," -a Display port audio devices\n"); - //fprintf (stderr," -W Monitor Source Audio When No Sync Detected (WIP!)\n"); + fprintf (stderr," -W Monitor Source Audio When No Sync Detected (WIP!)\n"); fprintf (stderr,"\n"); fprintf (stderr,"RTL-SDR options:\n"); fprintf (stderr," -c RTL-SDR Frequency\n"); @@ -429,6 +454,9 @@ usage () fprintf (stderr," -n Reset P25 Heuristics and initState variables on mixed signal decoding\n"); fprintf (stderr," Helps when decoding mixed signal types at same time\n"); fprintf (stderr," (WiP! May Cause Slow Memory Leak - Experimental)\n"); + fprintf (stderr," -T Enable DMR TDMA Stereo Voice (Two Slot Dual Voices)\n"); + fprintf (stderr," This feature will open two streams for slot 1 voice and slot 2 voice\n"); + fprintf (stderr," (WiP! May Cause DMR sync break resync issues)\n"); fprintf (stderr," -Z Log MBE Payload to console\n"); fprintf (stderr,"\n"); fprintf (stderr,"Report bugs to: https://github.com/lwvmobile/dsd-fme/issues \n"); @@ -674,7 +702,7 @@ main (int argc, char **argv) exitflag = 0; signal (SIGINT, sigfun); - while ((c = getopt (argc, argv, "haep:P:qstv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:Y:K:NWrlZ")) != -1) + while ((c = getopt (argc, argv, "haep:P:qstv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:Y:K:NWrlZT")) != -1) { opterr = 0; switch (c) @@ -771,8 +799,10 @@ main (int argc, char **argv) case 'W': //monitor_input_audio if no sync opts.monitor_input_audio = 1; + fprintf (stderr, "%s", KRED); fprintf (stderr,"Monitoring Source Audio when carrier present and No Sync Detected.\n"); fprintf (stderr," - May cause Pulse Audio Server to crash on some systems.\n"); + fprintf (stderr, "%s", KNRM); break; case 'N': @@ -786,6 +816,17 @@ main (int argc, char **argv) fprintf (stderr,"Logging MBE Payload to console\n"); break; + case 'T': + opts.dmr_stereo = 1; //just the end user option + state.dmr_stereo = 1; //this values toggles on and off depending on voice or data handling + opts.pulse_digi_rate_out = 24000; + opts.pulse_digi_out_channels = 2; + fprintf (stderr, "%s", KRED); + fprintf (stderr,"Experimental DMR Stereo Sync and Functionality. WIP!\n"); + fprintf (stderr,"DMR Stereo will disable WAV and MBE file saving!\n"); + fprintf (stderr, "%s", KNRM); + break; + case 'z': sscanf (optarg, "%d", &opts.scoperate); opts.errorbars = 0; @@ -830,13 +871,16 @@ main (int argc, char **argv) { fprintf (stderr,"Setting audio out gain to %f\n", opts.audio_gain); state.aout_gain = opts.audio_gain; + state.aout_gainR = opts.audio_gain; } break; case 'n': opts.reset_state = 1; + fprintf (stderr, "%s", KRED); fprintf (stderr,"Enabling Automatic Reset of P25 states\n"); fprintf (stderr," -Helps with multiple signal type decoding\n"); fprintf (stderr," -(WiP! May cause slow memory leak)\n"); + fprintf (stderr, "%s", KNRM); break; case 'w': strncpy(opts.wav_out_file, optarg, 1023); @@ -866,7 +910,7 @@ main (int argc, char **argv) opts.frame_dmr = 1; opts.frame_dpmr = 0; opts.frame_provoice = 0; - sprintf (opts.output_name, "Default Channel"); + sprintf (opts.output_name, "Auto Detect"); } else if (optarg[0] == 'd') { @@ -889,7 +933,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; - opts.frame_dpmr = 0; //borrow from LEH + opts.frame_dpmr = 0; opts.frame_provoice = 0; sprintf (opts.output_name, "X2-TDMA"); fprintf (stderr,"Decoding only X2-TDMA frames.\n"); @@ -902,7 +946,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; - opts.frame_dpmr = 0; //borrow from LEH + opts.frame_dpmr = 0; opts.frame_provoice = 1; state.samplesPerSymbol = 5; state.symbolCenter = 2; @@ -911,8 +955,7 @@ main (int argc, char **argv) opts.mod_gfsk = 1; state.rf_mod = 2; sprintf (opts.output_name, "ProVoice"); - //fprintf (stderr,"Setting symbol rate to 9600 / second\n"); - //fprintf (stderr,"Enabling only GFSK modulation optimizations.\n"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); fprintf (stderr,"Decoding only ProVoice frames.\n"); } else if (optarg[0] == '1') @@ -942,6 +985,7 @@ main (int argc, char **argv) opts.frame_dmr = 0; opts.frame_dpmr = 0; opts.frame_provoice = 0; + opts.frame_ysf = 0; state.samplesPerSymbol = 20; state.symbolCenter = 10; opts.mod_c4fm = 1; @@ -951,9 +995,30 @@ main (int argc, char **argv) sprintf (opts.output_name, "NXDN48"); //opts.symboltiming = 2400; //NXDN48 uses 2400 symbol rate fprintf (stderr,"Setting symbol rate to 2400 / second\n"); - //fprintf (stderr,"Enabling only C4FM modulation optimizations.\n"); fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n"); } + else if (optarg[0] == 'y') + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 0; + opts.frame_ysf = 1; + state.samplesPerSymbol = 10; + //state.symbolCenter = 10; + opts.mod_c4fm = 1; + opts.mod_qpsk = 0; + opts.mod_gfsk = 0; + state.rf_mod = 0; + sprintf (opts.output_name, "YSF"); + //opts.symboltiming = 2400; //NXDN48 uses 2400 symbol rate + fprintf (stderr,"Setting symbol rate to 2400 / second\n"); + fprintf (stderr,"Decoding only YSF frames.\nNot working yet!\n"); + } else if (optarg[0] == 'n') { opts.frame_dstar = 0; @@ -969,7 +1034,6 @@ main (int argc, char **argv) opts.mod_gfsk = 0; state.rf_mod = 0; sprintf (opts.output_name, "NXDN96"); - //fprintf (stderr,"Enabling only C4FM modulation optimizations.\n"); fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n"); } else if (optarg[0] == 'r') @@ -986,13 +1050,8 @@ main (int argc, char **argv) opts.mod_qpsk = 0; opts.mod_gfsk = 0; // state.rf_mod = 0; // - //opts.pulse_raw_rate_out = 24000; //doing tests with 2 channels at 24000 for 48000 audio default in pulse - //opts.pulse_digi_rate_out = 24000; //need to copy this to rtl type in and change rate out to 8000 - //opts.pulse_raw_out_channels = 2; - //opts.pulse_digi_out_channels = 2; //2 sprintf (opts.output_name, "DMR/MOTOTRBO"); - //sprintf (opts.output_name, "DMR/MOTOTRBO Left"); - fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n"); + //fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n"); fprintf (stderr,"Decoding only DMR/MOTOTRBO frames.\n"); } else if (optarg[0] == 'm') @@ -1005,6 +1064,7 @@ main (int argc, char **argv) opts.frame_dmr = 0; opts.frame_provoice = 0; opts.frame_dpmr = 1; + opts.frame_ysf = 0; //testing with NXDN48 parameters state.samplesPerSymbol = 20; //same as NXDN48 - 20 state.symbolCenter = 10; //same as NXDN48 - 10 opts.mod_c4fm = 1; @@ -1013,11 +1073,10 @@ main (int argc, char **argv) state.rf_mod = 0; sprintf (opts.output_name, "dPMR"); fprintf(stderr, "Notice: dPMR cannot autodetect polarity. \n Use -xd option if Inverted Signal expected.\n"); - //fprintf(stderr, "Enabling only C4FM modulation optimizations.\n"); fprintf(stderr, "Decoding only dPMR frames.\n"); } break; - //don't mess with the modulations, doesn't really do anything anyways + //don't mess with the modulations, doesn't really do anything meaningful anyways case 'm': if (optarg[0] == 'a') { @@ -1127,7 +1186,6 @@ main (int argc, char **argv) if (opts.use_ncurses_terminal == 1) { ncursesOpen(); - //ncursesPrinter(&opts, &state); } if (opts.resume > 0) @@ -1165,6 +1223,7 @@ main (int argc, char **argv) { opts.split = 1; opts.playoffset = 0; + opts.playoffsetR = 0; opts.delay = 0; if (strlen(opts.wav_out_file) > 0) { @@ -1179,6 +1238,7 @@ main (int argc, char **argv) { opts.split = 1; opts.playoffset = 0; + opts.playoffsetR = 0; opts.delay = 0; if (strlen(opts.wav_out_file) > 0) { @@ -1197,7 +1257,8 @@ main (int argc, char **argv) else { opts.split = 0; - opts.playoffset = 25; // 38 + opts.playoffset = 25; // 38 + opts.playoffsetR = 25; // 38 opts.delay = 0; openAudioInDevice (&opts); //disabled all four instances of openAudio devices, in and out for now opts.audio_out_fd = opts.audio_in_fd; @@ -1214,8 +1275,8 @@ main (int argc, char **argv) else { liveScanner (&opts, &state); - } + if (opts.use_ncurses_terminal == 1){ //ncursesClose (); } diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index bfc5883..0c64119 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -126,44 +126,78 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } else { - 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); -#ifdef AMBE_PACKET_OUT - - for(i=0; i<49; i++) { - ambe_d_str[i] = ambe_d[i] + '0'; + //stereo slots and slot 0 (left slot) + if (state->currentslot == 0 && opts->dmr_stereo == 1) + { + 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); } - ambe_d_str[49] = '\0'; - // print binary string - fprintf (stderr, "\n?\t?\t%s\t", ambe_d_str); - // print error data - fprintf (stderr, "E1: %d; E2: %d; S: %s", state->errs, state->errs2, state->err_str);} -#endif - if (opts->mbe_out_f != NULL) + //stereo slots and slot 1 (right slot) + if (state->currentslot == 1 && opts->dmr_stereo == 1) + { + mbe_processAmbe3600x2450Framef (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR, ambe_fr, ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality); + //just put this in here for troubleshooting for now + if (opts->mbe_out_f != NULL) { 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->mbe_out_f != NULL) + { + saveAmbe2450Data (opts, state, ambe_d); + } + } + + } if (opts->errorbars == 1) { //state->err_buf = state->err_str; //make comy to compare, and only print when comparison differs?? THIS HERE HERE //strncpy (state->err_buf, state->err_str, sizeof(state->err_str)); //is this the correct placement for this? want it just before err_str is set? - fprintf (stderr, "%s", state->err_str); //this the actual error 'bar' ==== printer, find way to keep this entire string from printing constantly unless err_str changes + //fprintf (stderr, "%s", state->err_str); //this the actual error 'bar' ==== printer, find way to keep this entire string from printing constantly unless err_str changes //fprintf (stderr, "%s", state->err_buf); } state->debug_audio_errors += state->errs2; - - processAudio (opts, state); - if (opts->wav_out_f != NULL) - { - writeSynthesizedVoice (opts, state); - } - - if (opts->audio_out == 1) + state->debug_audio_errorsR += state->errs2R; + if (opts->dmr_stereo == 1 && state->currentslot == 0) + { + processAudio (opts, state); + if (opts->audio_out == 1) { playSynthesizedVoice (opts, state); } + } + if (opts->dmr_stereo == 1 && state->currentslot == 1) + { + processAudioR (opts, state); + if (opts->audio_out == 1) + { + playSynthesizedVoiceR (opts, state); + } + } + if (opts->dmr_stereo == 0) + { + processAudio (opts, state); + if (opts->audio_out == 1) + { + playSynthesizedVoice (opts, state); + } + } + if (opts->wav_out_f != NULL && opts->dmr_stereo == 0) + { + writeSynthesizedVoice (opts, state); + } + /* + if (opts->audio_out == 1) + { + playSynthesizedVoice (opts, state); + } + */ } /* This function decipher data */ diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 0325817..8de1877 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -42,6 +42,7 @@ int i = 0; char * s0last = " "; char * s1last = " "; char versionstr[25]; +char lrrp[6][9999]; //local lrrp storage unsigned long long int call_matrix[33][6]; //0 - sync type; 1 - tg/ran; 2 - rid; 3 - slot; 4 - cc; 5 - time(NULL) ; char * FM_bannerN[9] = { " CTRL + C twice to exit", @@ -54,14 +55,15 @@ char * FM_bannerN[9] = { "https://github.com/lwvmobile/dsd-fme/tree/pulseaudio " }; -char * SyncTypes[30] = { +//need to double check these sync types, not quite matching up with frame_sync +char * SyncTypes[36] = { "+P25P1", "-P25P1", "+X2TDMA DATA", "-X2TDMA DATA", "+X2TDMA VOICE", "-X2TDMA VOICE", - "+DSTARC", + "+DSTAR", "-DSTAR", "+NXDN VOICE", //8 "-NXDN VOICE", //9 @@ -82,7 +84,14 @@ char * SyncTypes[30] = { "-dPMR", "-dPMR", "-dPMR", - "-dPMR" + "-dPMR", + "+NXDN (sync only)", + "-NXDN (sync only)", + "+YSF", + "-YSF", + "DMR MS VOICE", + "DMR MS DATA", + "DMR RC DATA" }; @@ -202,7 +211,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //if (state->dmr_color_code != dcc && (lls == 11 || lls == 13 || lls == 10 || lls == 12) ) //DMR Voice + last two is data //if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR - if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 12 || lls == 13) ) //DMR, needs work + if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 12 || lls == 13 || lls == 32) ) //DMR, needs work { //reset amateur signs on radio id change for (short i = 0; i < 5; i++) @@ -228,6 +237,35 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) call_matrix[9][5] = time(NULL); //i++; } + /* + //needs work, look at saving just values to part of the state->dmr_lrrp array, or rework for int values + if ( (strcmp(state->dmr_lrrp[3], lrrp[3]) != 0) && (lls == 10 || lls == 11) ) //DMR Data LRRP + { + sprintf (lrrp[1], state->dmr_lrrp[1]); + sprintf (lrrp[2], state->dmr_lrrp[2]); + sprintf (lrrp[3], state->dmr_lrrp[3]); + //lrrp[1] = state->dmr_lrrp[1]; + //lrrp[2] = state->dmr_lrrp[2]; + //lrrp[3] = state->dmr_lrrp[3]; + for (short int k = 0; k < 9; k++) + { + call_matrix[k][0] = call_matrix[k+1][0]; + call_matrix[k][1] = call_matrix[k+1][1]; + call_matrix[k][2] = call_matrix[k+1][2]; + call_matrix[k][3] = call_matrix[k+1][3]; + call_matrix[k][4] = call_matrix[k+1][4]; + call_matrix[k][5] = call_matrix[k+1][5]; + } + + call_matrix[9][0] = lls; + call_matrix[9][1] = (unsigned long long int)&lrrp[3]; + call_matrix[9][2] = (unsigned long long int)&lrrp[1]; + call_matrix[9][3] = (unsigned long long int)&lrrp[2]; + call_matrix[9][4] = dcc; + call_matrix[9][5] = time(NULL); + //i++; + } + */ //if ( (lls == 0 || lls == 1) && state->lastsrc != rd && state->lastsrc > 0) //find condition to make this work well, probably if last != local last variables if ( (lls == 0 || lls == 1) && call_matrix[9][2] != rd && nc > 0 && tg > 0) //P25 @@ -304,9 +342,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("--Audio Decode----------------------------------------------------------------\n"); + printw ("| Decoding: %s \n", opts->output_name); printw ("| In Level: [%3i%%] \n", level); - printw ("| Voice Error: [%i][%i] \n| Error Bars: [%s] \n", state->errs, state->errs2, state->err_str); // - //printw ("| Carrier = %i\n", state->carrier); + //printw ("| Voice Error: [%i][%i] \n| Error Bars: [%s] \n", state->errs, state->errs2, state->err_str); // + printw ("| Voice Error: [%i][%i] \n", state->errs, state->errs2); printw ("------------------------------------------------------------------------------\n"); @@ -320,11 +359,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // case 3: Ptr = "AES"; break; // default: Ptr = "Unknown Cipher Type"; break; //} - + /* if (state->lastsynctype != -1) //not sure if this will be okay { lls = state->lastsynctype; } + */ + if (state->synctype > 0 && state->synctype < 34) //not sure if this will be okay + { + lls = state->synctype; + } if (state->nxdn_last_rid > 0 && state->nxdn_last_rid != src); { src = state->nxdn_last_rid; @@ -340,20 +384,20 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //if (state->dmr_color_code > 0 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? //if (state->color_code > -1 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? - if (state->color_code_ok && (lls == 12 || lls == 13) ) //DMR, needs work + if (state->color_code_ok && (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33) ) //DMR, needs work { dcc = state->dmr_color_code; //dcc = state->color_code; } - if (state->lastsrc > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1)) //DMR Voice and P25P1 + if (state->lastsrc > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1 || lls == 32)) //DMR Voice and P25P1 { rd = state->lastsrc; opts->p25enc = 0; } - if (state->lasttg > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1)) //DMR Voice and P25P1 + if (state->lasttg > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1 || lls == 32)) //DMR Voice and P25P1 { tg = state->lasttg; opts->p25enc = 0; @@ -440,7 +484,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //printw("| System Type: %s \n", ALGIDS[state->payload_keyid] ); } //if (state->lastsynctype == 12 || state->lastsynctype == 13) //DMR Voice Types - if (lls == 12 || lls == 13) //DMR Voice Types + if (lls == 12 || lls == 13 || lls == 32) //DMR Voice Types { //printw ("| DCC: [%i] FID: [%02X]\n", dcc, state->dmr_fid); //attron(COLOR_PAIR(3)); @@ -538,7 +582,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //if (state->lastsynctype == 10 || state->lastsynctype == 11) //DMR Data Types - if (lls == 10 || lls == 11 ) //DMR Data Types + if (lls == 10 || lls == 11 || lls == 33) //DMR Data Types { //printw ("| DCC: [%i]\n", dcc); printw ("| DCC: [%2i] FID: [%02X] SOP: [%02X] \n", dcc, state->dmr_fid, state->dmr_so); @@ -654,7 +698,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("TG [%4d] ", call_matrix[9-j][4]); printw ("RID [%4d] ", call_matrix[9-j][2]); } - if (lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 || lls == 11 ) //P25 P1 and DMR + if (lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33) //P25 P1 and DMR { printw ("TID [%8d] ", call_matrix[9-j][1]); printw ("RID [%8d] ", call_matrix[9-j][2]); diff --git a/src/dsd_symbol.c b/src/dsd_symbol.c index 96dccb1..ceb5cdf 100644 --- a/src/dsd_symbol.c +++ b/src/dsd_symbol.c @@ -300,8 +300,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync) state->lastsample = sample; - //steal sample here for rtl input or stdin input - state->input_sample_buffer = sample; //sample or sum, honestly this seemt to be best place to grab this sadly, sounds terrible most everywhere else + } symbol = (sum / count); diff --git a/src/dsd_upsample.c b/src/dsd_upsample.c index 42623f4..a700acd 100644 --- a/src/dsd_upsample.c +++ b/src/dsd_upsample.c @@ -23,8 +23,19 @@ upsample (dsd_state * state, float invalue) int i, j, sum; float *outbuf1, c, d; - - outbuf1 = state->audio_out_float_buf_p; + if (state->currentslot == 0 && state->dmr_stereo == 1) + { + outbuf1 = state->audio_out_float_buf_p; + } + if (state->currentslot == 1 && state->dmr_stereo == 1) + { + outbuf1 = state->audio_out_float_buf_pR; + } + if (state->dmr_stereo == 0) + { + outbuf1 = state->audio_out_float_buf_p; + } + //outbuf1 = state->audio_out_float_buf_p; outbuf1--; c = *outbuf1; d = invalue; @@ -43,6 +54,33 @@ upsample (dsd_state * state, float invalue) *outbuf1 = d; outbuf1++; + if (state->currentslot == 1 && state->dmr_stereo == 1) + { + if (state->audio_out_idx2R > 24) + { + // smoothing + outbuf1 -= 16; + for (j = 0; j < 4; j++) + { + for (i = 0; i < 6; i++) + { + sum = 0; + outbuf1 -= 2; + sum += (int)*outbuf1; + outbuf1 += 2; + sum += (int)*outbuf1; + outbuf1 += 2; + sum += (int)*outbuf1; + outbuf1 -= 2; + *outbuf1 = (sum / (float) 3); + outbuf1++; + } + outbuf1 -= 8; + } + } + } + if (state->currentslot == 0 && state->dmr_stereo == 1) + { if (state->audio_out_idx2 > 24) { // smoothing @@ -65,4 +103,30 @@ upsample (dsd_state * state, float invalue) outbuf1 -= 8; } } + } + if (state->dmr_stereo == 0) + { + if (state->audio_out_idx2 > 24) + { + // smoothing + outbuf1 -= 16; + for (j = 0; j < 4; j++) + { + for (i = 0; i < 6; i++) + { + sum = 0; + outbuf1 -= 2; + sum += (int)*outbuf1; + outbuf1 += 2; + sum += (int)*outbuf1; + outbuf1 += 2; + sum += (int)*outbuf1; + outbuf1 -= 2; + *outbuf1 = (sum / (float) 3); + outbuf1++; + } + outbuf1 -= 8; + } + } + } } diff --git a/src/dstar.c b/src/dstar.c index 9ca83e3..a5a945c 100644 --- a/src/dstar.c +++ b/src/dstar.c @@ -128,14 +128,15 @@ void processDSTAR(dsd_opts * opts, dsd_state * state) { slowdata[0] ^= 0x70; slowdata[1] ^= 0x4f; slowdata[2] ^= 0x93; - //fprintf (stderr, "unscrambled- %s",slowdata); + //fprintf (stderr, "unscrambled- %c%c%c \n", slowdata[0], slowdata[1], slowdata[2] ); } else if (framecount == 0) { - //fprintf (stderr, "never scrambled-%s\n",slowdata); + //fprintf (stderr, "never scrambled-%c\n",slowdata); + //fprintf (stderr, "never scrambled- %c%c%c \n", slowdata[0], slowdata[1], slowdata[2] ); } framecount++; - } + } //end while loop end: if (opts->errorbars == 1) { fprintf (stderr, "\n"); diff --git a/src/nxdn_data.c b/src/nxdn_data.c index 82b0684..3dc93e2 100644 --- a/src/nxdn_data.c +++ b/src/nxdn_data.c @@ -66,12 +66,19 @@ void processNXDNData (dsd_opts * opts, dsd_state * state) //fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1); if(CrcIsGood) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1); - fprintf (stderr, "(CRC OK) "); + fprintf (stderr, "%s", KNRM); + //fprintf (stderr, "(CRC OK) "); state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead } - else fprintf (stderr, "(CRC ERR) "); + else + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, "(CRC ERR) "); + fprintf (stderr, "%s", KNRM); + } /* Decode the SACCH only when all 4 voice frame diff --git a/src/nxdn_lib.c b/src/nxdn_lib.c index 786e781..22c8c75 100644 --- a/src/nxdn_lib.c +++ b/src/nxdn_lib.c @@ -732,6 +732,7 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message) } /* Print the "Call Type" */ + fprintf (stderr, "%s", KGRN); fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType)); //line break and 1 space //sprintf (state->nxdn_call_type, NXDN_Call_Type_To_Str(CallType)); //fix warning below //warning: format not a string literal and no format arguments [-Wformat-security] @@ -753,12 +754,16 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message) /* Print the Key ID */ if(CipherType != 0) { + fprintf (stderr, "%s", KYEL); fprintf(stderr, "Key ID %u - ", KeyID & 0xFF); + fprintf (stderr, "%s", KNRM); //state->nxdn_key = (KeyID & 0xFF); } /* Print Source ID and Destination ID (Talk Group or Unit ID) */ + fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF); + fprintf (stderr, "%s", KNRM); if(state->NxdnElementsContent.VCallCrcIsGood) @@ -770,18 +775,15 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message) state->nxdn_key = (KeyID & 0xFF); state->nxdn_cipher_type = CipherType; } - fprintf(stderr, "(CRC OK) "); + //fprintf(stderr, "(CRC OK) "); } //fprintf(stderr, " (OK) - "); - else fprintf(stderr, "(CRC ERR) "); - - //fprintf(stderr, "\nVCALL = "); - - //for(i = 0; i < 8; i++) - //{ - // fprintf(stderr, "0x%02X, ", ConvertBitIntoBytes(&Message[i * 8], 8) & 0xFF); - //} - //fprintf(stderr, "\n"); + else + { + fprintf (stderr, "%s", KRED); + fprintf(stderr, "(CRC ERR) "); + fprintf (stderr, "%s", KNRM); + } } /* End NXDN_decode_VCALL() */ diff --git a/src/nxdn_voice.c b/src/nxdn_voice.c index 3d3a270..04b12ab 100644 --- a/src/nxdn_voice.c +++ b/src/nxdn_voice.c @@ -80,11 +80,18 @@ void processNXDNVoice (dsd_opts * opts, dsd_state * state) //if(CrcIsGood) fprintf(stderr, " (OK) - "); if(CrcIsGood) { + fprintf (stderr, "%s", KCYN); fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1); - fprintf (stderr, "(CRC OK) "); + fprintf (stderr, "%s", KNRM); + //fprintf (stderr, "(CRC OK) "); state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead } - else fprintf(stderr, "(CRC ERR) "); + else + { + fprintf (stderr, "%s", KRED); + fprintf(stderr, "(CRC ERR) "); + fprintf (stderr, "%s", KNRM); + } /* Determine the current part of superframe diff --git a/src/p25p1_hdu.c b/src/p25p1_hdu.c index 5aa1d2f..2c6a9ad 100644 --- a/src/p25p1_hdu.c +++ b/src/p25p1_hdu.c @@ -469,19 +469,17 @@ processHDU(dsd_opts* opts, dsd_state* state) mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8); } - //if (state->errs == 0 && algidhex != 0x80) - if (1 == 1) //always print hdu alg info - //if (opts->payload == 1 && state->errs == 0) + if (opts->payload == 1) //print on payload == 1 { - //fprintf (stderr, " HDU ALG ID: 0x%X KEY ID: 0x%X MI: %s \n", algidhex, kidhex, mi); + fprintf (stderr, "%s", KYEL); fprintf (stderr, " HDU ALG ID: 0x%X KEY ID: 0x%X MI: 0x%08llX%08llX%02llX MFID: 0x%02X \n", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid); + fprintf (stderr, "%s", KNRM); } if (opts->p25enc == 1 && opts->payload == 0) { algidhex = strtol (algid, NULL, 2); kidhex = strtol (kid, NULL, 2); - //fprintf (stderr, "mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex); } if (opts->p25lc == 1) { diff --git a/src/p25p1_ldu1.c b/src/p25p1_ldu1.c index fc0519a..2c6fe5f 100644 --- a/src/p25p1_ldu1.c +++ b/src/p25p1_ldu1.c @@ -51,7 +51,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) if (opts->errorbars == 1) { - fprintf (stderr, "e:"); + //fprintf (stderr, "e:"); } // IMBE 1 diff --git a/src/p25p1_ldu2.c b/src/p25p1_ldu2.c index 0432f6a..90ebe4d 100644 --- a/src/p25p1_ldu2.c +++ b/src/p25p1_ldu2.c @@ -51,7 +51,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) if (opts->errorbars == 1) { - fprintf (stderr, "e:"); + //fprintf (stderr, "e:"); } // IMBE 1 @@ -375,18 +375,16 @@ processLDU2 (dsd_opts * opts, dsd_state * state) mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8); } - //if (state->errs == 0 && algidhex != 0x80) - if (1 == 1) //always print the LDU2 alg info - //if (opts->payload == 1 && state->errs == 0) + if (opts->payload == 1) //print on payload == 1 { - //fprintf (stderr, " LDU2 ALG ID: 0x%X KEY ID: 0x%X MI: %s \n", algidhex, kidhex, mi); + fprintf (stderr, "%s", KYEL); fprintf (stderr, " LDU2 ALG ID: 0x%X KEY ID: 0x%X MI: 0x%08llX%08llX%02llX\n", algidhex, kidhex, mihex1, mihex2, mihex3); + fprintf (stderr, "%s", KNRM); } - //if (opts->p25enc == 1 && opts->payload == 0) + if (opts->payload == 0) { algidhex = strtol (algid, NULL, 2); kidhex = strtol (kid, NULL, 2); - //fprintf (stderr, "mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex); } } diff --git a/src/p25p1_tdu.c b/src/p25p1_tdu.c index 4f4e501..432983a 100644 --- a/src/p25p1_tdu.c +++ b/src/p25p1_tdu.c @@ -22,7 +22,9 @@ processTDU (dsd_opts* opts, dsd_state* state) // Next we should find an status dibit if (status_count != 35) { + fprintf (stderr, "%s", KRED); fprintf (stderr, "*** SYNC ERROR\n"); + fprintf (stderr, "%s", KNRM); } // trailing status symbol diff --git a/src/p25p1_tdulc.c b/src/p25p1_tdulc.c index 1f5152a..2e56bf2 100644 --- a/src/p25p1_tdulc.c +++ b/src/p25p1_tdulc.c @@ -114,7 +114,9 @@ read_and_correct_dodeca_word (dsd_opts* opts, dsd_state* state, char* dodeca, in } else { + fprintf (stderr, "%s", KRED); fprintf (stderr, " IRRECOVERABLE"); + fprintf (stderr, "%s", KNRM); } fprintf (stderr, "\n"); #endif @@ -293,7 +295,9 @@ processTDULC (dsd_opts* opts, dsd_state* state) // Next we should find an status dibit if (status_count != 35) { + fprintf (stderr, "%s", KRED); fprintf (stderr, "*** SYNC ERROR\n"); + fprintf (stderr, "%s", KNRM); } // trailing status symbol diff --git a/src/trellis.cpp b/src/trellis.cpp index e667bbd..73b4315 100644 --- a/src/trellis.cpp +++ b/src/trellis.cpp @@ -1,8 +1,7 @@ /* * Copyright (C) 2016 by Jonathan Naylor, G4KLX * - * Modifications of original code to work with OP25 - * Copyright (C) 2019 by Graham J. Norbury + * Heavy Modificatins of Original Code to work for DSD-FME * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,7 +13,6 @@ * GNU General Public License for more details. */ #include "dsd.h" -//#include "trellis.h" #include #include @@ -52,6 +50,23 @@ const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U #define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) unsigned char state = 0U; +//rework quickHamming to accept unsigned intergers instead and go bit by bit +int quickHamming(unsigned int butt, unsigned int farts) +{ + unsigned int i = 0, count = 0, shit = 0, shart = 0; + //fprintf (stderr, "Distance Compare %X - %X \n", butt, farts ); + for (i; i < 3; i++) //shift three places tops; + { + shit = (butt >> i) & 0b1; + shart = (farts >> i) & 0b1; + if (shit != shart) + { + count++; + } + } + + return count; +} bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload) { @@ -101,44 +116,107 @@ void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* paylo // convert bits to bytes + //fprintf (stderr, "\n Tribit Binary Sequence\n "); + for (short i = 0; i < 144; i++) + { + //fprintf (stderr, "%b", bits[i]); + } + for(short int i = 0; i < 18; i++) + { + payload[i] = (bits[i * 8 + 0] << 7) | (bits[i * 8 + 1] << 6) | (bits[i * 8 + 2] << 5) | (bits[i * 8 + 3] << 4) | + (bits[i * 8 + 4] << 3) | (bits[i * 8 + 5] << 2) | (bits[i * 8 + 6] << 1) | (bits[i * 8 + 7] << 0); + } - for (int i = 0; i < 144; i++) - payload[i/8] = (payload[i/8] << 1) | bits[i]; + //for (int i = 0; i < 144; i++) + //payload[i/8] = (payload[i/8] << 1) | bits[i]; + //DmrDataBit[j + 0] = (TrellisReturn[17-i] >> 0) & 0x01; //example } unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits) { + //unsigned char state = 0U; //move outside of function to preserve state for every iteration //let's try reversing the points to pass through the decode table instead of flipping them here, //it may mess up the fixcode and failpos POS state = 0U; - for (unsigned int i = 0U; i < 49U; i++) { + for (unsigned int i = 0U; i < 49U;) //total number of points to process + { tribits[i] = 9U; - - for (unsigned int j = 0U; j < 8U; j++) { - //if (points[i] == ENCODE_TABLE[state * 8U + j]) { - if (points[i] == DECODE_TABLE[state * 8U + j]) { - //if (points[48-i] == DECODE_TABLE[state * 8U + j]) { + for (unsigned int j = 0U; j < 8U; j++) + { + //if (points[i] == DECODE_TABLE[state * 8U + j]) + //if (points[48-i] == DECODE_TABLE[state * 8U + j]) + if (points[i] == ENCODE_TABLE[state * 8U + j]) + { tribits[i] = j; + state = tribits[i]; //carry over if found right away + i++; //increment i by one value break; } } - + //try to find shortest distance for surviving paths and decide on one + //seems to be that two paths will have distance value 1, and two will have distance value 2 + //need to keep totaling up a few rounds before we can eliminate a path and resume? if (tribits[i] == 9U) - return i; + { + int distance = 0; + int maxdistance = 99; //random metric for two position distance + int pathdistance = 0; + unsigned int match = 0; + unsigned int nextmatch = 0; + unsigned int q = 0; + //only 4 possible states for each point, but in 8 (0-7) FSM states + char path[99][99] = {0}; //[i][pathcount] = distance + int pathcount = 0; + for (unsigned int o = 0; o < 64; o++) //check entire state transition table for matches + { + if (points[i] == ENCODE_TABLE[o]) //ENCODE TABLE + { + match = o / (unsigned int)8; //get dividend no remainder/decimal I hope, get fsm state 0-7 + //fprintf (stderr, "\n Point #%d [%d] Match State %d Position %d Tribit Value %d \n", i, points[i], match, o, o % (unsigned int)8); + distance = quickHamming(match, state); + //fprintf (stderr, "Raw Distance Current %d\n", distance); + path[i][pathcount] = distance ; //save which table values (paths) match as possible values and distance + for (q; q < 64; q++) //check entire state transition table for next point match + { + if (points[i+1] == ENCODE_TABLE[q]) //ENCODE TABLE + { + nextmatch = q / (unsigned int)8; + path[i+1][pathcount] = quickHamming(nextmatch, q % (unsigned int)8); + //fprintf (stderr, "Raw Distance Next %d\n", path[i+1][pathcount]); + pathcount++; + //fprintf (stderr, "State 0 Match %d Distance %d \n", o, distance); + } + } + for (short int z = 0; z <= pathcount; z++) //add metrics on paths to find shortest distance + { + pathdistance = path[i][0] + path[i+1][z]; + //fprintf (stderr, "Total Path Distance %d\n", pathdistance); + if (pathdistance <= maxdistance) //equal too in case of tie? + { + maxdistance = pathdistance; + tribits[i] = o % (unsigned int)8; //remainder value will be tribit value on table + if (i < 49) //just in case we are on 49 already, don't want to crash or something. + { + tribits[i+1] = q % (unsigned int)8; //remainder value will be tribit value on table + } - state = tribits[i]; + //fprintf (stderr, "Point #%d [%d] - Tribit Candidate %d\n", i, points[i], tribits[i] ); + //fprintf (stderr, "Point #%d [%d]- Tribit Candidate %d\n", i+1, points[i+1], tribits[i+1] ); + } + } + } + //fprintf (stderr, "Selected Point #%d [%d] - Tribit Selection %d\n", i, points[i], tribits[i] ); + //fprintf (stderr, "Selected Point #%d [%d]- Tribit Selection %d\n", i+1, points[i+1], tribits[i+1] ); + } + state = tribits[i+1]; //not quite sure if this will be needed or not, if so, set state to last found value + i += 2; //increment i by two since we already got the next tribit value as well; + //state = tribits[i]; //not quite sure if this will be needed or not, if so, set state to last found value + //i++; //increment i by two since we already got the next tribit value as well; + } } - - //if (tribits[48U] != 0U) - if (tribits[48U] != 0U) // - { - return 48U; - } - - return 999U; } @@ -146,7 +224,8 @@ void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits) { int n; signed char dibit; - + //can only assume this is giving the correct dibits out, and not inverse or something. + /* for (int i = 0; i < 98; i++) { n = i * 2; if (n >= 98) n += 68; @@ -157,19 +236,44 @@ void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits) bool b2 = data[n] != 0x0; if (!b1 && b2) - dibit = +3; + dibit = +3; // 1 - 01 else if (!b1 && !b2) - dibit = +1; + dibit = +1; // 0 - 00 else if (b1 && !b2) - dibit = -1; + dibit = -1; // 2 - 10 else - dibit = -3; + dibit = -3; // 3 - 11 - n = INTERLEAVE_TABLE[i]; //should this go in reverse? - dibits[n] = dibit; + */ + //Keep it simple! + for (int i = 0; i < 98; i++) + { + n = INTERLEAVE_TABLE[97-i]; + //n = i; + if (data[i] == 0) + { + dibits[n] = 1; //1 + } + if (data[i] == 1) + { + dibits[n] = 3; //3 + } + if (data[i] == 2) + { + dibits[n] = -1; //-1 + } + if (data[i] == 3) + { + dibits[n] = -3; //-3 + } + } + + //n = INTERLEAVE_TABLE[i]; //should this go in reverse? + //n = INTERLEAVE_TABLE[97-i]; //should this go in reverse? + //dibits[n] = dibit; //should this go in reverse too? not sure on this one, but definitely the INTERLEAVE_TABLE //n = INTERLEAVE_TABLE[97-i]; //should this go in reverse? //dibits[97-n] = dibit; - } + //} } void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points) @@ -324,7 +428,7 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload) } signed char dibits[98U]; CDMRTrellisDeinterleave(data, dibits); - if (1 == 1) + if (1 == 2) { fprintf (stderr, "\n Trellis Deinterleaved Dibits inside Decoder (converted to signed)\n "); for (short i = 0; i < 98; i++) @@ -333,8 +437,19 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload) } } unsigned char points[49U]; + unsigned char reverse_points[49U]; CDMRTrellisDibitsToPoints(dibits, points); - if (1 == 1) + //maybe keep point 49 at 49, since its the flush? + //fprintf (stderr, "\n Trellis Reverse Points inside Decoder\n "); + for (short i = 0; i < 49; i++) + { + reverse_points[i] = points[48-i]; + //fprintf (stderr, "#%d [%02u] ", i, reverse_points[i]); + } + //reverse_points[48] = points[48]; + //fprintf (stderr, "#%d [%02u] ", 48, reverse_points[48]); + + if (1 == 2) { fprintf (stderr, "\n Trellis Points inside Decoder\n "); for (short i = 0; i < 49; i++) @@ -343,19 +458,11 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload) } } // Check the original code - unsigned char reverse_points[49U]; - fprintf (stderr, "\n Trellis Reversed Points inside Decoder\n "); - for (short i = 0; i < 49; i++) - { - reverse_points[i] = points[48-i]; - fprintf (stderr, "#%d [%02u] ", i, reverse_points[i]); - } - //unsigned int failPos = CDMRTrellisCheckCode(points, tribits); - unsigned int failPos = CDMRTrellisCheckCode(reverse_points, tribits); + unsigned int failPos = CDMRTrellisCheckCode(points, tribits); + //unsigned int failPos = CDMRTrellisCheckCode(reverse_points, tribits); //stack smashing detected terminated - - if (1 == 1) + if (1 == 2) { fprintf (stderr, "\n Check Code Trellis Tribits inside Decoder\n "); for (short i = 0; i < 49; i++) @@ -367,27 +474,36 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload) if (failPos == 999U) { CDMRTrellisTribitsToBits(tribits, payload); - if (1 == 1) + if (1 == 2) { - fprintf (stderr, "\nTrellis Payload Tribits to Bits failPos == 999U\n "); - for (short i = 0; i < 18; i++) + fprintf (stderr, "\nTrellis Payload Tribits\n %s ", KCYN); + for (short i = 0; i < 49; i++) { fprintf (stderr, "#%d [%0u] ", i, tribits[i]); //shouldn't have any values greater that 0x7? } } + if (1 == 2) + { + fprintf (stderr, "\nTrellis Payload Tribits to Bytes inside Decoder\n %s ", KCYN); + for (short i = 0; i < 18; i++) + { + fprintf (stderr, "[%02X]", payload[i]); + } + fprintf (stderr,"%s", KNRM); //change back to normal + } return true; } unsigned char savePoints[49U]; for (unsigned int i = 0U; i < 49U; i++) - //savePoints[i] = points[i]; - savePoints[i] = reverse_points[i]; + savePoints[i] = points[i]; + //savePoints[i] = reverse_points[i]; - //bool ret = CDMRTrellisFixCode(points, failPos, payload); - bool ret = CDMRTrellisFixCode(reverse_points, failPos, payload); + bool ret = CDMRTrellisFixCode(points, failPos, payload); + //bool ret = CDMRTrellisFixCode(reverse_points, failPos, payload); if (ret){ - if (1 == 1) + if (1 == 2) { fprintf (stderr, "\n if return Trellis Tribits inside Decoder\n "); for (short i = 0; i < 49; i++) @@ -396,7 +512,7 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload) } } - if (1 == 1) + if (1 == 2) { fprintf (stderr, "\nTrellis Payload Tribits to Bits fix return true\n "); //this one seems to be happening for (short i = 0; i < 18; i++) @@ -408,9 +524,9 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload) if (failPos == 0U) { - if (1 == 1) + if (1 == 2) { - fprintf (stderr, "\nTrellis Payload Tribits to Bits failPos == 0U\n "); + fprintf (stderr, "\nTrellis Payload Tribits to Bytes failPos == 0U\n "); for (short i = 0; i < 18; i++) { fprintf (stderr, "[%02X]", payload[i]); diff --git a/src/ysf.c b/src/ysf.c new file mode 100644 index 0000000..8882036 --- /dev/null +++ b/src/ysf.c @@ -0,0 +1,287 @@ +/*------------------------------------------------------------------------------- + * ysf.c + * Yaesu Fusion Decoder for DSD + * + * LWVMOBILE + * 2022-04 DSD-FME Florida Man Edition + *-----------------------------------------------------------------------------*/ + /* + * 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" + +const int fichInterleave[100] = { + 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, + 1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, 76, 81, 86, 91, 96, + 2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, + 3, 8, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58, 63, 68, 73, 78, 83, 88, 93, 98, + 4, 9, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64, 69, 74, 79, 84, 89, 94, 99, +}; + +const int vd2Interleave[104] = { + 0, 26, 52, 78, + 1, 27, 53, 79, + 2, 28, 54, 80, + 3, 29, 55, 81, + 4, 30, 56, 82, + 5, 31, 57, 83, + 6, 32, 58, 84, + 7, 33, 59, 85, + 8, 34, 60, 86, + 9, 35, 61, 87, + 10, 36, 62, 88, + 11, 37, 63, 89, + 12, 38, 64, 90, + 13, 39, 65, 91, + 14, 40, 66, 92, + 15, 41, 67, 93, + 16, 42, 68, 94, + 17, 43, 69, 95, + 18, 44, 70, 96, + 19, 45, 71, 97, + 20, 46, 72, 98, + 21, 47, 73, 99, + 22, 48, 74, 100, + 23, 49, 75, 101, + 24, 50, 76, 102, + 25, 51, 77, 103 +}; + +const int vfrInterleave[144] = { //IMBE 7200x4400 full rate interleave schedule + 0, 24, 48, 72, 96, 120, 25, 1, 73, 49, 121, 97, + 2, 26, 50, 74, 98, 122, 27, 3, 75, 51, 123, 99, + 4, 28, 52, 76, 100, 124, 29, 5, 77, 53, 125, 101, + 6, 30, 54, 78, 102, 126, 31, 7, 79, 55, 127, 103, + 8, 32, 56, 80, 104, 128, 33, 9, 81, 57, 129, 105, + 10, 34, 58, 82, 106, 130, 35, 11, 83, 59, 131, 107, + 12, 36, 60, 84, 108, 132, 37, 13, 85, 61, 133, 109, + 14, 38, 62, 86, 110, 134, 39, 15, 87, 63, 135, 111, + 16, 40, 64, 88, 112, 136, 41, 17, 89, 65, 137, 113, + 18, 42, 66, 90, 114, 138, 43, 19, 91, 67, 139, 115, + 20, 44, 68, 92, 116, 140, 45, 21, 93, 69, 141, 117, + 22, 46, 70, 94, 118, 142, 47, 23, 95, 71, 143, 119 +}; + +//half-rate ambe3600x2450 interleave schedule +const int YSFrW[36] = { + 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 2, + 0, 2, 0, 2, 0, 2, + 0, 2, 0, 2, 0, 2 +}; + +// bit index +const int YSFrX[36] = { + 23, 10, 22, 9, 21, 8, + 20, 7, 19, 6, 18, 5, + 17, 4, 16, 3, 15, 2, + 14, 1, 13, 0, 12, 10, + 11, 9, 10, 8, 9, 7, + 8, 6, 7, 5, 6, 4 +}; + +// bit 0 +// frame index +const int YSFrY[36] = { + 0, 2, 0, 2, 0, 2, + 0, 2, 0, 3, 0, 3, + 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3 +}; + +// bit index +const int YSFrZ[36] = { + 5, 3, 4, 2, 3, 1, + 2, 0, 1, 13, 0, 12, + 22, 11, 21, 10, 20, 9, + 19, 8, 18, 7, 17, 6, + 16, 5, 15, 4, 14, 3, + 13, 2, 12, 1, 11, 0 +}; + +void processYSF(dsd_opts * opts, dsd_state * state) //YSF +{ + //480 dibits - 20 dibit sync leaves 460 dibits (920 bits) each 'superframe' + fprintf (stderr, "processYSF Function \n"); + uint32_t i, j, k; + unsigned char dibit; + const int *w, *x, *y, *z; + char ambe_fr[8][4][24]; + unsigned char storage[8][4][24]; //may expand later + unsigned char discard[460]; //just set up for now to hold random dibit grabs with nothing to do but skip them + unsigned char vch[4][36]; //correct size array for 4*32 dibit VD2 VCH? + unsigned char fich[200]; //store as bits after deinterleaving + unsigned char fichraw[100]; + unsigned char fichdibits[100]; //deinterleaved fich dibits + unsigned char fichbytes[26]; + unsigned int fi = 0; + unsigned int cs = 0; + unsigned int cm = 0; + unsigned int bn = 0; + unsigned int bt = 0; + unsigned int fn = 0; + unsigned int ft = 0; + unsigned int mr = 0; + unsigned int ip = 0; + unsigned int dt = 0; //DT, 2 bytes (according to manual, but has to be two bits) + unsigned int sq = 0; + unsigned int sc = 0; + int vd1 = 1; + int vd2 = 0; + unsigned int msbI = 0; + unsigned int lsbI = 0; + unsigned char vd2BitsRaw[104]; + opts->inverted_ysf = 0; //here for testing purposes, move to main later + for (i = 0; i < 100; i++) //100 dibits for fich + { + dibit = getDibit (opts, state); + + if (opts->inverted_ysf == 1) //opts->inverted_ysf == 1 + { + dibit = (dibit ^ 2); + } + fichraw[i] = dibit; //raw storage for debug + fichdibits[fichInterleave[i]] = dibit; //sort into proper order + //fich[i*2] = (1 & (dibit >> 1)); // bit 1 + //fich[i*2+1] = (1 & dibit); // bit 0 + fich[i*2] = (fichdibits[fichInterleave[i]] >> 1) & 1; + //fprintf (stderr, "%b", fich[i*2]); + fich[i*2+1] = fichdibits[fichInterleave[i]] & 1; + //fprintf (stderr, "%b", fich[i*2+1]); + if (i == 4) + { + //datatype = dibit; + } + } + //fprintf (stderr, "\nFICH BYTES = "); + for (i = 0; i < 25; i++) //bits to bytes + { + fichbytes[i] = 0; //initialize to see what isn't beign set + fichbytes[i] = ( (fich[i*8] << 7) | (fich[i*8+1] << 6) | (fich[i*8+2] << 5) | (fich[i*8+3] << 4) | + (fich[i*8+4] << 3) | (fich[i*8+5] << 2) | (fich[i*8+6] << 1) | (fich[i*8+7] << 0) ); + //fprintf (stderr, "[%02X]", fichbytes[i]); + } + //fprintf (stderr, "\n"); + //datatype = (fich[8] << 1) + fich[9]; + dt = fichbytes[2] & 0x03; + for (i = 0; i < 2; i++) + { + //datatype << 1; + //datatype = datatype | fichraw[i+8]; + //fprintf (stderr, "%02b", fichdibits[i+4]); + //fprintf (stderr, "%02b", fichdibits[i+11]); + } + //fprintf (stderr, "Datatype = %02b\n", dt); + //quick dirty VD1 method? + if (0 == 0) //dt == 0 for VD1 + { + fprintf (stderr, "processYSF VD1 \n"); + for (j = 0; j < 5; j++) //5 DCH and VCH + { + w = YSFrW; + x = YSFrX; + y = YSFrY; + z = YSFrZ; + k = 0; + for (i = 0; i < 36; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_ysf == 1) //opts->inverted_ysf == 1 + { + dibit = (dibit ^ 2); + } + + storage[j][*w][*x] = (1 & (dibit >> 1)); // bit 1 + storage[j][*y][*z] = (1 & dibit); // bit 0 + + } + + + + //AMBE VCH + for (i = 0; i < 36; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_ysf == 1) //opts->inverted_ysf == 1 + { + dibit = (dibit ^ 2); + } + + ambe_fr[j][*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr[j][*y][*z] = (1 & dibit); // bit 0 + w++; + x++; + y++; + z++; + } + //processMbeFrame (opts, state, NULL, ambe_fr[j], NULL); + } //end j < 4 loop + } //end VD1 + //move processMbeFrame to outside of dibit loop to prevent slip + for (j = 0; j < 5; j++) + { + //processMbeFrame (opts, state, NULL, ambe_fr[j], NULL); + } + //VD2 method + if (0 == 3) //dt == 3 + { + fprintf (stderr, "processYSF VD2 \n"); + for (j = 0; j < 4; j++) + { + //VCH[j] and VeCH[j] + for (i = 0; i < 20; i++) //32? + { + dibit = getDibit (opts, state); + + if (opts->inverted_ysf == 1) //opts->inverted_ysf == 1 + { + dibit = (dibit ^ 2); + } + //collect dibits into array for processing + vch[j][i] = dibit; + w = YSFrW; + x = YSFrX; + y = YSFrY; + z = YSFrZ; + msbI = vd2Interleave[(j+1)*i]; //don't this this is remotely correct, really could use the manual right about now + lsbI = vd2Interleave[(j+1)*i+1]; + vd2BitsRaw[msbI] = ((vch[j][i] >> 1) & 1); + vd2BitsRaw[lsbI] = (vch[j][i] & 1); + } // VCH[j] + //DCH[j] + for (i = 0; i < 20; i++) //32? + { + dibit = getDibit (opts, state); + + if (opts->inverted_ysf == 1) //opts->inverted_ysf == 1 + { + dibit = (dibit ^ 2); + } + discard[i] = dibit; + } //end DCH[j] + } //end for j loop + + } //end VD2 + +} //end processYSF From ab873d3821a33d18940ad30a32afe818ec19b2b5 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Tue, 3 May 2022 06:54:08 -0400 Subject: [PATCH 02/10] NCurses Updates Update NCurses to work with DMR BS TDMA Stereo, DMR MS, dPMR, D-STAR --reorganize the code and clean up the mess Minor Tweaks and Code Cleanup --- include/dsd.h | 33 +- include/dstar_header.h | 4 +- src/dmr_bs.c | 8 +- src/dmr_data.c | 12 +- src/dmr_ms.c | 4 +- src/dmr_process_voice.c | 2 +- src/dmr_sync.c | 245 ++++++++--- src/dmr_voice.c | 9 + src/dsd_frame.c | 7 +- src/dsd_main.c | 124 ++++-- src/dsd_ncurses.c | 888 +++++++++++++++++++++++----------------- src/dsd_reset.c | 9 +- src/dstar.c | 30 +- src/dstar_header.c | 12 +- src/p25p1_hdu.c | 4 +- src/p25p1_ldu2.c | 4 +- 16 files changed, 879 insertions(+), 516 deletions(-) diff --git a/include/dsd.h b/include/dsd.h index 5fd5ad5..4660580 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -374,6 +374,7 @@ typedef struct // int frame_ysf; int inverted_ysf; //not sure if ysf comes in inverted or not, but signal could if IQ flipped + short int aggressive_framesync; //set to 1 for more aggressive framesync, 0 for less aggressive } dsd_opts; @@ -432,7 +433,9 @@ typedef struct char tg[25][16]; int tgcount; int lasttg; + int lasttgR; int lastsrc; + int lastsrcR; int nac; int errs; int errs2; @@ -466,9 +469,13 @@ typedef struct mbe_parms *prev_mp_enhanced2; int p25kid; int payload_algid; + int payload_algidR; int payload_keyid; + int payload_keyidR; int payload_mfid; + int payload_mfidR; int payload_mi; + int payload_miR; unsigned long long int K; unsigned int debug_audio_errors; @@ -488,17 +495,6 @@ typedef struct //input sample buffer for monitoring Input short input_sample_buffer; //HERE HERE short pulse_raw_out_buffer; //HERE HERE - //float *input_sample_buffer; //HERE HERE - short dibit_to_hex_buf; - // state->sr_0 shift registers - short hexbuf; - unsigned long long sr_0; //64-bit shift registers for pushing decoded binary or dibit data - unsigned long long sr_1; //384 - unsigned long long sr_2; // - unsigned long long sr_3; // - unsigned long long sr_4; // - unsigned long long sr_5; // - unsigned long long sr_6; // unsigned int dmr_color_code; unsigned int nxdn_last_ran; @@ -507,8 +503,8 @@ typedef struct unsigned int nxdn_cipher_type; unsigned int nxdn_key; char nxdn_call_type[1024]; - char dmr_callsign[6][99]; //plenty of room in case of overflow; - char dmr_lrrp[6][9999]; + char dmr_callsign[2][6][99]; //plenty of room in case of overflow; + char dmr_lrrp[2][6][9999]; NxdnSacchRawPart_t NxdnSacchRawPart[4]; NxdnFacch1RawPart_t NxdnFacch1RawPart[2]; @@ -531,6 +527,9 @@ typedef struct unsigned int dmr_fid; unsigned int dmr_so; + unsigned int dmr_fidR; + unsigned int dmr_soR; + char slot1light[8]; char slot2light[8]; int directmode; @@ -540,8 +539,6 @@ typedef struct char dmr_branding[25]; uint8_t dmr_12_rate_sf[2][60]; //going five frames deep by 12 bytes //[slot][value] uint8_t dmr_34_rate_sf[2][64]; //going four frames deep by 16 bytes //[slot][value] - //char dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame - //char dmr_msmode_payload[144]; int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame @@ -572,6 +569,11 @@ typedef struct short int dmr_stereo; //need state variable for upsample function short int dmr_ms_rc; short int dmr_ms_mode; + unsigned int dmrburstL; + unsigned int dmrburstR; + + //dstar header for ncurses + unsigned char dstarradioheader[41]; #ifdef TRACE_DSD char debug_prefix; @@ -857,6 +859,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits); void InitAllFecFunction(void); void resetState (dsd_state * state); +void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]); #ifdef __cplusplus extern "C" { diff --git a/include/dstar_header.h b/include/dstar_header.h index 9723fbf..0b8e86d 100644 --- a/include/dstar_header.h +++ b/include/dstar_header.h @@ -2,5 +2,5 @@ #ifndef _DSTAR_HEADER_H #define _DSTAR_HEADER_H -void dstar_header_decode(int radioheaderbuffer[660]); -#endif /* _DSTAR_HEADER_H */ \ No newline at end of file +//void dstar_header_decode(int radioheaderbuffer[660]); +#endif /* _DSTAR_HEADER_H */ diff --git a/src/dmr_bs.c b/src/dmr_bs.c index 7d3c70d..33dc1d1 100644 --- a/src/dmr_bs.c +++ b/src/dmr_bs.c @@ -293,7 +293,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state) skipcount++; //after 6 data frames, drop back to getFrameSync and process subsequent data with processDMRdata goto SKIP; } - + if( (strcmp (sync, DMR_BS_DATA_SYNC) != 0) ) //only play voice no data sync // || (strcmp (sync, DMR_MS_DATA_SYNC) != 0) { if (EmbeddedSignallingOk == 0) @@ -304,11 +304,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state) fprintf (stderr,"%s ", getTime()); if (internalslot == 0) { + state->dmrburstL = 16; //use 16 for Voice? fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | VC%d \n", state->color_code, vc1); } if (internalslot == 1) { + state->dmrburstR = 16; //use 16 for Voice? fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | VC%d \n", state->color_code, vc2); } if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues) @@ -354,8 +356,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state) } //using these conditions may cause excessive resyncs IF bad signal, //but still better than getting stuck in a wonk wonk loop for too long. - //maybe consider making a opt variable for loose or aggressive resyncing for this sort of thing? - if (1 == 1) //set for more aggressive or less aggressive resync during accumulated playback errs + //set for more aggressive or less aggressive resync during accumulated playback errs + if (opts->aggressive_framesync == 1) { //errors caused due to playing MBE files out of sync, break loop if (state->errs > 2 || state->errsR > 2) diff --git a/src/dmr_data.c b/src/dmr_data.c index d747695..ccb31f2 100644 --- a/src/dmr_data.c +++ b/src/dmr_data.c @@ -51,7 +51,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state) dibit_p = state->dibit_buf_p - 90; //using the estimate_symbol method for the dmr_payload_p buffer causes sync //issues with P25, so only do it when frame_p25p1 == 0, or -fr option - if (opts->frame_p25p1 != 1) + if (opts->frame_p25p1 == 0) //opts->frame_p25p1 == 0 { dibit_p = state->dmr_payload_p - 90; } @@ -312,7 +312,17 @@ processDMRdata (dsd_opts * opts, dsd_state * state) //fprintf(stderr, "| Color Code=%02d ", (int)state->color_code); /* Reconstitute the burst type */ + //consider assigning thsi 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; + } + /* Reconstitute the burst type */ bursttype[0] = SlotType[4] + '0'; diff --git a/src/dmr_ms.c b/src/dmr_ms.c index e8dc69d..5e5aafc 100644 --- a/src/dmr_ms.c +++ b/src/dmr_ms.c @@ -66,7 +66,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state) //Run Loop while the getting is good while (loop == 1) { - + state->dmrburstL = 16; //Use 16 for Voice? // No CACH in MS Mode? for(i = 0; i < 12; i++) { @@ -464,7 +464,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state) return curr; } - + state->dmrburstL = 16; //Use 16 for Voice? dibit_p = state->dmr_payload_p - 90; //payload buffer tests //CACH + First Half Payload + Sync = 12 + 54 + 24 diff --git a/src/dmr_process_voice.c b/src/dmr_process_voice.c index 9c3e2af..36bc56d 100644 --- a/src/dmr_process_voice.c +++ b/src/dmr_process_voice.c @@ -94,7 +94,7 @@ if (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->pa } } -if (state->currentslot == 1 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0) +if (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0) { fprintf (stderr, "%s", KYEL); fprintf(stderr, " BPK %lld", state->K); diff --git a/src/dmr_sync.c b/src/dmr_sync.c index 17f3e3a..dd9cbda 100644 --- a/src/dmr_sync.c +++ b/src/dmr_sync.c @@ -219,13 +219,17 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] fprintf (stderr, " - Port %05d", (state->dmr_34_rate_sf[10] << 8) + state->dmr_34_rate_sf[11]); } */ + //temp hide behind payload until working better + if (opts->payload == 1) + { //LRRP if ( (state->dmr_34_rate_sf[slot][0] & 0x7F) == 0x45) //Start LRRP now { + sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); fprintf (stderr, "%s ", KMAG); //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); - for (short i = 1; i < 64; i++) //find way to get padding so we only go as deep as we need to! + for (short i = 1; i < 60; i++) //find way to get padding so we only go as deep as we need to! changed from 64 to 60 to skip the CRC Bytes for confirmed data { if ( state->dmr_34_rate_sf[slot][i] == 0x0C) //Source and Destination info { @@ -269,9 +273,13 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] //fprintf (stderr, "-"); } fprintf (stderr, "%.5lf", (lrrplon * lon_unit) ); - sprintf ( state->dmr_lrrp[3], "Lat: %.5lf Lon: %.5lf", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + if (state->dmr_34_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? + { + sprintf ( state->dmr_lrrp[state->currentslot][3], "Lat: -%.5lf Lon: %.5lf ", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + } + else sprintf ( state->dmr_lrrp[state->currentslot][3], "Lat: %.5lf Lon: %.5lf ", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); //print for easy copy/paste into browser? - fprintf (stderr, " ("); + //fprintf (stderr, " ("); if (state->dmr_34_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { fprintf (stderr, "-"); @@ -287,16 +295,17 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] double velocity = ( ((double)( (state->dmr_34_rate_sf[slot][i+1] ) + state->dmr_34_rate_sf[slot][i+2] )) / ( (double)128)); //fprintf (stderr, "\n LRRP - %.4lf Meters Per Second", velocity); fprintf (stderr, "\n LRRP - %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity) ); - sprintf ( state->dmr_lrrp[1], "Vel: %.4lf kph", (3.6 * velocity)); + sprintf ( state->dmr_lrrp[state->currentslot][4], "Vel: %.4lf kph ", (3.6 * velocity)); } if ( state->dmr_34_rate_sf[slot][i] == 0x56 ) { //check for appropriate terminology here - Heading, bearing, course, or track? fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_34_rate_sf[slot][i+1] * 2); - sprintf ( state->dmr_lrrp[2], "Dir: %d Deg", state->dmr_34_rate_sf[slot][i+1] * 2); + sprintf ( state->dmr_lrrp[state->currentslot][5], "Dir: %d Deg ", state->dmr_34_rate_sf[slot][i+1] * 2); } } } + }//end payload fprintf (stderr, "%s ", KNRM); //Full if (opts->payload == 2) @@ -1080,7 +1089,8 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ DmrDataBit[j + 7] = (DmrDataByte[i] >> 0) & 0x01; } //Headers Source Destination - if ( (state->dmr_12_rate_sf[slot][36] & 0x7F) == 0x45) //Start Source Destination early in case not enough 1/2 data frames + //Start Source Destination early in case not enough 1/2 data frames + if ( (state->dmr_12_rate_sf[slot][36] & 0x7F) == 0x45 ) { fprintf (stderr, "%s ", KMAG); //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? @@ -1101,13 +1111,73 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ } fprintf (stderr, "%s ", KNRM); } + /* + //Jurek lrrp, 0x41 is probably a different header type or something, or I need to look at the data header packet PDU first. + if ( (state->dmr_12_rate_sf[slot][36] & 0x7F) == 0x41) //Start LRRP now + { + sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); + fprintf (stderr, "%s ", KMAG); + for (short i = 36; i < 60; i++) //find way to get padding so we only go as deep as we need to! + { + if ( state->dmr_12_rate_sf[slot][i] == 0x51 ) //lattitude and longitude + { + fprintf (stderr, "\n LRRP -"); + fprintf (stderr, " Lat: "); + if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? + { + fprintf (stderr, "-"); + } + long int lrrplat; + long int lrrplon; + double lat_unit = (double)180/(double)4294967295; + double lon_unit = (double)360/(double)4294967295; + lrrplat = ( ( ((state->dmr_12_rate_sf[slot][i+1] & 0x7F ) << 24 ) + (state->dmr_12_rate_sf[slot][i+2] << 16) + (state->dmr_12_rate_sf[slot][i+3] << 8) + state->dmr_12_rate_sf[slot][i+4]) * 1 ); + lrrplon = ( ( (state->dmr_12_rate_sf[slot][i+5] << 24 ) + (state->dmr_12_rate_sf[slot][i+6] << 16) + (state->dmr_12_rate_sf[slot][i+7] << 8) + state->dmr_12_rate_sf[slot][i+8]) * 1 ); + fprintf (stderr, "%.5lf ", ((double)lrrplat) * lat_unit); + fprintf (stderr, " Lon: "); + if (state->dmr_12_rate_sf[slot][i+5] & 0x80) //first bit indicates a sign, or hemisphere? + { + //fprintf (stderr, "-"); + } + fprintf (stderr, "%.5lf", (lrrplon * lon_unit) ); + sprintf ( state->dmr_lrrp[state->currentslot][3], "Lat: %.5lf Lon: %.5lf ", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + //print for easy copy/paste into browser? + //fprintf (stderr, " ("); + if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? + { + fprintf (stderr, "-"); + } + fprintf (stderr, "%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + } + + + if ( state->dmr_12_rate_sf[slot][i] == 0x6C ) + { + //either Plus is wrong, or I'm wrong on higher velocities exceeding 0xFF. + //fprintf (stderr, "\n LRRP - Vi %02X Vf %02X Velocity Units (hex)", state->dmr_34_rate_sf[i+1], state->dmr_34_rate_sf[i+2]); + double velocity = ( ((double)( (state->dmr_12_rate_sf[slot][i+1] ) + state->dmr_12_rate_sf[slot][i+2] )) / ( (double)128)); + //fprintf (stderr, "\n LRRP - %.4lf Meters Per Second", velocity); + fprintf (stderr, "\n LRRP - %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity) ); + sprintf ( state->dmr_lrrp[state->currentslot][4], "Vel: %.4lf kph ", (3.6 * velocity)); + } + if ( state->dmr_12_rate_sf[slot][i] == 0x56 ) + { + //check for appropriate terminology here - Heading, bearing, course, or track? + fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_12_rate_sf[slot][i+1] * 2); + sprintf ( state->dmr_lrrp[state->currentslot][5], "Dir: %d Deg ", state->dmr_12_rate_sf[slot][i+1] * 2); + } + } + } + //end Jurek lrrp + */ //LRRP if ( (state->dmr_12_rate_sf[slot][0] & 0x7F) == 0x45) //Start LRRP now { + sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); fprintf (stderr, "%s ", KMAG); //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); - for (short i = 1; i < 60; i++) //find way to get padding so we only go as deep as we need to! + for (short i = 1; i < 56; i++) //find way to get padding so we only go as deep as we need to! changed from 60 to 56 to skip the CRC bytes { /* if ( state->dmr_12_rate_sf[slot][i] == 0x0C) //Source and Destination info @@ -1153,9 +1223,12 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ //fprintf (stderr, "-"); } fprintf (stderr, "%.5lf", (lrrplon * lon_unit) ); - sprintf ( state->dmr_lrrp[3], "Lat: %.5lf Lon: %.5lf", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); - //print for easy copy/paste into browser? - fprintf (stderr, " ("); + if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? + { + sprintf ( state->dmr_lrrp[state->currentslot][3], "Lat: -%.5lf Lon: %.5lf ", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + } + else sprintf ( state->dmr_lrrp[state->currentslot][3], "Lat: %.5lf Lon: %.5lf ", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); //print for easy copy/paste into browser? + //fprintf (stderr, " ("); if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { fprintf (stderr, "-"); @@ -1171,13 +1244,13 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ double velocity = ( ((double)( (state->dmr_12_rate_sf[slot][i+1] ) + state->dmr_12_rate_sf[slot][i+2] )) / ( (double)128)); //fprintf (stderr, "\n LRRP - %.4lf Meters Per Second", velocity); fprintf (stderr, "\n LRRP - %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity) ); - sprintf ( state->dmr_lrrp[1], "Vel: %.4lf kph", (3.6 * velocity)); + sprintf ( state->dmr_lrrp[state->currentslot][4], "Vel: %.4lf kph ", (3.6 * velocity)); } if ( state->dmr_12_rate_sf[slot][i] == 0x56 ) { //check for appropriate terminology here - Heading, bearing, course, or track? fprintf (stderr, "\n LRRP - Direction %d Degrees", state->dmr_12_rate_sf[slot][i+1] * 2); - sprintf ( state->dmr_lrrp[2], "Dir: %d Deg", state->dmr_12_rate_sf[slot][i+1] * 2); + sprintf ( state->dmr_lrrp[state->currentslot][5], "Dir: %d Deg ", state->dmr_12_rate_sf[slot][i+1] * 2); } } } @@ -1595,17 +1668,35 @@ void ProcessDmrPIHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], u DmrDataBit[j + 7] = (DmrDataByte[i] >> 0) & 0x01; } - - state->payload_algid = DmrDataByte[0]; - state->payload_keyid = DmrDataByte[2]; - state->payload_mi = ( ((DmrDataByte[3]) << 24) + ((DmrDataByte[4]) << 16) + ((DmrDataByte[5]) << 8) + (DmrDataByte[6]) ); - if (1 == 1) //have it always print? + if (state->currentslot == 0) { - fprintf (stderr, "%s ", KYEL); - fprintf (stderr, "\n 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); + state->payload_algid = DmrDataByte[0]; + state->payload_keyid = DmrDataByte[2]; + state->payload_mi = ( ((DmrDataByte[3]) << 24) + ((DmrDataByte[4]) << 16) + ((DmrDataByte[5]) << 8) + (DmrDataByte[6]) ); + if (1 == 1) //have it always print? + { + 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->currentslot == 1) + { + state->payload_algidR = DmrDataByte[0]; + state->payload_keyidR = DmrDataByte[2]; + state->payload_miR = ( ((DmrDataByte[3]) << 24) + ((DmrDataByte[4]) << 16) + ((DmrDataByte[5]) << 8) + (DmrDataByte[6]) ); + if (1 == 1) //have it always print? + { + 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); + } + } + + //test if((IrrecoverableErrors == 0) && CRCCorrect) { @@ -2005,11 +2096,24 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x07 ) //work out why amateur sets sourceid on other codes, but pro only does on whatever */ { - state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; - state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - //state->dmr_color_code = state->color_code; - state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; - state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + if (state->currentslot == 0) + { + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + } + if (state->currentslot == 1) + { + //state->lasttgR = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrcR = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fidR = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_soR = TSVoiceSupFrame->FullLC.ServiceOptions; + } + + } //only set on good CRC value or corrected values //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; @@ -2035,11 +2139,22 @@ void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[19 TSVoiceSupFrame->FullLC.FullLinkControlOpcode != 0x07 ) //work out why amateur sets sourceid on other codes, but pro only does on whatever */ { - state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; - state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - //state->dmr_color_code = state->color_code; - state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; - state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + if (state->currentslot == 0) + { + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + } + if (state->currentslot == 1) + { + //state->lasttgR = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrcR = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fidR = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_soR = TSVoiceSupFrame->FullLC.ServiceOptions; + } } //only set on good CRC value or corrected values //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; @@ -2224,41 +2339,43 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) // let's assume that something has to indicate end of valid characters? //Embedded Alias - if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x03 && TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x08 && opts->payload == 1) + if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x03 && TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x08) { - sprintf (state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control? + sprintf (state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control? for (i = 0; i < 10; i++) { //full range of alphanumerical characters? if ( (LC_DataBytes[i] > 0x19 && LC_DataBytes[i] < 0x7F) ) { - sprintf ( state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3] + - strlen(state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3]) , "%c", LC_DataBytes[i]); + sprintf ( state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3] + + strlen(state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3]) , "%c", LC_DataBytes[i]); } } if (opts->use_ncurses_terminal == 1) { - fprintf (stderr, "%s", KCYN); + fprintf (stderr, "%s", KMAG); if (state->dmr_stereo == 0) { fprintf(stderr, "\n"); } fprintf (stderr, " SLOT %d", state->currentslot+1); - fprintf (stderr, " Embedded Alias Header and Blocks: [%s%s%s%s%s]", state->dmr_callsign[0], state->dmr_callsign[1], state->dmr_callsign[2], state->dmr_callsign[3], state->dmr_callsign[4] ); + fprintf (stderr, " Embedded Alias Header and Blocks: [%s%s%s%s%s]", state->dmr_callsign[state->currentslot][0], + state->dmr_callsign[state->currentslot][1], state->dmr_callsign[state->currentslot][2], + state->dmr_callsign[state->currentslot][3], state->dmr_callsign[state->currentslot][4] ); fprintf (stderr, "%s ", KNRM); } } if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode == 0x08 && opts->payload == 1) //Embedded GPS { - sprintf (state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control? + sprintf (state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control? for (i = 0; i < 10; i++) { //full range of alphanumerical characters? if ( (LC_DataBytes[i] > 0x19 && LC_DataBytes[i] < 0x7F) ) { - sprintf ( state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3] + - strlen(state->dmr_callsign[TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3]) , "%c", LC_DataBytes[i]); + sprintf ( state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3] + + strlen(state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3]) , "%c", LC_DataBytes[i]); } } if (opts->use_ncurses_terminal == 1) @@ -2269,7 +2386,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) fprintf(stderr, "\n"); } fprintf (stderr, " SLOT %d", state->currentslot+1); - fprintf (stderr, " Embedded GPS: [%s]", state->dmr_callsign[5] ); + fprintf (stderr, " Embedded GPS: [%s]", state->dmr_callsign[state->currentslot][5] ); fprintf (stderr, "%s ", KNRM); } } @@ -2277,7 +2394,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) /* Print the destination ID (TG) and the source ID */ if((IrrecoverableErrors == 0) && CRCCorrect) { - if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x09) //other opcodes may convey callsigns, names, etc. was > 0x07 + if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x08) //other opcodes may convey callsigns, names, etc. was > 0x07 { fprintf (stderr, "%s", KGRN); if (state->dmr_stereo == 0) @@ -2289,16 +2406,30 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); fprintf (stderr, "%s ", KNRM); //fprintf(stderr, "(CRC OK ) "); - state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; - state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + if (state->currentslot == 0) + { + state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + } + if (state->currentslot == 1) + { + state->lasttgR = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrcR = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fidR = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_soR = TSVoiceSupFrame->FullLC.ServiceOptions; + } //state->dmr_color_code = state->color_code; //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; } //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; - state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; //state->dmr_color_code = state->color_code; } else if(IrrecoverableErrors == 0) @@ -2307,7 +2438,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) //fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); //fprintf(stderr, "RAS (FEC OK/CRC ERR)"); //voice burst //or set if was corrected - if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x07) + if (TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x04 || TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x08) //7, or 8? { fprintf (stderr, "%s", KGRN); if (state->dmr_stereo == 0) @@ -2320,16 +2451,30 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) fprintf (stderr, "%s ", KRED); fprintf(stderr, "RAS (FEC OK/CRC ERR) "); fprintf (stderr, "%s ", KNRM); - state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; - state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + if (state->currentslot == 0) + { + state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + } + if (state->currentslot == 1) + { + state->lasttgR = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrcR = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_color_code = state->color_code; + state->dmr_fidR = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_soR = TSVoiceSupFrame->FullLC.ServiceOptions; + } //state->dmr_color_code = state->color_code; //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; } //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; - state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; - state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; //state->dmr_color_code = state->color_code; } else {} //fprintf(stderr, "\n(FEC FAIL/CRC ERR)"); diff --git a/src/dmr_voice.c b/src/dmr_voice.c index 85f38b5..e7322c3 100644 --- a/src/dmr_voice.c +++ b/src/dmr_voice.c @@ -129,6 +129,15 @@ void processDMRvoice (dsd_opts * opts, dsd_state * state) } 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; diff --git a/src/dsd_frame.c b/src/dsd_frame.c index 0992492..38dc782 100644 --- a/src/dsd_frame.c +++ b/src/dsd_frame.c @@ -177,9 +177,12 @@ processFrame (dsd_opts * opts, dsd_state * state) //Start DMR Types else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC { + //disable so radio id doesn't blink in and out during ncurses and aggressive_framesync state->nac = 0; - state->lastsrc = 0; - state->lasttg = 0; + //state->lastsrc = 0; + //state->lasttg = 0; + //state->lastsrcR = 0; + //state->lasttgR = 0; if (opts->errorbars == 1) { if (opts->verbose > 0) diff --git a/src/dsd_main.c b/src/dsd_main.c index 361ba6d..1e2142b 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -93,8 +93,11 @@ noCarrier (dsd_opts * opts, dsd_state * state) sprintf (state->ftype, " "); state->errs = 0; state->errs2 = 0; + //should I disable this or not? state->lasttg = 0; state->lastsrc = 0; + state->lasttgR = 0; + state->lastsrcR = 0; state->lastp25type = 0; state->repeat = 0; state->nac = 0; @@ -213,7 +216,8 @@ initOpts (dsd_opts * opts) opts->EncryptionMode = MODE_UNENCRYPTED; opts->inverted_dpmr = 0; - opts->dmr_stereo = 0; //change currently only in dsd_frame.c + opts->dmr_stereo = 0; + opts->aggressive_framesync = 1; //more aggressive to combat wonk wonk voice decoding } void @@ -346,25 +350,51 @@ initState (dsd_state * state) state->dpmr_caller_id = 0; state->dpmr_target_id = 0; - state->payload_mi = 0; + state->payload_mi = 0; + state->payload_miR = 0; state->payload_mfid = 0; + state->payload_mfidR = 0; state->payload_algid = 0; + state->payload_algidR = 0; state->payload_keyid = 0; + state->payload_keyidR = 0; sprintf (state->dmr_branding, " "); - sprintf (state->dmr_callsign[0], ""); - sprintf (state->dmr_callsign[1], ""); - sprintf (state->dmr_callsign[2], ""); - sprintf (state->dmr_callsign[3], ""); - sprintf (state->dmr_lrrp[0], ""); - sprintf (state->dmr_lrrp[1], ""); - sprintf (state->dmr_lrrp[2], ""); - sprintf (state->dmr_lrrp[3], ""); - sprintf (state->dmr_lrrp[4], ""); - sprintf (state->dmr_lrrp[5], ""); + sprintf (state->dmr_callsign[0][0], ""); + sprintf (state->dmr_callsign[0][1], ""); + sprintf (state->dmr_callsign[0][2], ""); + sprintf (state->dmr_callsign[0][3], ""); + sprintf (state->dmr_callsign[0][4], ""); + sprintf (state->dmr_callsign[0][5], ""); + sprintf (state->dmr_callsign[1][0], ""); + sprintf (state->dmr_callsign[1][1], ""); + sprintf (state->dmr_callsign[1][2], ""); + sprintf (state->dmr_callsign[1][3], ""); + sprintf (state->dmr_callsign[1][4], ""); + sprintf (state->dmr_callsign[1][5], ""); + sprintf (state->dmr_lrrp[0][0], ""); + sprintf (state->dmr_lrrp[0][1], ""); + sprintf (state->dmr_lrrp[0][2], ""); + sprintf (state->dmr_lrrp[0][3], ""); + sprintf (state->dmr_lrrp[0][4], ""); + sprintf (state->dmr_lrrp[0][5], ""); + sprintf (state->dmr_lrrp[1][0], ""); + sprintf (state->dmr_lrrp[1][1], ""); + sprintf (state->dmr_lrrp[1][2], ""); + sprintf (state->dmr_lrrp[1][3], ""); + sprintf (state->dmr_lrrp[1][4], ""); + sprintf (state->dmr_lrrp[1][5], ""); state->K = 0; state->dmr_stereo = 0; + state->dmrburstL = 17; //initialize at higher value than possible + state->dmrburstR = 17; //17 in char array is set for ERR + state->dmr_so = 0; + state->dmr_soR = 0; + state->dmr_fid = 0; + state->dmr_fidR = 0; + + memset(state->dstarradioheader, 0, 41); #ifdef TRACE_DSD state->debug_sample_index = 0; @@ -452,11 +482,14 @@ usage () fprintf (stderr," -M Min/Max buffer size for QPSK decision point tracking\n"); fprintf (stderr," (default=15)\n"); fprintf (stderr," -n Reset P25 Heuristics and initState variables on mixed signal decoding\n"); - fprintf (stderr," Helps when decoding mixed signal types at same time\n"); - fprintf (stderr," (WiP! May Cause Slow Memory Leak - Experimental)\n"); + fprintf (stderr," Helps when decoding mixed signal types (P25P1) at same time\n"); + fprintf (stderr," (WiP! May Cause Slow Memory Leak or System Hang - Experimental)\n"); fprintf (stderr," -T Enable DMR TDMA Stereo Voice (Two Slot Dual Voices)\n"); fprintf (stderr," This feature will open two streams for slot 1 voice and slot 2 voice\n"); - fprintf (stderr," (WiP! May Cause DMR sync break resync issues)\n"); + fprintf (stderr," May Cause Skipping or sync issues if bad signal/errors\n"); + fprintf (stderr," -F Enable DMR TDMA Stereo Passive Frame Sync\n"); + fprintf (stderr," This feature will attempt to resync less often due to excessive voice errors\n"); + fprintf (stderr," Use if skipping occurs, but may cuase wonky audio due to loss of good sync\n"); fprintf (stderr," -Z Log MBE Payload to console\n"); fprintf (stderr,"\n"); fprintf (stderr,"Report bugs to: https://github.com/lwvmobile/dsd-fme/issues \n"); @@ -697,12 +730,12 @@ main (int argc, char **argv) initOpts (&opts); initState (&state); - InitAllFecFunction(); //HERE + InitAllFecFunction(); exitflag = 0; signal (SIGINT, sigfun); - while ((c = getopt (argc, argv, "haep:P:qstv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:Y:K:NWrlZT")) != -1) + while ((c = getopt (argc, argv, "haep:P:qstv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:Y:K:NWrlZTF")) != -1) { opterr = 0; switch (c) @@ -824,6 +857,14 @@ main (int argc, char **argv) fprintf (stderr, "%s", KRED); fprintf (stderr,"Experimental DMR Stereo Sync and Functionality. WIP!\n"); fprintf (stderr,"DMR Stereo will disable WAV and MBE file saving!\n"); + fprintf (stderr,"Also consider using -F if playback is too choppy!\n"); + fprintf (stderr, "%s", KNRM); + break; + + case 'F': + opts.aggressive_framesync = 0; + fprintf (stderr, "%s", KRED); + fprintf (stderr,"DMR Stereo Aggressive Resync Disabled!\n"); fprintf (stderr, "%s", KNRM); break; @@ -878,8 +919,9 @@ main (int argc, char **argv) opts.reset_state = 1; fprintf (stderr, "%s", KRED); fprintf (stderr,"Enabling Automatic Reset of P25 states\n"); - fprintf (stderr," -Helps with multiple signal type decoding\n"); - fprintf (stderr," -(WiP! May cause slow memory leak)\n"); + //fprintf (stderr," -Helps with multiple signal type decoding\n"); + //fprintf (stderr," -(WiP! May cause slow memory leak)\n"); + fprintf (stderr," -(Disabled until memory leak issues can be resolved!)\n"); fprintf (stderr, "%s", KNRM); break; case 'w': @@ -968,9 +1010,9 @@ main (int argc, char **argv) opts.frame_dmr = 0; opts.frame_dpmr = 0; opts.frame_provoice = 0; - opts.mod_gfsk = 0; opts.mod_c4fm = 1; opts.mod_qpsk = 0; + opts.mod_gfsk = 0; state.rf_mod = 0; // sprintf (opts.output_name, "P25P1"); fprintf (stderr,"Decoding only P25 Phase 1 frames.\n"); @@ -997,27 +1039,27 @@ main (int argc, char **argv) fprintf (stderr,"Setting symbol rate to 2400 / second\n"); fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n"); } - else if (optarg[0] == 'y') - { - opts.frame_dstar = 0; - opts.frame_x2tdma = 0; - opts.frame_p25p1 = 0; - opts.frame_nxdn48 = 0; - opts.frame_nxdn96 = 0; - opts.frame_dmr = 0; - opts.frame_dpmr = 0; - opts.frame_provoice = 0; - opts.frame_ysf = 1; - state.samplesPerSymbol = 10; - //state.symbolCenter = 10; - opts.mod_c4fm = 1; - opts.mod_qpsk = 0; - opts.mod_gfsk = 0; - state.rf_mod = 0; - sprintf (opts.output_name, "YSF"); - //opts.symboltiming = 2400; //NXDN48 uses 2400 symbol rate - fprintf (stderr,"Setting symbol rate to 2400 / second\n"); - fprintf (stderr,"Decoding only YSF frames.\nNot working yet!\n"); + else if (optarg[0] == 'y') + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 0; + opts.frame_ysf = 1; + state.samplesPerSymbol = 10; + //state.symbolCenter = 10; + opts.mod_c4fm = 1; + opts.mod_qpsk = 0; + opts.mod_gfsk = 0; + state.rf_mod = 0; + sprintf (opts.output_name, "YSF"); + //opts.symboltiming = 2400; //NXDN48 uses 2400 symbol rate + fprintf (stderr,"Setting symbol rate to 2400 / second\n"); + fprintf (stderr,"Decoding only YSF frames.\nNot working yet!\n"); } else if (optarg[0] == 'n') { diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 8de1877..00541d8 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -30,20 +30,20 @@ int reset = 0; char c; //getch key -int tg; //last tg +int tg; +int tgR; int tgn; -int rd; //last rid -int rn; //last ran -int nc; //nac -int src; //last rid? double? -int lls = -1; //last last sync type -int dcc = -1; //initialize with 0 to prevent core dump +int rd; +int rdR; +int rn; +int nc; +int src; +int lls = -1; +int dcc = -1; int i = 0; -char * s0last = " "; -char * s1last = " "; char versionstr[25]; -char lrrp[6][9999]; //local lrrp storage -unsigned long long int call_matrix[33][6]; //0 - sync type; 1 - tg/ran; 2 - rid; 3 - slot; 4 - cc; 5 - time(NULL) ; +unsigned long long int call_matrix[33][6]; + char * FM_bannerN[9] = { " CTRL + C twice to exit", " ██████╗ ██████╗██████╗    ███████╗███╗ ███╗███████╗", @@ -55,7 +55,6 @@ char * FM_bannerN[9] = { "https://github.com/lwvmobile/dsd-fme/tree/pulseaudio " }; -//need to double check these sync types, not quite matching up with frame_sync char * SyncTypes[36] = { "+P25P1", "-P25P1", @@ -65,16 +64,16 @@ char * SyncTypes[36] = { "-X2TDMA VOICE", "+DSTAR", "-DSTAR", - "+NXDN VOICE", //8 - "-NXDN VOICE", //9 - "+DMR DATA", //10 - "-DMR DATA", //11 - "+DMR VOICE", //12 - "-DMR VOICE", //13 - "+PROVOICE", //14 - "-PROVOICE", //15 - "+NXDN DATA", //16 - "-NXDN DATA", //17 + "+NXDN VOICE", + "-NXDN VOICE", + "+DMR DATA", + "-DMR DATA", + "+DMR VOICE", + "-DMR VOICE", + "+PROVOICE", + "-PROVOICE", + "+NXDN DATA", + "-NXDN DATA", "+DSTAR HD", "-DSTAR HD", "+dPMR", @@ -92,6 +91,27 @@ char * SyncTypes[36] = { "DMR MS VOICE", "DMR MS DATA", "DMR RC DATA" +}; + +char * DMRBusrtTypes[18] = { + "PI Header ", + "VOICE LC HDR ", + "TLC ", + "CSBK ", + "MBC Header ", + "MBC Cont ", + "DATA Header ", + "RATE 1/2 DATA ", + "RATE 3/4 DATA ", + "Slot Idle ", + "Rate 1 DATA ", + "ERR ", //These values for ERR may be Reserved for use in future? + "ERR ", + "ERR ", + "ERR ", + "ERR ", + "Voice ", //Using 16 for Voice since its higher than possible value? + "INITIAL " //17 is assigned on start up }; @@ -125,48 +145,139 @@ void ncursesOpen () noecho(); cbreak(); - //fprintf (stderr, "Opening NCurses Terminal. \n"); - } void ncursesPrinter (dsd_opts * opts, dsd_state * state) { int level; - - //level = (int) state->max / 164; level = 0; //start each cycle with 0 - erase(); - //disabling until wide support can be built for LM, etc. $(ncursesw5-config --cflags --libs) - printw ("%s \n", FM_bannerN[0]); //top line in white - attron(COLOR_PAIR(4)); - for (short int i = 1; i < 7; i++) //following lines in cyan - { - printw("%s \n", FM_bannerN[i]); - } - attroff(COLOR_PAIR(4)); - printw ("--Build Info------------------------------------------------------------------\n"); - printw ("| %s \n", FM_bannerN[7]); //http link - printw ("| Digital Speech Decoder: Florida Man Edition\n"); - printw ("| Github Build Version: %s \n", GIT_TAG); - printw ("| mbelib version %s\n", versionstr); - //printw ("| Press CTRL+C twice to exit\n"); - printw ("------------------------------------------------------------------------------\n"); + //Variable reset/set section + + //carrier reset if (state->carrier == 0) //reset these to 0 when no carrier { - state->payload_algid = 0; - state->payload_keyid = 0; + state->payload_algid = 0; + state->payload_keyid = 0; + state->payload_keyidR = 0; + state->payload_algidR = 0; //state->payload_mfid = 0; - state->payload_mi = 0; + state->payload_mi = 0; + state->payload_miR = 0; state->nxdn_key = 0; state->nxdn_cipher_type = 0; - + //memset(state->dstarradioheader, 0, 41); sprintf(state->dmr_branding, " "); } - if ( (lls == 14 || lls == 15) && (time(NULL) - call_matrix[9][5] > 5) && state->carrier == 1) //honestly have no idea how to do this for pV, just going time based? only update on carrier == 1. + //set lls sync types + if (state->synctype >= 0 && state->synctype < 35) //not sure if this will be okay + { + lls = state->synctype; + } + + //reset DMR alias block and embedded gps if burst type is not 16 or carrier drop + if (state->dmrburstL != 16 || state->carrier == 0) + { + for (short i = 0; i < 6; i++) + { + sprintf (state->dmr_callsign[0][i], ""); + } + } + if (state->dmrburstR != 16 || state->carrier == 0) + { + for (short i = 0; i < 6; i++) + { + sprintf (state->dmr_callsign[1][i], ""); + } + } + + //reset DMR LRRP when call is active on current slot if burst type is not data or carrier drop + if (state->dmrburstL == 16 || state->carrier == 0) + { + for (short i = 0; i < 6; i++) + { + //sprintf (state->dmr_lrrp[0][i], ""); + } + } + if (state->dmrburstR == 16 || state->carrier == 0) + { + for (short i = 0; i < 6; i++) + { + //sprintf (state->dmr_lrrp[1][i], ""); + } + } + + //NXDN + if (state->nxdn_last_rid > 0 && state->nxdn_last_rid != src); + { + src = state->nxdn_last_rid; + } + if (state->nxdn_last_ran > -1 && state->nxdn_last_ran != rn); + { + rn = state->nxdn_last_ran; + } + if (state->nxdn_last_tg > 0 && state->nxdn_last_tg != tgn); + { + tgn = state->nxdn_last_tg; + } + + //DMR CC + if (state->color_code_ok && state->dmr_color_code != -1 && (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33) ) + { + dcc = state->dmr_color_code; + } + + //DMR SRC + if ( (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32) ) + { + if (state->dmrburstL == 16 && state->lastsrc > 0) //state->currentslot == 0 && + { + rd = state->lastsrc; + } + + if (state->dmrburstR == 16 && state->lastsrcR > 0) //state->currentslot == 1 && + { + rdR = state->lastsrcR; + } + //move to seperate P25 version plz + opts->p25enc = 0; + } + + //DMR TG + if ( (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32) ) + { + if (state->dmrburstL == 16 && state->lasttg > 0) //state->currentslot == 0 && + { + tg = state->lasttg; + } + + if (state->dmrburstR == 16 && state->lastsrcR > 0) //state->currentslot == 1 && + { + tgR = state->lasttgR; + } + opts->p25enc = 0; + } + + //P25 + if (state->nac > 0) + { + nc = state->nac; + } + if ( state->lasttg > 0 && (lls == 0 || lls == 1) ) + { + tg = state->lasttg; + } + if ( state->lastsrc > 0 && (lls == 0 || lls == 1) ) + { + rd = state->lastsrc; + } + + //Call History Matrix Shuffling + //ProVoice + if ( (lls == 14 || lls == 15) && (time(NULL) - call_matrix[9][5] > 5) && state->carrier == 1) { for (short int k = 0; k < 9; k++) { @@ -184,12 +295,33 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) call_matrix[9][3] = 1; call_matrix[9][4] = 1; call_matrix[9][5] = time(NULL); - //nowN = time(NULL); + } - //if ( (state->nxdn_last_rid != src && src > 0) || (state->nxdn_last_ran != rn && rn > 0) ) //find condition to make this work well, probably if last != local last variables - //if ( (call_matrix[9][2] != src && src > 0) || (call_matrix[9][1] != rn && rn > 0) ) //NXDN working well now with this, updates immediately and only once - if ( call_matrix[9][2] != src && src > 0 && rn > -1 ) //NXDN working well now with this, updates immediately and only once + //D-STAR, work on adding the headers later + if ( (lls == 6 || lls == 7 || lls == 18 || lls == 19) && (time(NULL) - call_matrix[9][5] > 5) && state->carrier == 1) + { + for (short int k = 0; k < 9; k++) + { + call_matrix[k][0] = call_matrix[k+1][0]; + call_matrix[k][1] = call_matrix[k+1][1]; + call_matrix[k][2] = call_matrix[k+1][2]; + call_matrix[k][3] = call_matrix[k+1][3]; + call_matrix[k][4] = call_matrix[k+1][4]; + call_matrix[k][5] = call_matrix[k+1][5]; + } + + call_matrix[9][0] = lls; + call_matrix[9][1] = 1; + call_matrix[9][2] = 1; + call_matrix[9][3] = 1; + call_matrix[9][4] = 1; + call_matrix[9][5] = time(NULL); + + } + + //NXDN + if ( call_matrix[9][2] != src && src > 0 && rn > -1 ) { for (short int k = 0; k < 9; k++) { @@ -209,17 +341,11 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } - //if (state->dmr_color_code != dcc && (lls == 11 || lls == 13 || lls == 10 || lls == 12) ) //DMR Voice + last two is data - //if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR - if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 12 || lls == 13 || lls == 32) ) //DMR, needs work + //DMR MS + if ( call_matrix[9][2] != rd && (lls == 32 || lls == 33 || lls == 34) ) { - //reset amateur signs on radio id change - for (short i = 0; i < 5; i++) - { - sprintf (state->dmr_callsign[i], ""); - } - //dcc = state->dmr_color_code; - for (short int k = 0; k < 9; k++) + + for (short int k = 0; k < 10; k++) { call_matrix[k][0] = call_matrix[k+1][0]; call_matrix[k][1] = call_matrix[k+1][1]; @@ -232,22 +358,40 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) call_matrix[9][0] = lls; call_matrix[9][1] = tg; call_matrix[9][2] = rd; - call_matrix[9][3] = 0; + call_matrix[9][3] = 1; //hard set slot number call_matrix[9][4] = dcc; call_matrix[9][5] = time(NULL); - //i++; + } - /* - //needs work, look at saving just values to part of the state->dmr_lrrp array, or rework for int values - if ( (strcmp(state->dmr_lrrp[3], lrrp[3]) != 0) && (lls == 10 || lls == 11) ) //DMR Data LRRP + + //DMR BS Slot 1 - matrix 0-4 + if ( call_matrix[4][2] != rd && (lls == 12 || lls == 13 || lls == 10 || lls == 11) ) { - sprintf (lrrp[1], state->dmr_lrrp[1]); - sprintf (lrrp[2], state->dmr_lrrp[2]); - sprintf (lrrp[3], state->dmr_lrrp[3]); - //lrrp[1] = state->dmr_lrrp[1]; - //lrrp[2] = state->dmr_lrrp[2]; - //lrrp[3] = state->dmr_lrrp[3]; - for (short int k = 0; k < 9; k++) + + for (short int k = 0; k < 4; k++) + { + call_matrix[k][0] = call_matrix[k+1][0]; + call_matrix[k][1] = call_matrix[k+1][1]; + call_matrix[k][2] = call_matrix[k+1][2]; + call_matrix[k][3] = call_matrix[k+1][3]; + call_matrix[k][4] = call_matrix[k+1][4]; + call_matrix[k][5] = call_matrix[k+1][5]; + } + + call_matrix[4][0] = lls; + call_matrix[4][1] = tg; + call_matrix[4][2] = rd; + call_matrix[4][3] = 1; //hard set slot number + call_matrix[4][4] = dcc; + call_matrix[4][5] = time(NULL); + + } + + //DMR BS Slot 2 - matrix 5-9 + if ( call_matrix[9][2] != rdR && (lls == 12 || lls == 13 || lls == 10 || lls == 11) ) + { + + for (short int k = 5; k < 9; k++) { call_matrix[k][0] = call_matrix[k+1][0]; call_matrix[k][1] = call_matrix[k+1][1]; @@ -258,17 +402,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } call_matrix[9][0] = lls; - call_matrix[9][1] = (unsigned long long int)&lrrp[3]; - call_matrix[9][2] = (unsigned long long int)&lrrp[1]; - call_matrix[9][3] = (unsigned long long int)&lrrp[2]; + call_matrix[9][1] = tgR; + call_matrix[9][2] = rdR; + call_matrix[9][3] = 2; //hard set slot number call_matrix[9][4] = dcc; call_matrix[9][5] = time(NULL); - //i++; - } - */ - //if ( (lls == 0 || lls == 1) && state->lastsrc != rd && state->lastsrc > 0) //find condition to make this work well, probably if last != local last variables - if ( (lls == 0 || lls == 1) && call_matrix[9][2] != rd && nc > 0 && tg > 0) //P25 + } + + //P25 + if ( (lls == 0 || lls == 1) && call_matrix[9][2] != rd && nc > 0 && tg > 0) { for (short int k = 0; k < 9; k++) { @@ -286,15 +429,30 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) call_matrix[9][3] = 0; call_matrix[9][4] = nc; call_matrix[9][5] = time(NULL); - //i++; + } - //printw ("Time: "); - //printw ("%s \n", getTimeN()); + + //Start Printing Section + erase(); + printw ("%s \n", FM_bannerN[0]); //top line in white + attron(COLOR_PAIR(4)); + for (short int i = 1; i < 7; i++) //following lines in cyan + { + printw("%s \n", FM_bannerN[i]); + } + attroff(COLOR_PAIR(4)); + printw ("--Build Info------------------------------------------------------------------\n"); + printw ("| %s \n", FM_bannerN[7]); //http link + printw ("| Digital Speech Decoder: Florida Man Edition\n"); + printw ("| Github Build Version: %s \n", GIT_TAG); + printw ("| mbelib version %s\n", versionstr); + //printw ("| Press CTRL+C twice to exit\n"); + printw ("------------------------------------------------------------------------------\n"); attron(COLOR_PAIR(4)); printw ("--Input Output----------------------------------------------------------------\n"); if (opts->audio_in_type == 0) { - printw ("| Pulse Audio Input [%i] kHz [%i] Channel\n", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels); + printw ("| Pulse Audio Input: [%i] kHz [%i] Channel\n", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels); } if (opts->audio_in_type == 1) { @@ -311,17 +469,17 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } if (opts->audio_out_type == 0) { - printw ("| Pulse Audio Output [%i] kHz [%i] Channel\n", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels); + printw ("| Pulse Audio Output: [%i] kHz [%i] Channel\n", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels); } if (opts->monitor_input_audio == 1) { - printw ("| Monitoring Source Audio when carrier present and No Sync Detected\n"); + printw ("| Monitoring Source Audio when Carrier Present and No Sync Detected\n"); } - if (opts->mbe_out_dir[0] != 0) + if (opts->mbe_out_dir[0] != 0 && opts->dmr_stereo == 0) { printw ("| Writing MBE data files to directory %s\n", opts->mbe_out_dir); } - if (opts->wav_out_file[0] != 0) + if (opts->wav_out_file[0] != 0 && opts->dmr_stereo == 0) { printw ("| Appending Audio WAV to file %s\n", opts->wav_out_file); } @@ -340,83 +498,52 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) reset = 0; } - printw ("--Audio Decode----------------------------------------------------------------\n"); - printw ("| Decoding: %s \n", opts->output_name); + printw ("| Decoding: [%s] \n", opts->output_name); printw ("| In Level: [%3i%%] \n", level); - //printw ("| Voice Error: [%i][%i] \n| Error Bars: [%s] \n", state->errs, state->errs2, state->err_str); // - printw ("| Voice Error: [%i][%i] \n", state->errs, state->errs2); + if (opts->dmr_stereo == 0) + { + printw ("| Voice Error: [%i][%i] \n", state->errs, state->errs2); + } + + if (opts->dmr_stereo == 1) + { + printw ("| Voice ErrS1: [%i][%i] \n", state->errs, state->errs2); + printw ("| Voice ErrS2: [%i][%i] \n", state->errsR, state->errs2R); + } printw ("------------------------------------------------------------------------------\n"); - printw ("--Call Info-------------------------------------------------------------------\n"); - //NXDN Ciper Types - //switch(CipherType) - //{ - // case 0: Ptr = ""; break; /* Non-ciphered mode / clear call */ - // case 1: Ptr = "Scrambler"; break; - // case 2: Ptr = "DES"; break; - // case 3: Ptr = "AES"; break; - // default: Ptr = "Unknown Cipher Type"; break; - //} - /* - if (state->lastsynctype != -1) //not sure if this will be okay + //DSTAR...what a pain... + if (lls == 6 || lls == 7 || lls == 18 || lls == 19) { - lls = state->lastsynctype; - } - */ - if (state->synctype > 0 && state->synctype < 34) //not sure if this will be okay - { - lls = state->synctype; - } - if (state->nxdn_last_rid > 0 && state->nxdn_last_rid != src); - { - src = state->nxdn_last_rid; - } - if (state->nxdn_last_ran > -1 && state->nxdn_last_ran != rn); - { - rn = state->nxdn_last_ran; - } - if (state->nxdn_last_tg > 0 && state->nxdn_last_tg != tgn); - { - tgn = state->nxdn_last_tg; + if (state->dstarradioheader[3] != 0) // + { + printw ("| RPT 2: [%c%c%c%c%c%c%c%c] ", state->dstarradioheader[3], state->dstarradioheader[4], + state->dstarradioheader[5], state->dstarradioheader[6], state->dstarradioheader[7], state->dstarradioheader[8], + state->dstarradioheader[9], state->dstarradioheader[10]); + printw ("RPT 1: [%c%c%c%c%c%c%c%c] \n| ", state->dstarradioheader[11], state->dstarradioheader[12], + state->dstarradioheader[13], state->dstarradioheader[14], state->dstarradioheader[15], state->dstarradioheader[16], + state->dstarradioheader[17], state->dstarradioheader[18]); + printw ("YOUR: [%c%c%c%c%c%c%c%c] ", state->dstarradioheader[19], state->dstarradioheader[20], + state->dstarradioheader[21], state->dstarradioheader[22], state->dstarradioheader[23], state->dstarradioheader[24], + state->dstarradioheader[25], state->dstarradioheader[26]); + printw ("MY: [%c%c%c%c%c%c%c%c] [%c%c%c%c]\n", state->dstarradioheader[27], + state->dstarradioheader[28], state->dstarradioheader[29], state->dstarradioheader[30], state->dstarradioheader[31], + state->dstarradioheader[32], state->dstarradioheader[33], state->dstarradioheader[34], state->dstarradioheader[35], + state->dstarradioheader[36], state->dstarradioheader[37], state->dstarradioheader[38]); + } } - //if (state->dmr_color_code > 0 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? - //if (state->color_code > -1 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? - if (state->color_code_ok && (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33) ) //DMR, needs work - - { - dcc = state->dmr_color_code; - //dcc = state->color_code; - } - - if (state->lastsrc > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1 || lls == 32)) //DMR Voice and P25P1 - { - rd = state->lastsrc; - opts->p25enc = 0; - } - - if (state->lasttg > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1 || lls == 32)) //DMR Voice and P25P1 - { - tg = state->lasttg; - opts->p25enc = 0; - } - - //if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //change this to NXDN syncs later on + //NXDN if (lls == 8 || lls == 9 || lls == 16 || lls == 17) { printw ("| RAN: [%2d] ", rn); printw ("TID: [%4d] ", tgn); - //printw ("| RID: [%d] \n", src); printw ("RID: [%4d] \n| ALG: [0x%02X] KEY [0x%02X] ", src, state->nxdn_cipher_type, state->nxdn_key); if (state->carrier == 1) { printw("%s ", state->nxdn_call_type); - //attron(COLOR_PAIR(4)); - //printw ("No Encryption "); - //attroff(COLOR_PAIR(4)); - //attron(COLOR_PAIR(3)); } if (state->nxdn_cipher_type == 0x1 && state->carrier == 1) { @@ -448,31 +575,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } //printw("%s ", state->nxdn_call_type); printw("\n"); - - } - //printw ("Error?: [%i] [%i] \n", state->errs, state->errs2); //what are these? - /* //wasn't we already doing this twice - if (state->lasttg > 0 && state->lastsrc > 0) + //P25 + if (lls == 0 || lls == 1) { - tg = state->lasttg; - rd = state->lastsrc; - } - */ - if (state->nac > 0) - { - nc = state->nac; - } - if ((lls == 0 || lls == 1)) //1 for P25 P1 Audio - { - //printw("| TID:[%i] | RID:[%i] \n", tg, rd); - //printw("| NAC: [0x%X] \n", nc); printw("| TID:[%8i] RID:[%8i] ", tg, rd); printw("NAC: [0x%3X] \n", nc); printw("| ALG:[0x%02X] ", state->payload_algid); printw(" KEY:[0x%04X] ", state->payload_keyid); - printw(" MFID: [0x%02X] ", state->payload_mfid); //no way of knowing if this is accurate info yet + printw(" MFID: [0x%02X] ", state->payload_mfid); //no way of knowing if this is accurate info yet if (state->payload_algid != 0x80 && state->payload_algid != 0x0 && state->carrier == 1) { attron(COLOR_PAIR(2)); @@ -481,182 +593,218 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) attron(COLOR_PAIR(3)); } printw("\n"); - //printw("| System Type: %s \n", ALGIDS[state->payload_keyid] ); - } - //if (state->lastsynctype == 12 || state->lastsynctype == 13) //DMR Voice Types - if (lls == 12 || lls == 13 || lls == 32) //DMR Voice Types - { - //printw ("| DCC: [%i] FID: [%02X]\n", dcc, state->dmr_fid); - //attron(COLOR_PAIR(3)); - printw ("| DCC: [%2i] FID: [%02X] SOP: [%02X] ", dcc, state->dmr_fid, state->dmr_so); - //if(state->payload_mi == 0 && state->dmr_so & 0x40) - if(state->payload_mi == 0 && (state->dmr_so & 0xCF) == 0x40) //4F or CF mask? - { - attron(COLOR_PAIR(5)); - printw ("**BP** "); - //printw ("0x%X", state->payload_algid); - attroff(COLOR_PAIR(5)); - attron(COLOR_PAIR(3)); - } - //if(state->payload_keyid > 0 && state->dmr_so & 0x40) - if(state->payload_keyid > 0 && (state->dmr_so & 0xCF) == 0x40) - { - attron(COLOR_PAIR(5)); - printw (" ALG: [0x%02X] KEY [0x%02X] MI [0x%08X]", state->payload_algid, state->payload_keyid, state->payload_mi); - //printw ("0x%X", state->payload_algid); - attroff(COLOR_PAIR(5)); - attron(COLOR_PAIR(3)); - } - //if(state->K > 0 && state->payload_keyid == 0 && state->dmr_so & 0x40) - if(state->K > 0 && (state->dmr_so & 0xCF) == 0x40) - { - attron(COLOR_PAIR(5)); - printw ("BPK [%3lld]", state->K); - attroff(COLOR_PAIR(5)); - attron(COLOR_PAIR(3)); - } - - printw("\n"); - printw ("| TID: [%8i] RID: [%8i]", tg, rd); - - if (1 == 1) //figure out what to put here later on for amateur call signs - { - printw (" ["); - for (short i = 0; i < 5; i++) - { - printw ("%s", state->dmr_callsign[i]); - } - printw ("]"); - } - //if(state->dmr_so & 0x80) //1000 0000 //prints emergency on some amateru DMR repeaters with 0x8E SOP code, need to investigate - if(state->dmr_so == 0x80) - { - attron(COLOR_PAIR(2)); - printw (" **Emergency** "); - attroff(COLOR_PAIR(2)); - attron(COLOR_PAIR(3)); - } - - //if(state->dmr_so & 0x40) //0100 0000 - if(state->dmr_so == 0x40) //0100 0000 - { - attron(COLOR_PAIR(2)); - printw (" **ENC** "); - //printw ("0x%X", state->payload_algid); - attroff(COLOR_PAIR(2)); - attron(COLOR_PAIR(3)); - if (state->K == 0) - { - //opts->p25enc = 1; //just testing for now - } - - } - - //if(state->dmr_so & 0x30) //0010 0000 - if(state->dmr_so == 0x30) //0010 0000 - { - attron(COLOR_PAIR(2)); - printw (" **Private Call** "); - attroff(COLOR_PAIR(2)); - attron(COLOR_PAIR(3)); - } - printw("\n"); - //printw ("%s ", state->slot0light); - printw ("| SLOT 1 "); - if (state->currentslot == 0) //find out how to tell when slot0 has voice - { - s0last = "Voice"; - //printw("Voice"); - } - printw ("%s ", s0last); - - //printw ("%s ", state->slot1light); - printw ("SLOT 2 "); - if (state->currentslot == 1) //find out how to tell when slot1 has voice - { - s1last = "Voice"; - //printw("Voice"); - } - printw ("%s \n", s1last); } - - //if (state->lastsynctype == 10 || state->lastsynctype == 11) //DMR Data Types - if (lls == 10 || lls == 11 || lls == 33) //DMR Data Types + //DMR BS/MS Voice and Data Types + if ( lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33 || lls == 34) { - //printw ("| DCC: [%i]\n", dcc); - printw ("| DCC: [%2i] FID: [%02X] SOP: [%02X] \n", dcc, state->dmr_fid, state->dmr_so); - printw ("| TID: [%8i] RID: [%8i]", tg, rd); - - if (1 == 1) //figure out what to put here later on for amateur call signs + printw ("| "); + if (lls < 30) { - printw (" ["); - for (short i = 0; i < 5; i++) - { - printw ("%s", state->dmr_callsign[i]); - } - printw ("]"); + printw ("DMR BS - DCC: [%02i] ", dcc); } - //does this need to be in DATA type? - /* - if(state->dmr_so & 0x80) + else + { + printw ("DMR MS - DCC: [%02i] ", dcc); + } + + printw ("\n"); + //Slot 1 [0] + printw ("| SLOT 1 - "); + if (state->dmrburstL != 16 && state->carrier == 1 && state->lasttg > 0 && state->lastsrc > 0) { attron(COLOR_PAIR(2)); - printw (" **Emergency** "); + } + printw ("TGT: [%8i] SRC: [%8i] ", state->lasttg, state->lastsrc); + if (state->dmrburstL != 16 && state->carrier == 1 && state->lasttg > 0 && state->lastsrc > 0) + { attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } + printw ("FID: [%02X] SVC: [%02X] ", state->dmr_fid, state->dmr_so); + printw ("%s ", DMRBusrtTypes[state->dmrburstL]); + printw ("\n"); + printw ("| | "); //10 spaces + //Burger King + if(state->dmrburstL == 16 && state->payload_mi == 0 && (state->dmr_so & 0xCF) == 0x40) //4F or CF mask? + { + attron(COLOR_PAIR(5)); + printw (" **BP** "); + attroff(COLOR_PAIR(5)); + attron(COLOR_PAIR(3)); + } + //Point + if(state->dmrburstL == 16 && state->payload_mi == 0 && state->K > 0 && (state->dmr_so & 0xCF) == 0x40) + { + attron(COLOR_PAIR(1)); + printw ("BPK [%3lld] ", state->K); + attroff(COLOR_PAIR(1)); + attron(COLOR_PAIR(3)); + } + //ALG, KeyID, MI + if(state->dmrburstL == 16 && state->payload_keyid > 0 && (state->dmr_so & 0xCF) == 0x40) + { + attron(COLOR_PAIR(1)); + printw ("ALG: [0x%02X] KEY: [0x%02X] MI: [0x%08X]", state->payload_algid, state->payload_keyid, state->payload_mi); + attroff(COLOR_PAIR(1)); + attron(COLOR_PAIR(3)); + } - if(state->dmr_so & 0x40) + if(state->dmrburstL == 16 && state->dmr_so == 0x40) //0100 0000 { attron(COLOR_PAIR(2)); printw (" **ENC** "); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } - - if(state->dmr_so & 0x30) + if(state->dmrburstL == 16 && state->dmr_so == 0x80) + { + attron(COLOR_PAIR(2)); + printw (" **Emergency** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + if(state->dmrburstL == 16 && state->dmr_so == 0x30) //0010 0000 { attron(COLOR_PAIR(2)); printw (" **Private Call** "); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } - */ - printw("\n"); + printw ("\n"); + //Alias Blocks and Embedded GPS + printw ("| | "); //10 spaces + if(state->dmrburstL == 16) //only during call + { + attron(COLOR_PAIR(5)); + for (short i = 0; i < 5; i++) + { + printw ("%s", state->dmr_callsign[0][i]); + } + //Embedded GPS (not LRRP) + printw ("%s", state->dmr_callsign[0][5] ); + attroff(COLOR_PAIR(5)); + if (state->carrier == 1) + { + attron(COLOR_PAIR(3)); + } - //printw ("%s ", state->slot0light); - printw ("| SLOT 1 "); - if (state->currentslot == 0) //find out how to tell when slot0 has voice - { - s0last = "Data "; - if (strcmp (state->fsubtype, " Slot idle ") == 0) + //LRRP + if(state->dmrburstL != 16) //only during data { - s0last = "Idle "; + attron(COLOR_PAIR(5)); + for (short i = 0; i < 5; i++) + { + printw ("%s", state->dmr_lrrp[0][i]); + } + attroff(COLOR_PAIR(5)); + if (state->carrier == 1) + { + attron(COLOR_PAIR(3)); + } } } - printw ("%s ", s0last); - //printw ("%s ", state->slot1light); - printw ("SLOT 2 "); - if (state->currentslot == 1) //find out how to tell when slot1 has voice + printw ("\n"); + + //Slot 2 [1] + if (lls < 30){ //Don't print on MS mode + printw ("| SLOT 2 - "); + if (state->dmrburstR != 16 && state->carrier == 1 && state->lasttgR > 0 && state->lastsrcR > 0) { - s1last = "Data "; - if (strcmp (state->fsubtype, " Slot idle ") == 0) + attron(COLOR_PAIR(2)); + } + printw ("TGT: [%8i] SRC: [%8i] ", state->lasttgR, state->lastsrcR); + if (state->dmrburstR != 16 && state->carrier == 1 && state->lasttgR > 0 && state->lastsrcR > 0) + { + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + printw ("FID: [%02X] SVC: [%02X] ", state->dmr_fidR, state->dmr_soR); + printw ("%s ", DMRBusrtTypes[state->dmrburstR]); + printw ("\n"); + printw ("| | "); //12 spaces + + //Burger King 2 + if(state->dmrburstR == 16 && state->payload_miR == 0 && (state->dmr_soR & 0xCF) == 0x40) //4F or CF mask? + { + attron(COLOR_PAIR(5)); + printw (" **BP** "); + attroff(COLOR_PAIR(5)); + attron(COLOR_PAIR(3)); + } + //Point 2 + if(state->dmrburstR == 16 && state->payload_miR == 0 && state->K > 0 && (state->dmr_soR & 0xCF) == 0x40) + { + attron(COLOR_PAIR(1)); + printw ("BPK [%3lld] ", state->K); + attroff(COLOR_PAIR(1)); + attron(COLOR_PAIR(3)); + } + //ALG, KeyID, MI 2 + if(state->dmrburstR == 16 && state->payload_keyidR > 0 && (state->dmr_soR & 0xCF) == 0x40) + { + attron(COLOR_PAIR(1)); + printw ("ALG: [0x%02X] KEY: [0x%02X] MI: [0x%08X]", state->payload_algidR, state->payload_keyidR, state->payload_miR); + attroff(COLOR_PAIR(1)); + attron(COLOR_PAIR(3)); + } + //Call Types, may switch to the more robust version later? + if(state->dmrburstR == 16 && state->dmr_soR == 0x40) //0100 0000 + { + attron(COLOR_PAIR(2)); + printw (" **ENC** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + if(state->dmrburstR == 16 && state->dmr_soR == 0x80) + { + attron(COLOR_PAIR(2)); + printw (" **Emergency** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + if(state->dmrburstR == 16 && state->dmr_soR == 0x30) //0010 0000 + { + attron(COLOR_PAIR(2)); + printw (" **Private Call** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + printw ("\n"); + //Alias Blocks and Embedded GPS + printw ("| | "); //10 spaces + if(state->dmrburstR == 16) //only during call + { + attron(COLOR_PAIR(5)); + for (short i = 0; i < 5; i++) { - s1last = "Idle "; + printw ("%s", state->dmr_callsign[1][i]); + } + //Embedded GPS (not LRRP) + printw ("%s", state->dmr_callsign[1][5] ); + attroff(COLOR_PAIR(5)); + if (state->carrier == 1) + { + attron(COLOR_PAIR(3)); } } - printw ("%s \n", s1last); - /* - if (state->dmr_lrrp[3] != NULL) + //LRRP + if(state->dmrburstR != 16) //only during data { - printw ("| LRRP -"); - printw (" %s", state->dmr_lrrp[3]); - printw (" %s", state->dmr_lrrp[1]); - printw (" %s\n", state->dmr_lrrp[2]); + attron(COLOR_PAIR(5)); + for (short i = 0; i < 5; i++) + { + printw ("%s", state->dmr_lrrp[1][i]); + } + attroff(COLOR_PAIR(5)); + if (state->carrier == 1) + { + attron(COLOR_PAIR(3)); + } } - */ - } + printw ("\n"); + } // end if not MS + } //end DMR BS Types //dPMR if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27) @@ -665,97 +813,73 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("TID: [%s] RID: [%s] \n", state->dpmr_target_id, state->dpmr_caller_id); } - if (lls != -1) //is there a synctype 0? + if (lls == 6 || lls == 7 || lls == 18 || lls == 19 || lls == 14 || lls == 15) { printw ("| %s ", SyncTypes[lls]); //printw ("%s", state->dmr_branding); printw ("\n"); } + //fence bottom printw ("------------------------------------------------------------------------------\n"); //colors off if (state->carrier == 1){ //same as above attroff(COLOR_PAIR(3)); } - //if (state->carrier == 0){ //same as above - //attroff(COLOR_PAIR(1)); - //} attron(COLOR_PAIR(4)); //cyan for history - //add other interesting info to put here - //make Call_Matrix - //0 - sync type; 1 - tg/ran; 2 - rid; 3 - slot; 4 - cc; 5 - time(NULL) ; - printw ("--Call History----------------------------------------------------------------\n"); for (short int j = 0; j < 10; j++) { + //only print if a valid time was assinged to the matrix if ( ((time(NULL) - call_matrix[9-j][5]) < 9999) ) - //if (1 == 1) { - printw ("| #%d %s ", j, SyncTypes[call_matrix[9-j][0]]); - if (lls == 8 || lls == 9 || lls == 16 || lls == 17) - { - printw ("RAN [%2d] ", call_matrix[9-j][1]); - printw ("TG [%4d] ", call_matrix[9-j][4]); - printw ("RID [%4d] ", call_matrix[9-j][2]); - } - if (lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33) //P25 P1 and DMR - { - printw ("TID [%8d] ", call_matrix[9-j][1]); - printw ("RID [%8d] ", call_matrix[9-j][2]); - } - if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27) //dPMR - { - printw ("TID [%8d] ", call_matrix[9-j][1]); - printw ("RID [%8d] ", call_matrix[9-j][2]); - printw ("DCC [%2d] ", call_matrix[9-j][4]); + printw ("| %s ", SyncTypes[call_matrix[9-j][0]]); + if (lls == 8 || lls == 9 || lls == 16 || lls == 17) + { + printw ("RAN [%2lld] ", call_matrix[9-j][1]); + printw ("TG [%4lld] ", call_matrix[9-j][4]); + printw ("RID [%4lld] ", call_matrix[9-j][2]); + } + //dPMR + if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27) + { + printw ("TGT [%8lld] ", call_matrix[9-j][1]); + printw ("SRC [%8lld] ", call_matrix[9-j][2]); + printw ("DCC [%2lld] ", call_matrix[9-j][4]); + } + //P25P1 Voice and Data + if (call_matrix[9-j][0] == 0 || call_matrix[9-j][0] == 1) + { + printw ("TID [%8lld] ", call_matrix[9-j][1]); + printw ("RID [%8lld] ", call_matrix[9-j][2]); + printw ("NAC [0x%03llX] ", call_matrix[9-j][4]); + } + //DMR BS Types + if (call_matrix[9-j][0] == 12 || call_matrix[9-j][0] == 13 || call_matrix[9-j][0] == 10 || call_matrix[9-j][0] == 11 ) + { + printw ("S[%d] ", call_matrix[9-j][3]); + printw ("TGT [%8lld] ", call_matrix[9-j][1]); + printw ("SRC [%8lld] ", call_matrix[9-j][2]); + printw ("DCC [%02lld] ", call_matrix[9-j][4]); + } + //DMR MS Types + if (call_matrix[9-j][0] == 32 || call_matrix[9-j][0] == 33 || call_matrix[9-j][0] == 34 ) + { + //printw ("S[%d] ", call_matrix[9-j][3]); + printw ("TGT [%8lld] ", call_matrix[9-j][1]); + printw ("SRC [%8lld] ", call_matrix[9-j][2]); + printw ("DCC [%02lld] ", call_matrix[9-j][4]); + } + printw ("%lld secs ago\n", time(NULL) - call_matrix[9-j][5]); } + } //end Call History + //fence bottom + printw ("------------------------------------------------------------------------------\n"); + attroff(COLOR_PAIR(4)); //cyan for history - //printw ("RID [%08d] ", call_matrix[9-j][2]); - //printw ("S %d - ", call_matrix[j][3]); - if (call_matrix[9-j][0] == 0 || call_matrix[9-j][0] == 1) //P25P1 Voice - { - printw ("NAC [0x%3X] ", call_matrix[9-j][4]); - } - if (call_matrix[9-j][0] == 12 || call_matrix[9-j][0] == 13 || call_matrix[9-j][0] == 10 || call_matrix[9-j][0] == 11 ) //DMR Voice Types - { - printw ("DCC [%2d] ", call_matrix[9-j][4]); - } - printw ("%d secs ago\n", time(NULL) - call_matrix[9-j][5]); - } - //printw("\n"); - } - printw ("------------------------------------------------------------------------------\n"); - attroff(COLOR_PAIR(4)); //cyan for history - //put sync type at very bottom - //printw ("%s %s\n", state->ftype, state->fsubtype); //some ftype strings have extra spaces in them - //if (state->lastsynctype != -1) //is there a synctype 0? - //while((c = getch()) != '~') { - // printw ("%c\n",c);} - //c = getch(); - /* - if (c = getch() != '~') - { - printf ("%c\n",c); - printf ("closing NCurses"); - ncursesClose(); - } - */ - //debug sr printw - if (1==2) //disable this later on - { - - printw ("sr_0 = %16llX \n", state->sr_0); - printw ("sr_1 = %16llX \n", state->sr_1); - printw ("sr_2 = %16llX \n", state->sr_2); - printw ("sr_3 = %16llX \n", state->sr_3); - printw ("sr_4 = %16llX \n", state->sr_4); - printw ("sr_5 = %16llX \n", state->sr_5); - printw ("sr_6 = %16llX \n", state->sr_6); - - } - refresh(); -} + refresh(); +} //end ncursesPrinter void ncursesClose () { diff --git a/src/dsd_reset.c b/src/dsd_reset.c index e8aa893..ab6165d 100644 --- a/src/dsd_reset.c +++ b/src/dsd_reset.c @@ -96,12 +96,7 @@ resetState (dsd_state * state) state->nxdn_last_ran = 0; -//#ifdef TRACE_DSD -// state->debug_sample_index = 0; -// state->debug_label_file = NULL; -// state->debug_label_dibit_file = NULL; -// state->debug_label_imbe_file = NULL; -//#endif - + //each time you run this, it increses memory use by 4MB, massive memory leak + //need to revisit this sometime and look into only resetting only the necesary items to let P25 switch between signals (C4FM or Wide) without needing a restart initialize_p25_heuristics(&state->p25_heuristics); //see if we want to re-init this or not, currently seems to cause memory leak when running over and over, mitigated with a reset flag } diff --git a/src/dstar.c b/src/dstar.c index a5a945c..12a47d0 100644 --- a/src/dstar.c +++ b/src/dstar.c @@ -39,6 +39,9 @@ void processDSTAR(dsd_opts * opts, dsd_state * state) { unsigned int bitbuffer = 0; const int *w, *x; + //set carrier to 1 so ncurses will light up green + state->carrier = 1; + if (opts->errorbars == 1) { fprintf (stderr, "e:"); } @@ -56,7 +59,7 @@ void processDSTAR(dsd_opts * opts, dsd_state * state) { } else { framecount = 1; //just saw a sync frame; there should be 20 not 21 till the next } - + //why is this running in a loop for? while (sync_missed < 3) { memset(ambe_fr, 0, 96); @@ -86,6 +89,7 @@ void processDSTAR(dsd_opts * opts, dsd_state * state) { ambe_fr[*w][*x] = (1 & dibit); w++; x++; + } @@ -128,14 +132,30 @@ void processDSTAR(dsd_opts * opts, dsd_state * state) { slowdata[0] ^= 0x70; slowdata[1] ^= 0x4f; slowdata[2] ^= 0x93; - //fprintf (stderr, "unscrambled- %c%c%c \n", slowdata[0], slowdata[1], slowdata[2] ); + //fprintf (stderr, "unscrambled- %02X %02X %02X %02X \n", slowdata[0], slowdata[1], slowdata[2], slowdata[3] ); } else if (framecount == 0) { //fprintf (stderr, "never scrambled-%c\n",slowdata); - //fprintf (stderr, "never scrambled- %c%c%c \n", slowdata[0], slowdata[1], slowdata[2] ); - } + //fprintf (stderr, "never scrambled- %02X %02X %02X %02X \n", slowdata[0], slowdata[1], slowdata[2], slowdata[3] ); + } + for (short int i = 0; i < 4; i++) + { + if (slowdata[i] <= 0x7E && slowdata[i] >=0x20) + { + //fprintf (stderr, "%c", slowdata[i]); + } + //else fprintf (stderr, "."); + } + //fprintf (stderr, "\n"); framecount++; + + //since we are in a while loop, run ncursesPrinter here. + if (opts->use_ncurses_terminal == 1) + { + ncursesPrinter(opts, state); + } + } //end while loop end: if (opts->errorbars == 1) { @@ -154,7 +174,7 @@ void processDSTAR_HD(dsd_opts * opts, dsd_state * state) { // Note: These routines contain GPLed code. Remove if you object to that. // Due to this, they are in a separate source file. - dstar_header_decode(radioheaderbuffer); + dstar_header_decode(state, radioheaderbuffer); //We officially have sync now, so just pass on to the above routine: diff --git a/src/dstar_header.c b/src/dstar_header.c index 204ee7b..db7907c 100644 --- a/src/dstar_header.c +++ b/src/dstar_header.c @@ -19,8 +19,9 @@ #include "fcs.h" #include "descramble.h" #include "dstar_header.h" +#include "dsd.h" -void dstar_header_decode(int radioheaderbuffer[660]) { +void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]) { int radioheaderbuffer2[660]; unsigned char radioheader[41]; int octetcount, bitcount, loop; @@ -33,6 +34,7 @@ void dstar_header_decode(int radioheaderbuffer[660]) { deinterleave(radioheaderbuffer2, radioheaderbuffer); len = FECdecoder(radioheaderbuffer, radioheaderbuffer2); memset(radioheader, 0, 41); + //memset(state->dstarradioheader, 0, 41); // note we receive 330 bits, but we only use 328 of them (41 octets) // bits 329 and 330 are unused octetcount = 0; @@ -48,6 +50,14 @@ void dstar_header_decode(int radioheaderbuffer[660]) { bitcount = 0; } } + //state->dstarradioheader = radioheader; + + for (short i = 0; i < 41; i++) + { + state->dstarradioheader[i] = radioheader[i]; + } + + // print header fprintf (stderr, "\nDSTAR HEADER: "); //fprintf (stderr, "FLAG1: %02X - FLAG2: %02X - FLAG3: %02X\n", radioheader[0], diff --git a/src/p25p1_hdu.c b/src/p25p1_hdu.c index 2c6a9ad..1bdd2c7 100644 --- a/src/p25p1_hdu.c +++ b/src/p25p1_hdu.c @@ -469,10 +469,10 @@ processHDU(dsd_opts* opts, dsd_state* state) mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8); } - if (opts->payload == 1) //print on payload == 1 + if (1 == 1) //print on payload == 1 { fprintf (stderr, "%s", KYEL); - fprintf (stderr, " HDU ALG ID: 0x%X KEY ID: 0x%X MI: 0x%08llX%08llX%02llX MFID: 0x%02X \n", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid); + fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX MFID: 0x%02X \n", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid); fprintf (stderr, "%s", KNRM); } diff --git a/src/p25p1_ldu2.c b/src/p25p1_ldu2.c index 90ebe4d..c518c92 100644 --- a/src/p25p1_ldu2.c +++ b/src/p25p1_ldu2.c @@ -375,10 +375,10 @@ processLDU2 (dsd_opts * opts, dsd_state * state) mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8); } - if (opts->payload == 1) //print on payload == 1 + if (1 == 1) //print on payload == 1 { fprintf (stderr, "%s", KYEL); - fprintf (stderr, " LDU2 ALG ID: 0x%X KEY ID: 0x%X MI: 0x%08llX%08llX%02llX\n", algidhex, kidhex, mihex1, mihex2, mihex3); + fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX\n", algidhex, kidhex, mihex1, mihex2, mihex3); fprintf (stderr, "%s", KNRM); } From ea40e3e3b7c21a1de9061a26c579b0b537134b42 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Tue, 3 May 2022 07:54:45 -0400 Subject: [PATCH 03/10] More NCurses Tweaks More NCurses Tweaks --Clarify extra call and data information lines --Add Data SRC and TGT to NCurses for LRRP and other things --OCD nitpicks --- src/dmr_sync.c | 11 ++++++++--- src/dsd_ncurses.c | 16 ++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/dmr_sync.c b/src/dmr_sync.c index dd9cbda..409a190 100644 --- a/src/dmr_sync.c +++ b/src/dmr_sync.c @@ -225,7 +225,7 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] //LRRP if ( (state->dmr_34_rate_sf[slot][0] & 0x7F) == 0x45) //Start LRRP now { - sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); + //sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); fprintf (stderr, "%s ", KMAG); //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); @@ -454,7 +454,12 @@ void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[4] & 0x3F), DmrDataByte[5], DmrDataByte[6], DmrDataByte[7]); //not sure if these are right or not fprintf (stderr, "[%d]", (DmrDataByte[5] <<16 ) + (DmrDataByte[6] <<8 ) + DmrDataByte[7] ); } - + if (1 == 1) //already setting by current slot, no need for checking first + { + sprintf ( state->dmr_lrrp[state->currentslot][1], "SRC [%d] TGT [%d] ", + ( (DmrDataByte[2] <<16 ) + (DmrDataByte[3] << 8) + DmrDataByte[4]), + ( (DmrDataByte[5] <<16 ) + (DmrDataByte[6] <<8 ) + DmrDataByte[7]) ); + } if (DmrDataByte[0] == 0x01) { fprintf (stderr, "\n LRRP Control ACK - "); @@ -1173,7 +1178,7 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ //LRRP if ( (state->dmr_12_rate_sf[slot][0] & 0x7F) == 0x45) //Start LRRP now { - sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); + //sprintf ( state->dmr_lrrp[state->currentslot][0], "LRRP - "); fprintf (stderr, "%s ", KMAG); //fprintf (stderr, "\n IP4 Header"); //Not sure this is accurate info IP4 Header? //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 00541d8..b828665 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -199,14 +199,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { for (short i = 0; i < 6; i++) { - //sprintf (state->dmr_lrrp[0][i], ""); + sprintf (state->dmr_lrrp[0][i], ""); } } if (state->dmrburstR == 16 || state->carrier == 0) { for (short i = 0; i < 6; i++) { - //sprintf (state->dmr_lrrp[1][i], ""); + sprintf (state->dmr_lrrp[1][i], ""); } } @@ -624,7 +624,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("FID: [%02X] SVC: [%02X] ", state->dmr_fid, state->dmr_so); printw ("%s ", DMRBusrtTypes[state->dmrburstL]); printw ("\n"); - printw ("| | "); //10 spaces + //printw ("| | "); //10 spaces + printw ("| V XTRA | "); //10 spaces //Burger King if(state->dmrburstL == 16 && state->payload_mi == 0 && (state->dmr_so & 0xCF) == 0x40) //4F or CF mask? { @@ -673,7 +674,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } printw ("\n"); //Alias Blocks and Embedded GPS - printw ("| | "); //10 spaces + //printw ("| | "); //10 spaces + printw ("| D XTRA | "); if(state->dmrburstL == 16) //only during call { attron(COLOR_PAIR(5)); @@ -722,7 +724,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("FID: [%02X] SVC: [%02X] ", state->dmr_fidR, state->dmr_soR); printw ("%s ", DMRBusrtTypes[state->dmrburstR]); printw ("\n"); - printw ("| | "); //12 spaces + //printw ("| | "); //12 spaces + printw ("| V XTRA | "); //10 spaces //Burger King 2 if(state->dmrburstR == 16 && state->payload_miR == 0 && (state->dmr_soR & 0xCF) == 0x40) //4F or CF mask? @@ -772,7 +775,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } printw ("\n"); //Alias Blocks and Embedded GPS - printw ("| | "); //10 spaces + //printw ("| | "); + printw ("| D XTRA | "); if(state->dmrburstR == 16) //only during call { attron(COLOR_PAIR(5)); From 4c3a3e09592b3e5f4c7299b10faf5d2bad3b1b27 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Tue, 3 May 2022 13:49:44 -0400 Subject: [PATCH 04/10] Reconfigure STDIN and RTL for TDMA Stereo Reconfigure STDIN and RTL for TDMA Stereo --adjust configurations and rates to allow two channel audio output when using -T option with RTL and STDIN --minor LRRP print mistake --- src/dmr_sync.c | 11 +++++++---- src/dsd_audio.c | 47 +++++++++++------------------------------------ src/dsd_main.c | 23 +++++++++++++++++++---- src/dsd_symbol.c | 1 + 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/dmr_sync.c b/src/dmr_sync.c index 409a190..0429358 100644 --- a/src/dmr_sync.c +++ b/src/dmr_sync.c @@ -231,6 +231,7 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] //fprintf (stderr, "\n Data Blocks [%d]", state->dmr_34_rate_sf[5]); for (short i = 1; i < 60; i++) //find way to get padding so we only go as deep as we need to! changed from 64 to 60 to skip the CRC Bytes for confirmed data { + /* if ( state->dmr_34_rate_sf[slot][i] == 0x0C) //Source and Destination info { fprintf (stderr, "\n Source:"); @@ -242,6 +243,7 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] fprintf (stderr, " [%08d]", (state->dmr_34_rate_sf[slot][i+5] <<16 ) + (state->dmr_34_rate_sf[slot][i+6] << 8) + state->dmr_34_rate_sf[slot][i+7] ); fprintf (stderr, " - Port %05d", (state->dmr_34_rate_sf[slot][i+10] << 8) + state->dmr_34_rate_sf[slot][i+11]); } + */ if ( state->dmr_34_rate_sf[slot][i] == 0x34 ) //timestamp { fprintf (stderr, "\n LRRP - Timestamp: "); @@ -282,9 +284,10 @@ void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98] //fprintf (stderr, " ("); if (state->dmr_34_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { - fprintf (stderr, "-"); + fprintf (stderr, " (-%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + //fprintf (stderr, " -"); } - fprintf (stderr, "%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + else fprintf (stderr, " (%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); } @@ -1236,9 +1239,9 @@ void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_ //fprintf (stderr, " ("); if (state->dmr_12_rate_sf[slot][i+1] & 0x80) //first bit indicates a sign, or hemisphere? { - fprintf (stderr, "-"); + fprintf (stderr, " (-%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); } - fprintf (stderr, "%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); + else fprintf (stderr, " (%.5lf, %.5lf)", ((double)lrrplat) * lat_unit , (lrrplon * lon_unit) ); } diff --git a/src/dsd_audio.c b/src/dsd_audio.c index 136280a..a080257 100644 --- a/src/dsd_audio.c +++ b/src/dsd_audio.c @@ -351,10 +351,6 @@ writeSynthesizedVoice (dsd_opts * opts, dsd_state * state) short aout_buf[160]; short *aout_buf_p; -// for(n=0; n<160; n++) -// fprintf (stderr,"%d ", ((short*)(state->audio_out_temp_buf))[n]); -// fprintf (stderr,"\n"); - aout_buf_p = aout_buf; state->audio_out_temp_buf_p = state->audio_out_temp_buf; @@ -375,34 +371,6 @@ writeSynthesizedVoice (dsd_opts * opts, dsd_state * state) sf_write_short(opts->wav_out_f, aout_buf, 160); - /* - - int n; - short aout_buf[160]; - short *aout_buf_p; - ssize_t result; - - aout_buf_p = aout_buf; - state->audio_out_temp_buf_p = state->audio_out_temp_buf; - for (n = 0; n < 160; n++) - { - if (*state->audio_out_temp_buf_p > (float) 32760) - { - *state->audio_out_temp_buf_p = (float) 32760; - } - else if (*state->audio_out_temp_buf_p < (float) -32760) - { - *state->audio_out_temp_buf_p = (float) -32760; - } - *aout_buf_p = (short) *state->audio_out_temp_buf_p; - aout_buf_p++; - state->audio_out_temp_buf_p++; - } - - result = write (opts->wav_out_fd, aout_buf, 320); - fflush (opts->wav_out_f); - state->wav_out_bytes += 320; - */ } void @@ -419,8 +387,8 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) } else - //two slot audio testing, still need to seperate channels first internally, but this will play them out of different streams - + //most likely don't need this seperation anymore with L and R, just push it out without any condional checks + /* if(state->currentslot == 0 && opts->audio_in_type != 3 && opts->dmr_stereo == 1) { pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. @@ -433,6 +401,10 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. state->audio_out_idx = 0; } + */ + //Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo + pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. + state->audio_out_idx = 0; } @@ -460,13 +432,16 @@ playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state) //go F yourself PA } else - - //two slot audio testing, still need to seperate channels first internally, but this will play them out of different streams + /* + //most likely don't need this seperation anymore with L and R, just push it out without any condional checks if(opts->dmr_stereo == 1) //state->currentslot == 1 && opts->audio_in_type != 3 && opts->dmr_stereo == 1 { pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works. state->audio_out_idxR = 0; } + */ + pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works. + state->audio_out_idxR = 0; } if (state->audio_out_idx2R >= 800000) diff --git a/src/dsd_main.c b/src/dsd_main.c index 1e2142b..1edf06c 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -503,11 +503,18 @@ liveScanner (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 1) //if stdin, switch to 8000 and single channel audio { - opts->pulse_digi_rate_out = 8000; //stdin needs 8000 output, could have sworn this was working with 48000 + opts->pulse_digi_rate_out = 8000; //stdin needs 8000 output for single channel opts->pulse_digi_out_channels = 1; + if (opts->dmr_stereo == 1) + { + opts->pulse_digi_rate_out = 4000; //stdin needs 4000 by 2 channel for DMR TDMA Stereo output + opts->pulse_digi_out_channels = 2; + fprintf (stderr, "STDIN Audio Rate Out set to 4000 Khz/2 Channel \n"); + } + else fprintf (stderr, "STDIN Audio Rate Out set to 8000 Khz/1 Channel \n"); opts->pulse_raw_rate_out = 48000; opts->pulse_raw_out_channels = 1; - fprintf (stderr, "STDIN Audio Rate Out set to 8000 Khz/1 Channel \n"); + } #ifdef USE_PORTAUDIO @@ -526,11 +533,19 @@ if (opts->audio_in_type == 1) //if stdin, switch to 8000 and single channel audi #ifdef USE_RTLSDR if(opts->audio_in_type == 3) { - opts->pulse_digi_rate_out = 8000; //if rtl, switch to 8000 and 1 channel + //test to see if this is still necessary + opts->pulse_digi_rate_out = 8000; //rtl needs 8000 and 1 channel is mono system opts->pulse_digi_out_channels = 1; + if (opts->dmr_stereo == 1) + { + opts->pulse_digi_rate_out = 4000; //rtl needs 4000 by 2 channel for DMR TDMA Stereo output + opts->pulse_digi_out_channels = 2; + fprintf (stderr, "RTL Audio Rate Out set to 4000 Khz/2 Channel \n"); + } + else fprintf (stderr, "RTL Audio Rate Out set to 8000 Khz/1 Channel \n"); opts->pulse_raw_rate_out = 48000; opts->pulse_raw_out_channels = 1; - fprintf (stderr, "RTL Audio Rate Out set to 8000 Khz/1 Channel \n"); + open_rtlsdr_stream(opts); } #endif diff --git a/src/dsd_symbol.c b/src/dsd_symbol.c index ceb5cdf..2af17e0 100644 --- a/src/dsd_symbol.c +++ b/src/dsd_symbol.c @@ -94,6 +94,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync) } else if (opts->audio_in_type == 1) { result = sf_read_short(opts->audio_in_file, &sample, 1); + //fprintf (stderr, ".."); if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767) { state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here? From 02a27111d33afa23268f7fd32dbc785a2472d477 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Tue, 3 May 2022 15:48:15 -0400 Subject: [PATCH 05/10] Fix Upsampling Fix Upsampling --upsample audio on all input types to the proper output rate required for mono and stereo output. --Allows for 48000/1 or 24000/2 output when using RTL or STDIN input --MBE file playback still at 8000 due to random crackling when upscaled --WAV file still recorded at 8000 mono --MBE file saving and WAV saving disabled when using the -T Stereo TDMA option until both can be reworked. --- src/dsd_audio.c | 6 ++++-- src/dsd_main.c | 26 +++++++++++++------------- src/dsd_ncurses.c | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/dsd_audio.c b/src/dsd_audio.c index a080257..ea1644f 100644 --- a/src/dsd_audio.c +++ b/src/dsd_audio.c @@ -156,7 +156,8 @@ processAudio (dsd_opts * opts, dsd_state * state) // copy audio data to output buffer and upsample if necessary state->audio_out_temp_buf_p = state->audio_out_temp_buf; - if (opts->split == 0) + //the only time we don't want to upsample is when playing back MBE files, upsampling them seems to cause random crackling + if (opts->playfiles == 0) //opts->split == 0 { for (n = 0; n < 160; n++) { @@ -295,7 +296,8 @@ processAudioR (dsd_opts * opts, dsd_state * state) // copy audio data to output buffer and upsample if necessary state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; - if (opts->split == 0) //shouldn't need splitR + //the only time we don't want to upsample is when playing back MBE files, upsampling them seems to cause random crackling + if (opts->playfiles == 0) //opts->split == 0 { for (n = 0; n < 160; n++) { diff --git a/src/dsd_main.c b/src/dsd_main.c index 1edf06c..6517858 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -499,19 +499,19 @@ usage () void liveScanner (dsd_opts * opts, dsd_state * state) { -//need to use this area to configure audio rates for different sources if not pulse -if (opts->audio_in_type == 1) //if stdin, switch to 8000 and single channel audio +//not sure this section is needed now that I've worked out the issue, wasn't upsampling due to split == 1 +if (opts->audio_in_type == 1) { - opts->pulse_digi_rate_out = 8000; //stdin needs 8000 output for single channel + opts->pulse_digi_rate_out = 48000; opts->pulse_digi_out_channels = 1; if (opts->dmr_stereo == 1) { - opts->pulse_digi_rate_out = 4000; //stdin needs 4000 by 2 channel for DMR TDMA Stereo output + opts->pulse_digi_rate_out = 24000; //stdin needs 24000 by 2 channel for DMR TDMA Stereo output opts->pulse_digi_out_channels = 2; - fprintf (stderr, "STDIN Audio Rate Out set to 4000 Khz/2 Channel \n"); + fprintf (stderr, "STDIN Audio Rate Out set to 24000 Khz/2 Channel \n"); } - else fprintf (stderr, "STDIN Audio Rate Out set to 8000 Khz/1 Channel \n"); + else fprintf (stderr, "STDIN Audio Rate Out set to 48000 Khz/1 Channel \n"); opts->pulse_raw_rate_out = 48000; opts->pulse_raw_out_channels = 1; @@ -533,16 +533,16 @@ if (opts->audio_in_type == 1) //if stdin, switch to 8000 and single channel audi #ifdef USE_RTLSDR if(opts->audio_in_type == 3) { - //test to see if this is still necessary - opts->pulse_digi_rate_out = 8000; //rtl needs 8000 and 1 channel is mono system + //not sure this section is needed now that I've worked out the issue, wasn't upsampling due to split == 1 + opts->pulse_digi_rate_out = 48000; //rtl needs 8000 and 1 channel is mono system opts->pulse_digi_out_channels = 1; if (opts->dmr_stereo == 1) { - opts->pulse_digi_rate_out = 4000; //rtl needs 4000 by 2 channel for DMR TDMA Stereo output + opts->pulse_digi_rate_out = 24000; //rtl needs 4000 by 2 channel for DMR TDMA Stereo output opts->pulse_digi_out_channels = 2; - fprintf (stderr, "RTL Audio Rate Out set to 4000 Khz/2 Channel \n"); + fprintf (stderr, "RTL Audio Rate Out set to 24000 Khz/2 Channel \n"); } - else fprintf (stderr, "RTL Audio Rate Out set to 8000 Khz/1 Channel \n"); + else fprintf (stderr, "RTL Audio Rate Out set to 48000 Khz/1 Channel \n"); opts->pulse_raw_rate_out = 48000; opts->pulse_raw_out_channels = 1; @@ -1308,7 +1308,7 @@ main (int argc, char **argv) openAudioInDevice (&opts); - fprintf (stderr,"Press CTRL + C twice to close.\n"); //Kindly remind user to double tap CTRL + C + fprintf (stderr,"Press CTRL + C twice to close.\n"); } else @@ -1323,7 +1323,7 @@ main (int argc, char **argv) if (opts.playfiles == 1) { - opts.pulse_digi_rate_out = 8000; //need set to 8000 for amb/imb playback + opts.pulse_digi_rate_out = 8000; //need set to 8000 for amb/imb playback, upsamling seems to cause random crackles opts.pulse_digi_out_channels = 1; openPulseOutput(&opts); //need to open it up for output playMbeFiles (&opts, &state, argc, argv); diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index b828665..733c829 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -456,7 +456,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } if (opts->audio_in_type == 1) { - printw ("| STDIN - Input\n"); + printw ("| STDIN Standard Input: - \n"); } if (opts->audio_in_type == 3) { From ec9c220ad7fb58921f40409fa4fcdf670e89ba6c Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Wed, 4 May 2022 10:28:35 -0400 Subject: [PATCH 06/10] Fix Upsampling Bug Fix Upsampling Bug --Change logic to only upsample audio when out sample rate is greater than 8k. --Revert RTL and MBE output sample rate to 8k/1 due to random crackling --RTL will still shift to 24k/2 for DMR TDMA stereo, may produce noticeable crackling, unknown as of now, unable to test. --- src/dsd_audio.c | 26 +++++++++++++++++--------- src/dsd_main.c | 20 ++++++++++++-------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/dsd_audio.c b/src/dsd_audio.c index ea1644f..a5e277d 100644 --- a/src/dsd_audio.c +++ b/src/dsd_audio.c @@ -156,8 +156,9 @@ processAudio (dsd_opts * opts, dsd_state * state) // copy audio data to output buffer and upsample if necessary state->audio_out_temp_buf_p = state->audio_out_temp_buf; - //the only time we don't want to upsample is when playing back MBE files, upsampling them seems to cause random crackling - if (opts->playfiles == 0) //opts->split == 0 + //we only want to upsample when using sample rates greater than 8k for output, + //hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur. + if (opts->pulse_digi_rate_out > 8000) { for (n = 0; n < 160; n++) { @@ -296,8 +297,9 @@ processAudioR (dsd_opts * opts, dsd_state * state) // copy audio data to output buffer and upsample if necessary state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; - //the only time we don't want to upsample is when playing back MBE files, upsampling them seems to cause random crackling - if (opts->playfiles == 0) //opts->split == 0 + //we only want to upsample when using sample rates greater than 8k for output, + //hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur. + if (opts->pulse_digi_rate_out > 8000) { for (n = 0; n < 160; n++) { @@ -388,6 +390,11 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) //go F yourself PA } else + { + //Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo + pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. + state->audio_out_idx = 0; + } //most likely don't need this seperation anymore with L and R, just push it out without any condional checks /* @@ -404,9 +411,7 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) state->audio_out_idx = 0; } */ - //Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo - pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works. - state->audio_out_idx = 0; + } @@ -434,6 +439,10 @@ playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state) //go F yourself PA } else + { + pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works. + state->audio_out_idxR = 0; + } /* //most likely don't need this seperation anymore with L and R, just push it out without any condional checks if(opts->dmr_stereo == 1) //state->currentslot == 1 && opts->audio_in_type != 3 && opts->dmr_stereo == 1 @@ -442,8 +451,7 @@ playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state) state->audio_out_idxR = 0; } */ - pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works. - state->audio_out_idxR = 0; + } if (state->audio_out_idx2R >= 800000) diff --git a/src/dsd_main.c b/src/dsd_main.c index 6517858..e183c86 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -218,6 +218,7 @@ initOpts (dsd_opts * opts) opts->inverted_dpmr = 0; opts->dmr_stereo = 0; opts->aggressive_framesync = 1; //more aggressive to combat wonk wonk voice decoding + //opts->audio_in_type = 0; } void @@ -489,7 +490,7 @@ usage () fprintf (stderr," May Cause Skipping or sync issues if bad signal/errors\n"); fprintf (stderr," -F Enable DMR TDMA Stereo Passive Frame Sync\n"); fprintf (stderr," This feature will attempt to resync less often due to excessive voice errors\n"); - fprintf (stderr," Use if skipping occurs, but may cuase wonky audio due to loss of good sync\n"); + fprintf (stderr," Use if skipping occurs, but may cause wonky audio due to loss of good sync\n"); fprintf (stderr," -Z Log MBE Payload to console\n"); fprintf (stderr,"\n"); fprintf (stderr,"Report bugs to: https://github.com/lwvmobile/dsd-fme/issues \n"); @@ -500,10 +501,11 @@ void liveScanner (dsd_opts * opts, dsd_state * state) { -//not sure this section is needed now that I've worked out the issue, wasn't upsampling due to split == 1 +//still need this section mostly due the the crackling on the rtl dongle when upsampled +//probably need to dig a little deeper, maybe inlvl releated? if (opts->audio_in_type == 1) { - opts->pulse_digi_rate_out = 48000; + opts->pulse_digi_rate_out = 48000; //change to 48K/1 for STDIN input opts->pulse_digi_out_channels = 1; if (opts->dmr_stereo == 1) { @@ -533,13 +535,14 @@ if (opts->audio_in_type == 1) #ifdef USE_RTLSDR if(opts->audio_in_type == 3) { - //not sure this section is needed now that I've worked out the issue, wasn't upsampling due to split == 1 - opts->pulse_digi_rate_out = 48000; //rtl needs 8000 and 1 channel is mono system + //still need this section mostly due the the crackling on the rtl dongle when upsampled + //probably need to dig a little deeper, maybe inlvl releated? + opts->pulse_digi_rate_out = 8000; //revert to 8K/1 for RTL input, random crackling otherwise opts->pulse_digi_out_channels = 1; if (opts->dmr_stereo == 1) { - opts->pulse_digi_rate_out = 24000; //rtl needs 4000 by 2 channel for DMR TDMA Stereo output - opts->pulse_digi_out_channels = 2; + opts->pulse_digi_rate_out = 24000; //rtl needs 24000 by 2 channel for DMR TDMA Stereo output + opts->pulse_digi_out_channels = 2; //minimal crackling 'may' be observed, not sure, can't test to see on DMR with RTL fprintf (stderr, "RTL Audio Rate Out set to 24000 Khz/2 Channel \n"); } else fprintf (stderr, "RTL Audio Rate Out set to 48000 Khz/1 Channel \n"); @@ -1323,7 +1326,8 @@ main (int argc, char **argv) if (opts.playfiles == 1) { - opts.pulse_digi_rate_out = 8000; //need set to 8000 for amb/imb playback, upsamling seems to cause random crackles + //need set to 8000/1 for amb/imb playback, upsampling seems to cause random crackling + opts.pulse_digi_rate_out = 8000; opts.pulse_digi_out_channels = 1; openPulseOutput(&opts); //need to open it up for output playMbeFiles (&opts, &state, argc, argv); From ddc2a52a33850e13849a36cbd23129dfc71878f4 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Thu, 5 May 2022 13:06:15 -0400 Subject: [PATCH 07/10] Update Screenshots Update Screenshots --- dsd-fme.png | Bin 81073 -> 72624 bytes dsd-fme2.png | Bin 0 -> 160235 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 dsd-fme2.png diff --git a/dsd-fme.png b/dsd-fme.png index f770f2dabf4a8611f7e72f28e695414c475d7210..d45c8d47a59d5e3358552a94d28be5209a310a10 100644 GIT binary patch literal 72624 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
#M+#IfIJ8*klqt@4KN|VQc};G;rq5<* z@%FX8odq|xyFuDuN*$9bQhCA}@swwG0kw6Cu+B0suTHH5FUb>z8P`YUN5gwxt7TQa zdDs1hhalK0dn}*1Wy?49+8EiU9u$=eb|@fu9a)>{dA||y+s{2ykb1`)udmS6VKVQK zEHMkwR@kusseYrH_O^YInCpJF@c<)GL(N8**7aYT{&yeJsUsJJBz{31MaMXWGNfKK zA~N}x(Ylx?n?Ga*-b-sDSfrPP4V!e?yE0|FSo{Cyss(BGse)_0a}_SUwy@XQHM)&Q zppX?cjKY6O9X=CqGn@m&Ka>vq3ikhr^+Eif(nJ3@1@$?)$AQb6oyjoC7xj_l5K#X8 zSUyar$4ZIF_V$)GwdNnII5LN4?^@J{gcbk$F?osF$oSEfvz1p0nwXf#!~=oD>$8BI zK*{P2+pft``)|3=3MQ=)8$W>j{nppkEQ%og@cy510Fk-%=iUP75ZGMA5ZJ5#jK93M UJDB|SX<+~YPgg&ebxsLQ009x`xBvhE literal 81073 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

EYqm7DJ9iT>_yGti40g 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

mdn&=t?a1vxxuhi3}Nm^8QD1M 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 literal 0 HcmV?d00001 From 56da871ce216d5cf48216e7e46dd0d57df94d328 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Thu, 5 May 2022 13:28:53 -0400 Subject: [PATCH 08/10] Update README.md Update Readme to include information for DMR Stereo, new screenshots, and other updates. --- README.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 96f0e83..44e5020 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,24 @@ -# Digital Speech Decoder - Florida Man Edition -This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with a few tweaks from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch as well. NXDN voice decoding is currently working a lot better, thanks to the latter, although I have yet to explore the expanded NXDN or DMR decoding he has laid out. That is a goal. I have also implemented a few more RTL options, including rtl device gain, PPM error, device index selection, squelch, VFO bandwidth, and a UDP remote that works like the old rtl_udp fork, although its currently limited to changing frequency and squelch. The goal is to integrate this project into [EDACS-FM](https://github.com/lwvmobile/edacs-fm "EDACS-FM") but I also want it to be its own standalone project. +# Digital Speech Decoder - Florida Man Edition +This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch as well. NXDN voice decoding is currently working a lot better, thanks to the latter, although I have yet to explore the expanded NXDN or DMR decoding he has laid out. That is a goal. I have also implemented a few more RTL options, including rtl device gain, PPM error, device index selection, squelch, VFO bandwidth, and a UDP remote that works like the old rtl_udp fork, although its currently limited to changing frequency and squelch. The goal is to integrate this project into [EDACS-FM](https://github.com/lwvmobile/edacs-fm "EDACS-FM") but I also want it to be its own standalone project. + +## 2022.05.05 Update ## +I have successfully added a DMR Stereo method for listening to voice audio in both TDMA channels/slots simultaneously. This method will also allow for data decoding in the opposite slot if only one voice call is active, allowing the user not to miss any useful information in the second slot while the previous slot is in use. DMR Stereo also has vastly improved handling of MS/Simplex voice decoding and (limited/hit or miss) MS data decoding. To call this method, see the example below. The old method of decoding DMR is also included and is stil the default for the time being, but the DMR Stereo method is working well, users are encouraged to try both methods and find the one that is suitable for them. New commands include: +``` +-T Enable DMR 'TDMA' Stereo + Please note, for the time being, any MBE file saving, WAV file saving, or MBE payloads are disabled while using DMR Stereo. These features are still present in the default DMR handling and other voice decoder options and will be re- integrated into DMR Stereo in the future. DMR Stereo will also need to be enabled to handle DMR MS/Simplex voice, as it was removed from the default method due to poor performance. + +-F Enable Passive Frame Sync + Use Passive Frame Sync if voice in both slots becomes choppy/skips + Using Passive Sync may cause wonky audio though, depends on quality of signal. + +``` ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme.png) -## Example Usage - NCurses Terminal and Log Console to log file -`./dsd -N 2> voice.log` +![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme2.png) + +## Example Usage - New DMR Stereo, Ncurses Terminal, Pulse Input/Output, and Log Console to file +`./dsd -fr -T -Z -N 2> voice.log` in second terminal, same folder, run: @@ -67,7 +81,7 @@ The Current list of objectives include: 1. ~~Random Tinkering~~ More Random Tinkering -2. ~~Implemented Pulse Audio~~ Remove PortAudio, improve Pulse Audio for stereo output and channel/slot separation. +2. ~~Implemented Pulse Audio~~ Remove PortAudio, ~~improved Pulse Audio for stereo output and channel/slot separation~~ 3. ~~Improve NXDN and DMR support~~ Continue to improve ~~NXDN and DMR~~ all data and voice decoding. @@ -77,9 +91,9 @@ The Current list of objectives include: ## How to clone, check out, and build this branch -### Ubuntu 20.04/LM20/Debian Bullseye or Newer: +### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer: -Using the included install.sh should make for a simple and painless clone, build, and install on newer Debian or Ubuntu based system. Simply acquire or copy the script, and run it. +Using the included install.sh should make for a simple and painless clone, build, and install on newer Debian or Ubuntu based system. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 has been tested working with the installer script and use. ``` wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/install.sh From 064cc21b2b11eb0fcf5f60ae873fbf4403b53f70 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Thu, 5 May 2022 15:46:01 -0400 Subject: [PATCH 09/10] Update README.md Clarification/Cleanup/Spelling/Grammar --- README.md | 78 ++++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 44e5020..1bc2558 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,75 @@ # Digital Speech Decoder - Florida Man Edition -This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch as well. NXDN voice decoding is currently working a lot better, thanks to the latter, although I have yet to explore the expanded NXDN or DMR decoding he has laid out. That is a goal. I have also implemented a few more RTL options, including rtl device gain, PPM error, device index selection, squelch, VFO bandwidth, and a UDP remote that works like the old rtl_udp fork, although its currently limited to changing frequency and squelch. The goal is to integrate this project into [EDACS-FM](https://github.com/lwvmobile/edacs-fm "EDACS-FM") but I also want it to be its own standalone project. +This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch as well. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), and [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"). This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, and hrh17 for the many hours of wav samples submitted by them. ## 2022.05.05 Update ## -I have successfully added a DMR Stereo method for listening to voice audio in both TDMA channels/slots simultaneously. This method will also allow for data decoding in the opposite slot if only one voice call is active, allowing the user not to miss any useful information in the second slot while the previous slot is in use. DMR Stereo also has vastly improved handling of MS/Simplex voice decoding and (limited/hit or miss) MS data decoding. To call this method, see the example below. The old method of decoding DMR is also included and is stil the default for the time being, but the DMR Stereo method is working well, users are encouraged to try both methods and find the one that is suitable for them. New commands include: +I have successfully added a DMR Stereo method for listening to voice audio in both TDMA channels/slots simultaneously. This method will also allow for data decoding in the opposite slot if only one voice call is active, allowing the user not to miss any useful information in the second slot while the previous slot is in use. DMR Stereo also has vastly improved handling of MS/Simplex voice decoding and (hit or miss) MS data decoding. To call this method, see the example below. The old method of decoding DMR is also included and is stil the default for the time being, but the DMR Stereo method is working well, users are encouraged to try both methods and find the one that is suitable for them. New commands include: ``` -T Enable DMR 'TDMA' Stereo - Please note, for the time being, any MBE file saving, WAV file saving, or MBE payloads are disabled while using DMR Stereo. These features are still present in the default DMR handling and other voice decoder options and will be re- integrated into DMR Stereo in the future. DMR Stereo will also need to be enabled to handle DMR MS/Simplex voice, as it was removed from the default method due to poor performance. - -F Enable Passive Frame Sync - Use Passive Frame Sync if voice in both slots becomes choppy/skips - Using Passive Sync may cause wonky audio though, depends on quality of signal. - ``` +Please note, for the time being, any MBE file saving, WAV file saving, or MBE payloads are disabled while using DMR Stereo. These features are still present in the default DMR handling and other voice decoder options and will be re- integrated into DMR Stereo in the future. DMR Stereo will also need to be enabled to handle DMR MS/Simplex voice, as it was removed from the default method due to poor performance. + +Use Passive Frame Sync if voice in both slots becomes choppy or skips. Using Passive Sync may cause wonky audio though, depends on quality of signal and bit errors present. + ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme.png) ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme2.png) -## Example Usage - New DMR Stereo, Ncurses Terminal, Pulse Input/Output, and Log Console to file -`./dsd -fr -T -Z -N 2> voice.log` +### Example Usage and Note! +`dsd` or `./dsd` may need to be used in your usage, depending on if you are running it from the build folder, or if you have run make install. You will need to tweak the examples to your particular usage. Users can always check `dsd -h` for help and all command line switches to use. -in second terminal, same folder, run: +`dsd` is all you need to run for pulse input, pulse output, and auto detect for DMR, P25P1, D-STAR, and X2-TDMA decoding. To use other decoding methods which cannot be auto detected, please use the following command line switches. + +``` +-fi NXDN48 +-fn NXDN96 +-fp ProVoice +-fm dPMR, also may need to use -xd if inverted dPMR. +-fr DMR, also may need to use -xr if inverted DMR, use -T for Stereo, and -F for passive sync +-f1 P25P1 +-fx X2-TDMA +``` + +## Example Usage - New DMR Stereo, Ncurses Terminal, Pulse Input/Output, and Log Console to file +`dsd -fr -T -Z -N 2> voice.log` + +and in a second terminal tab, same folder, run `tail -n 40 -f voice.log` -## Example Usage - RTL +## Example Usage - RTL2832 Dongle Input `dsd -fi -i rtl -c 154.9875M -P -2 -D 1 -G 36 -L 70 -U 6021 -Y 12` ``` -i rtl to use rtl_fm (default is -i pulse for pulse audio) --o pulse to set output to pulse audio (is default anyways) +-o pulse to set output to pulse audio (excluded, is default anyways) -c Set frequency --P set PPM error +-P set PPM error (default = 0) --D set device index number +-D set device index number (default = 0; first device detected) -G set device gain (0-49) (default = 0 Auto Gain) --L set rtl squelch to 25 +-L set rtl squelch to 25 (default = 0; off) --V set RTL sample 'volume' multiplier +-U set UDP port for rtl_fm remote control (default = 6020) --U set UDP port for rtl_fm remote control - --Y 12 set rtl VFO bandwidth in kHz, (default = 48)(6, 8, 12, 16, 24, 48) +-Y 12 set rtl VFO bandwidth in kHz, (default = 12)(6, 8, 12, 16, 24, 48) ``` - -### Note - -`dsd` and `./dsd` may need to be used in your usage, depending on if you are running it from the build folder, or if you have run make install. You will need to tweak the examples to your particular usage - - -## Example Pulse Audio Input and Pulse Audio Output, Autodetect Frame Type -`dsd` or `./dsd` -``` -Yes, it really is this simple now - --fa Auto-detect frame type, is default - -sans NXDN, dPMR, or Provoice, need to specify -fp, -fd, -fi, or -fn respectively -pulse audio is set as default input and output methods - -``` -## Example STDIN UDP from GQRX or SDR++, output to Pulse Audio, and save wav files -`socat stdio udp-listen:7355 | dsd -fi -i - -o pulse -w nxdn.wav` +## Example STDIN UDP from GQRX or SDR++, output to Pulse Audio, and append decoded audio to wav file +`socat stdio udp-listen:7355 | dsd -fi -i - -w nxdn.wav` ## Example Usage - Extra Information for Academic Study and Logging +Be sure to create a folder called MBE first and run + `dsd -d ./MBE/ -Z -pu 2>> voice.log` -in second terminal, same folder, run: +and in a second terminal tab, same folder, run `tail -n 40 -f voice.log` @@ -106,8 +103,7 @@ The above install.sh should now function on older system types. You can elect to ## Manual Install -First, install dependency packages. This guide will assume you are using Ubuntu 20.04 based distros. Check your package manager for equivalent packages if different. PortAudio is not currently used in this build, and is disabled in CMakeLists.txt, you can re-enable it if you wish, but it isn't recommended unless you have a very specific reason to do so. Some of these dependencies are not currently be used, but may be used in future builds. - +First, install dependency packages. This guide will assume you are using Ubuntu 20.04 based distros. Check your package manager for equivalent packages if different. PortAudio is not used in this build! ``` sudo apt update From f8b9d47c6d4196524d2eee34fd04fe7904bda5b9 Mon Sep 17 00:00:00 2001 From: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Fri, 6 May 2022 11:48:37 -0400 Subject: [PATCH 10/10] Minor Framesync Print with DMR Minor Framesync Print with DMR --Minor Framesync Print Fix for DMR --- src/dmr_bs.c | 3 ++- src/dsd_frame_sync.c | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/dmr_bs.c b/src/dmr_bs.c index 33dc1d1..fff2fae 100644 --- a/src/dmr_bs.c +++ b/src/dmr_bs.c @@ -456,7 +456,8 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state) } - fprintf (stderr,"\n%s ", getTime()); + //fprintf (stderr,"\n%s ", getTime()); + fprintf (stderr,"%s ", getTime()); fprintf (stderr,"Sync: +DMR | Frame Sync | DMRSTEREO | VC1 FS \n"); processMbeFrame (opts, state, NULL, ambe_fr3, NULL); dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index be29dc5..546580b 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -839,7 +839,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf(state->ftype, "DMR "); if (opts->errorbars == 1) { - //printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); + printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); } if (state->lastsynctype != 11) { @@ -867,7 +867,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) sprintf(state->ftype, "DMR "); if (opts->errorbars == 1) { - //printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation); + printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation); } state->lastsynctype = 10; if ( opts->monitor_input_audio == 1) @@ -880,9 +880,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { // inverted voice frame sprintf(state->ftype, "DMR "); - if (opts->errorbars == 1) + 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) { @@ -891,7 +891,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->lastsynctype = 11; if ( opts->monitor_input_audio == 1) { - pa_simple_flush(opts->pulse_raw_dev_out, NULL); } return (11); @@ -909,7 +908,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { // voice frame sprintf(state->ftype, "DMR "); - if (opts->errorbars == 1) + if (opts->errorbars == 1 && opts->dmr_stereo == 0) { printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation); } @@ -929,7 +928,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { // inverted data frame sprintf(state->ftype, "DMR "); - if (opts->errorbars == 1) + if (opts->errorbars == 1 && opts->dmr_stereo == 0) { printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); } @@ -949,7 +948,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->min = ((state->min) + lmin) / 2; state->currentslot = 0; state->directmode = 1; //Direct mode - if (opts->inverted_dmr == 0) + if (opts->inverted_dmr == 0 && opts->dmr_stereo == 0) { // voice frame sprintf(state->ftype, "DMR "); @@ -972,7 +971,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { // inverted data frame sprintf(state->ftype, "DMR "); - if (opts->errorbars == 1) + if (opts->errorbars == 1 && opts->dmr_stereo == 0) { printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); } @@ -992,7 +991,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { // voice frame sprintf(state->ftype, "DMR "); - if (opts->errorbars == 1) + if (opts->errorbars == 1 && opts->dmr_stereo == 0) { printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation); } @@ -1011,7 +1010,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { // inverted data frame sprintf(state->ftype, "DMR "); - if (opts->errorbars == 1) + if (opts->errorbars == 1 && opts->dmr_stereo == 0) { printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation); }