diff --git a/README.md b/README.md index 60b8ceb..d9cc120 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments. -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, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody. +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, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, ilyacodes, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody. ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/audio_work/dsd-fme2.png) diff --git a/examples/Example_Usage.md b/examples/Example_Usage.md index e68f868..34723c9 100644 --- a/examples/Example_Usage.md +++ b/examples/Example_Usage.md @@ -8,7 +8,11 @@ -fa Legacy Auto (not recommended) -fi NXDN48 -fn NXDN96 --fp EDACS/Provoice +-fp Provoice +-fh EDACS Standard / Network +-fH EDACS Standard / Network with ESK +-fe EDACS Extended Addresssing +-fE EDACS Extended Addressing with ESK -fm dPMR, also may need to use -xd if inverted dPMR. -f1 P25P1 -f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel) @@ -74,7 +78,7 @@ Detailed Setup: ``` RTL-SDR options: - Usage: rtl:dev:freq:gain:ppm:bw:sq:udp + Usage: rtl:dev:freq:gain:ppm:bw:sq:vol NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended dev RTL-SDR Device Index Number freq RTL-SDR Frequency (851800000 or 851.8M) @@ -82,9 +86,9 @@ RTL-SDR options: ppm RTL-SDR PPM Error (default = 0) bw RTL-SDR Bandwidth kHz (default = 12)(8, 12, 16, 24) sq RTL-SDR Squelch Level vs RMS Value (Optional) - udp RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only) - Example: dsd-fme-zdev -fs -i rtl -C cap_plus_channel.csv -T - Example: dsd-fme-zdev -fp -i rtl:0:851.375M:22:-2:24:0:6021 + vol RTL-SDR Sample 'Volume' Multiplier (default = 1)(1,2,3) + Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T + Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2 ``` @@ -100,11 +104,11 @@ and in a second terminal tab, same folder, run EDACS Trunking (w/ channel map import) ---EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice, Analog Voice Monitoring and Per Call is Experimental) +--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (see switches above for STD/NET, EA, and ESK modes) -`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans` +`dsd-fme -i tcp -fE -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans` ---NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy) +--NXDN48 Trunking (Direct Frequency Assignment) with SDR++ (untested for frequency accuracy) `dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans` @@ -176,9 +180,9 @@ M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel) R - start capturing symbol capture bin (date/time name file) r - stop capturing symbol capture bin spacebar - replay last symbol capture bin (captures must be stopped first) -s - stop playing symbol capture bin or wav input file +s - stop playing symbol capture bin or wav input file (lower s) P - start per call decoded wav files (Capital P) -p - stop per call decoded wav files (Lower p) +p - stop per call decoded wav files (lower p) t - toggle trunking (needs either rtl input, or rigctl connection) y - toggle scanner (needs either rtl input, or rigctl connection) 1 - Toggle Voice Synthesis in TDMA Slot 1 or FDMA Conventional Voice @@ -202,6 +206,9 @@ l - Hold TG in Slot 2 on TDMA Systems, or clear current hold C - Drop Call and Return to CC during trunking operation L - Manual Cycle Forward Channel Tuned when RIGCTL or using RTL input and channel csv loaded +S - Toggle between EDACS Standard/Network and Extended Addressing mode (Capital S) +A - Toggle EDACS ESK Mask (none vs A0) + ``` ## Sending Audio to a Icecast 2 Server via FFmpeg (Windows) diff --git a/examples/trunking.sh b/examples/trunking.sh index cb8152e..7053097 100644 --- a/examples/trunking.sh +++ b/examples/trunking.sh @@ -17,9 +17,21 @@ dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans #dsd-fme -fs -i rtl:0:450M:44:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans -#EDACS/EDACS-EA Digital Only -#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans -#dsd-fme -fp -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#EDACS Standard Network Analog and Digital +#dsd-fme -fh -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fh -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans + +#EDACS Standard Network Analog and Digital (with ESK) +#dsd-fme -fH -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fH -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans + +#EDACS Extended Address Analog and Digital +#dsd-fme -fe -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fe -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans + +#EDACS Extended Address Analog and Digital (with ESK) +#dsd-fme -fE -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fE -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #NXDN48 Type-C or Type-D Trunking with Channel Map #dsd-fme -fi -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans diff --git a/include/dsd.h b/include/dsd.h index 8354c33..bc5acac 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -86,6 +86,35 @@ extern volatile uint8_t exitflag; //fix for issue #136 +//new audio filter stuff from: https://github.com/NedSimao/FilteringLibrary +typedef struct { + float coef[2]; + float v_out[2]; +}LPFilter; + +typedef struct { + float coef; + float v_out[2]; + float v_in[2]; + +}HPFilter; + +typedef struct { + LPFilter lpf; + HPFilter hpf; + float out_in; +}PBFilter; + +typedef struct { + float alpha; + float beta; + + float vin[3]; + float vout[3]; + +}NOTCHFilter; +//end new filters + //group csv import struct typedef struct { @@ -210,8 +239,8 @@ typedef struct SF_INFO *audio_in_file_info; uint32_t rtlsdr_center_freq; - int rtlsdr_ppm_error; - int audio_in_type; + int rtlsdr_ppm_error; + int audio_in_type; char audio_out_dev[1024]; int audio_out_fd; int audio_out_fdR; //right channel audio for OSS hack @@ -231,6 +260,7 @@ typedef struct FILE *symbol_out_f; float audio_gain; float audio_gainR; + float audio_gainA; int audio_out; int dmr_stereo_wav; char wav_out_dir[512]; @@ -285,6 +315,7 @@ typedef struct int rtl_started; long int rtl_rms; int monitor_input_audio; + int analog_only; int pulse_raw_rate_in; int pulse_raw_rate_out; int pulse_digi_rate_in; @@ -367,7 +398,7 @@ typedef struct char group_in_file[1024]; char lcn_in_file[1024]; char chan_in_file[1024]; - char key_in_file[1024]; + char key_in_file[1024]; //end import filenames //reverse mute @@ -405,6 +436,12 @@ typedef struct int slot1_on; int slot2_on; + //enable filter options + int use_lpf; + int use_hpf; + int use_pbf; + int use_hpf_d; + //'DSP' Format Output uint8_t use_dsp_output; char dsp_out_file[2048]; @@ -447,14 +484,14 @@ typedef struct //new stereo short sample storage short s_l[160]; //single sample left short s_r[160]; //single sample right - short s_l4[4][160]; //quad sample for up to a P25p2 4V - short s_r4[4][160]; //quad sample for up to a P25p2 4V + short s_l4[18][160]; //quad sample for up to a P25p2 4V + short s_r4[18][160]; //quad sample for up to a P25p2 4V //new stereo short sample storage tapped from 48_k internal upsampling short s_lu[160*6]; //single sample left short s_ru[160*6]; //single sample right short s_l4u[4][160*6]; //quad sample for up to a P25p2 4V short s_r4u[4][160*6]; //quad sample for up to a P25p2 4V - //end + //end int audio_out_idx; int audio_out_idx2; int audio_out_idxR; @@ -507,6 +544,7 @@ typedef struct char slot0light[8]; float aout_gain; float aout_gainR; + float aout_gainA; float aout_max_buf[200]; float aout_max_bufR[200]; float *aout_max_buf_p; @@ -616,7 +654,7 @@ typedef struct char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet. - + //dmr talker alias new/fixed stuff uint8_t dmr_alias_format[2]; //per slot uint8_t dmr_alias_len[2]; //per slot @@ -630,6 +668,16 @@ typedef struct dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame; + //new audio filter structs + LPFilter RCFilter; + HPFilter HRCFilter; + PBFilter PBF; + NOTCHFilter NF; + LPFilter RCFilterL; + HPFilter HRCFilterL; + LPFilter RCFilterR; + HPFilter HRCFilterR; + char dpmr_caller_id[20]; char dpmr_target_id[20]; @@ -664,6 +712,7 @@ typedef struct int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS) int ess_b[2][96]; //external storage for ESS_B fragments int fourv_counter[2]; //external reference counter for ESS_B fragment collection + int voice_counter[2]; //external reference counter for 18V x 2 P25p2 Superframe int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch //iden freq storage for frequency calculations @@ -695,13 +744,23 @@ typedef struct //edacs int ea_mode; - int esk_mode; + unsigned short esk_mask; unsigned long long int edacs_site_id; int edacs_lcn_count; //running tally of lcn's observed on edacs system int edacs_cc_lcn; //current lcn for the edacs control channel int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to) int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix + int edacs_vc_call_type; //the type of call on the given VC - see defines below + + //flags for EDACS call type + #define EDACS_IS_VOICE 0x01 + #define EDACS_IS_DIGITAL 0x02 + #define EDACS_IS_EMERGENCY 0x04 + #define EDACS_IS_GROUP 0x08 + #define EDACS_IS_INDIVIDUAL 0x10 + #define EDACS_IS_ALL_CALL 0x20 + #define EDACS_IS_INTERCONNECT 0x40 //trunking group and lcn freq list long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully @@ -943,10 +1002,15 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state); //float stere void playSynthesizedVoiceFS4 (dsd_opts * opts, dsd_state * state); //float stereo mix 4v2 P25p2 void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state); //float mono void agf (dsd_opts * opts, dsd_state * state, float samp[160], int slot); //float gain control +void agsm (dsd_opts * opts, dsd_state * state, short * input, int len); //short gain control for analog things +void analog_gain (dsd_opts * opts, dsd_state * state, short * input, int len); //manual gain handling for analong things //new short stuff +void playSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state); //short mono mix +void playSynthesizedVoiceMSR (dsd_opts * opts, dsd_state * state); //short mono mix R (needed for OSS 48k input/output) void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state); //short stereo mix void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state); //short stereo mix 3v2 DMR void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state); //short stereo mix 4v2 P25p2 +void playSynthesizedVoiceSS18 (dsd_opts * opts, dsd_state * state); //short stereo mix 18V Superframe void upsampleS (short invalue, short prev, short outbuf[6]); //upsample 8k to 48k short // void openAudioOutDevice (dsd_opts * opts, int speed); @@ -1188,7 +1252,7 @@ bool crc8_ok(uint8_t bits[], unsigned int len); //modified CRC functions for SB/RC uint8_t crc7(uint8_t bits[], unsigned int len); uint8_t crc3(uint8_t bits[], unsigned int len); -uint8_t crc4(uint8_t bits[], unsigned int len); +uint8_t crc4(uint8_t bits[], unsigned int len); //LFSR and LFSRP code courtesy of https://github.com/mattames/LFSR/ void LFSR(dsd_state * state); @@ -1285,12 +1349,24 @@ void eot_cc(dsd_opts * opts, dsd_state * state); //end of TX return to CC //Generic Tuning Functions void return_to_cc (dsd_opts * opts, dsd_state * state); -//misc generic audio filtering for analog at 48k/1 +//misc audio filtering for analog long int raw_rms(short *samples, int len, int step); -void analog_deemph_filter(short * input, int len); -void analog_preemph_filter(short * input, int len); -void analog_dc_block_filter(short * input, int len); -void analog_clipping_filter(short * input, int len); +void init_audio_filters(dsd_state * state); +void lpf(dsd_state * state, short * input, int len); +void hpf(dsd_state * state, short * input, int len); +void pbf(dsd_state * state, short * input, int len); +void nf(dsd_state * state, short * input, int len); +void hpf_dL(dsd_state * state, short * input, int len); +void hpf_dR(dsd_state * state, short * input, int len); +//from: https://github.com/NedSimao/FilteringLibrary +void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS); +float LPFilter_Update(LPFilter *filter, float v_in); +void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS); +float HPFilter_Update(HPFilter *filter, float v_in); +void PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS); +float PBFilter_Update(PBFilter *filter, float v_in); +void NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS); +float NOTCHFilter_Update(NOTCHFilter *filter, float vin); //csv imports int csvGroupImport(dsd_opts * opts, dsd_state * state); diff --git a/src/3.c b/src/3.c index c2adde2..e104993 100644 --- a/src/3.c +++ b/src/3.c @@ -321,4 +321,274 @@ uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2) // // Wojciech Kaczmarski, SP5WWP // M17 Project, 29 December 2023 -//-------------------------------------------------------------------- \ No newline at end of file +//-------------------------------------------------------------------- + + +//audio filter stuff sourced from: https://github.com/NedSimao/FilteringLibrary +//no license / information provided in source code +#define PI 3.141592653 + +//do we need this for test input sthit? +// #define PLOT_POINTS 100 +// #define SAMPLE_TIME_S 0.001 +// #define CUTOFFFREQ_HZ 50 +// #define sine_freq 40 +// #define sine_freq_1 500 +// #define HFC 50 +// #define LFC 100 + + +void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS) +{ + + float RC=0.0; + RC=1.0/(2*PI*cutoffFreqHz); + filter->coef[0]=sampleTimeS/(sampleTimeS+RC); + filter->coef[1]=RC/(sampleTimeS+RC); + + filter->v_out[0]=0.0; + filter->v_out[1]=0.0; + +} + + +float LPFilter_Update(LPFilter *filter, float v_in) +{ + + filter->v_out[1]=filter->v_out[0]; + filter->v_out[0]=(filter->coef[0]*v_in) + (filter->coef[1]*filter->v_out[1]); + + return (filter->v_out[0]); + +} + + +/******************************************************************************************************** + * HIGH PASS FILTER +********************************************************************************************************/ +void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS) +{ + + float RC=0.0; + RC=1.0/(2*PI*cutoffFreqHz); + + filter->coef=RC/(sampleTimeS+RC); + + filter->v_in[0]=0.0; + filter->v_in[1]=0.0; + + filter->v_out[0]=0.0; + filter->v_out[1]=0.0; + +} + + +float HPFilter_Update(HPFilter *filter, float v_in) +{ + + filter->v_in[1]=filter->v_in[0]; + filter->v_in[0]=v_in; + + filter->v_out[1]=filter->v_out[0]; + filter->v_out[0]=filter->coef * (filter->v_in[0] - filter->v_in[1]+filter->v_out[1]); + + return (filter->v_out[0]); + +} + +/******************************************************************************************************** + * BAND PASS FILTER +********************************************************************************************************/ + + +void PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS) +{ + + LPFilter_Init(&filter->lpf, LPF_cutoffFreqHz, sampleTimeS); + HPFilter_Init(&filter->hpf, HPF_cutoffFreqHz, sampleTimeS); + + filter->out_in=0.0; + +} + +float PBFilter_Update(PBFilter *filter, float v_in) +{ + + filter->out_in=HPFilter_Update(&filter->hpf, v_in); + + filter->out_in=LPFilter_Update(&filter->lpf, filter->out_in); + + return (filter->out_in); + +} + +/******************************************************************************************************** + * NOTCH FILTER +********************************************************************************************************/ + + +void NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS) +{ + + //filter frequency to angular (rad/s) + float w0_rps=2.0 * PI *centerFreqHz; + float ww_rps=2.0 * PI *notchWidthHz; + + //pre warp center frequency + float w0_pw_rps=(2.0/sampleTimeS) * tanf(0.5 * w0_rps * sampleTimeS); + + //computing filter coefficients + + filter->alpha=4.0 + w0_rps*w0_pw_rps*sampleTimeS*sampleTimeS; + filter->beta=2.0*ww_rps*sampleTimeS; + + //clearing input and output buffers + + for (uint8_t n=0; n<3; n++) + { + filter->vin[n]=0; + filter->vout[n]=0; + } + +} + +float NOTCHFilter_Update(NOTCHFilter *filter, float vin) +{ + + //shifting samples + filter->vin[2]=filter->vin[1]; + filter->vin[1]=filter->vin[0]; + + filter->vout[2]=filter->vout[1]; + filter->vout[1]=filter->vout[0]; + + filter->vin[0]=vin; + + //compute new output + filter->vout[0]=(filter->alpha*filter->vin[0] + 2.0 *(filter->alpha -8.0)*filter->vin[1] + filter->alpha*filter->vin[2] + -(2.0f*(filter->alpha-8.0)*filter->vout[1]+(filter->alpha-filter->beta)*filter->vout[2]))/(filter->alpha+filter->beta); + + + return (filter->vout[0]); +} + +void init_audio_filters (dsd_state * state) +{ + //still not sure if this is even correct or not, but 48k sounds good now + LPFilter_Init(&state->RCFilter, 960, (float)1/(float)48000); + HPFilter_Init(&state->HRCFilter, 960, (float)1/(float)48000); + + //left and right variants for stereo output testing on digital voice samples + LPFilter_Init(&state->RCFilterL, 960, (float)1/(float)16000); + HPFilter_Init(&state->HRCFilterL, 960, (float)1/(float)16000); + LPFilter_Init(&state->RCFilterR, 960, (float)1/(float)16000); + HPFilter_Init(&state->HRCFilterR, 960, (float)1/(float)16000); + + //PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS); + //NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS); + + //NOTE: PBFilter_init also inits a LPF and HPF, but on another set of filters, might be worth + //testing just using the PBFilter by itself and see how it does without the hpf used + + //passband filter working (seems to be), notch filter unsure which values to use, doesn't have any appreciable affect when used as is + PBFilter_Init(&state->PBF, 8000, 12000, (float)1/(float)1536000); //RTL Sampling at 1536000 S/s. + NOTCHFilter_Init(&state->NF, 1000, 4000, (float)1/(float)1536000); + +} + +//FUNCTIONS for handing use of above filters + +//lpf +void lpf(dsd_state * state, short * input, int len) +{ + int i; + for (i = 0; i < len; i++) + { + // fprintf (stderr, "\n in: %05d", input[i]); + input[i] = LPFilter_Update(&state->RCFilter, input[i]); + // fprintf (stderr, "\n out: %05d", input[i]); + } +} + +//hpf +void hpf(dsd_state * state, short * input, int len) +{ + int i; + for (i = 0; i < len; i++) + { + // fprintf (stderr, "\n in: %05d", input[i]); + input[i] = HPFilter_Update(&state->HRCFilter, input[i]); + // fprintf (stderr, "\n out: %05d", input[i]); + } +} + +//hpf digital left +void hpf_dL(dsd_state * state, short * input, int len) +{ + int i; + for (i = 0; i < len; i++) + { + // fprintf (stderr, "\n in: %05d", input[i]); + input[i] = HPFilter_Update(&state->HRCFilterL, input[i]); + // fprintf (stderr, "\n out: %05d", input[i]); + } +} + +//hpf digital right +void hpf_dR(dsd_state * state, short * input, int len) +{ + int i; + for (i = 0; i < len; i++) + { + // fprintf (stderr, "\n in: %05d", input[i]); + input[i] = HPFilter_Update(&state->HRCFilterR, input[i]); + // fprintf (stderr, "\n out: %05d", input[i]); + } +} + +//nf +void nf(dsd_state * state, short * input, int len) +{ + int i; + for (i = 0; i < len; i++) + { + // fprintf (stderr, "\n in: %05d", input[i]); + input[i] = NOTCHFilter_Update(&state->NF, input[i]); + // fprintf (stderr, "\n out: %05d", input[i]); + } +} + +//pbf +void pbf(dsd_state * state, short * input, int len) +{ + int i; + for (i = 0; i < len; i++) + { + // fprintf (stderr, "\n in: %05d", input[i]); + input[i] = PBFilter_Update(&state->PBF, input[i]); + } +} + +//Generic RMS function derived from RTL_FM (RTL_SDR) RMS code (doesnt' really work correctly outside of RTL) +long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as len +{ + + int i; + long int rms; + long p, t, s; + double dc, err; + + p = t = 0L; + for (i=0; idmr_rest_channel, pslot, oslot); //disabled + else if (!clear && csbk_fid == 254) fprintf (stderr, " Cap+ Rest LSN Change: %d; Slot %d Free; Slot %d Busy; Remain on LSN;", state->dmr_rest_channel, pslot, oslot); //disabled + else if (clear && csbk_fid == 253) fprintf (stderr, " Cap+ Rest LSN Change: %d; No CSBK Channel Activity; Go To Rest LSN;", state->dmr_rest_channel); + else if (!clear && csbk_fid == 253) fprintf (stderr, " Cap+ Rest LSN Change: %d; CSBK Channel Activity; Remain on LSN;", state->dmr_rest_channel); //this should never happen in code else if (!clear) fprintf (stderr, " Slot %d Clear; Slot %d Busy; Remain on VC;", pslot, oslot); else if (clear == 1) fprintf (stderr, " Slot %d Clear; Slot %d Idle; Return to CC;", pslot, oslot); else if (clear == 2 || clear == 3) fprintf (stderr, " Slot %d Clear; Slot %d Free; Return to CC;", pslot, oslot); @@ -441,14 +445,14 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 //display/le/buzzer bug fix when p_clear activated (unsure why this was disabled) //clear only the current slot initially, then clear both if tuning to a different freq - if (state->currentslot == 0) + if (state->currentslot == 0 && csbk_fid != 253) //don't reset on Cap+ since we aren't testing based on the current TS { state->payload_mi = 0; state->payload_algid = 0; state->payload_keyid = 0; state->dmr_so = 0; } - if (state->currentslot == 1) + if (state->currentslot == 1 && csbk_fid != 253) { state->payload_miR = 0; state->payload_algidR = 0; @@ -1206,6 +1210,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 //tg and channel info for trunking purposes uint16_t t_tg[24]; char cap_active[20]; //local string to concantenate to active channel stuff + uint8_t empty[24]; //used to evaluate whether or not all channels are idle or not with memcmp after loading //sanity check if (block_num > 6) @@ -1218,6 +1223,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 memset (t_tg, 0, sizeof(t_tg)); memset (ch, 0, sizeof(ch)); memset (pch, 0, sizeof(pch)); + memset (empty, 0, sizeof(empty)); //treating FL as a form of LCSS if (fl == 2 || fl == 3) //initial or single block (fl2 or fl3) @@ -1234,6 +1240,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 state->cap_plus_block_num[ts]++; } + //move assignment until later when evaluating for a p_clear condition if (rest_channel != state->dmr_rest_channel) { state->dmr_rest_channel = rest_channel; @@ -1242,7 +1249,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 //assign to cc freq to follow during no sync if (state->trunk_chan_map[rest_channel] != 0) { - state->p25_cc_freq = state->trunk_chan_map[rest_channel]; + // state->p25_cc_freq = state->trunk_chan_map[rest_channel]; //set to always tuned opts->p25_is_tuned = 1; } @@ -1412,15 +1419,15 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 tg = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][(group_tally*8)+(x*16)+56], 16); //don't change this AGAIN!, this is correct! if (tg != 0) fprintf (stderr, "%5d; ", tg); else fprintf (stderr, " P||D; "); - //flag as available for tuning if data calls enabled - if (opts->trunk_tune_data_calls == 1) t_tg[i] = tg; //ch[i] = 1; + //flag as available for tuning if private calls enabled + if (opts->trunk_tune_private_calls == 1) t_tg[i] = tg; //ch[i] = 1; if (tg != 0) x++; //add active channel to display string //NOTE: Consider only adding this if user toggled or else //lots of short data bursts blink in and out - if (1 == 1) //opts->trunk_tune_data_calls + if (1 == 1) //opts->trunk_tune_private_calls { sprintf (cap_active, "LSN:%d PC:%d; ", i+1, tg); strcat (state->active_channel[i+1], cap_active); @@ -1515,6 +1522,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+1]); state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1]; opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop + state->last_vc_sync_time = time(NULL); j = 11; //break loop } @@ -1550,6 +1558,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 // else fprintf (stderr, "\n DONT RTL LSN/TG to tune to: %d - %d", j+1, t_tg[j]); //debug state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1]; opts->p25_is_tuned = 1; + state->last_vc_sync_time = time(NULL); j = 11; //break loop #endif } @@ -1577,6 +1586,25 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8 } memset (state->cap_plus_csbk_bits[ts], 0, sizeof(state->cap_plus_csbk_bits[ts])); state->cap_plus_block_num[ts] = 0; + + //check here to see if we can go to the current rest lsn, if not already on it + int busy = memcmp (empty, t_tg, sizeof(empty)); + + //testing (don't keep setting on quick data call LSN flip flops but same frequency for rest channel, other misc conditions) + // if (!busy && rest_channel != state->dmr_rest_channel && opts->p25_trunk == 1 && state->p25_cc_freq != state->trunk_chan_map[rest_channel]) + if (!busy && opts->p25_trunk == 1 && state->p25_cc_freq != state->trunk_chan_map[rest_channel]) + { + //assign now, ideally, this should always trigger a positive p_clear when needed + // state->dmr_rest_channel = rest_channel; + + //update frequency + if (state->trunk_chan_map[rest_channel] != 0) + state->p25_cc_freq = state->trunk_chan_map[rest_channel]; + + //Craft a fake CSBK pdu send it to run as a p_clear to go to rest channel if its available (no calls currently) + uint8_t dummy[12]; uint8_t* dbits; memset (dummy, 0, sizeof(dummy)); dummy[0] = 46; dummy[1] = 253; + dmr_cspdu (opts, state, dbits, dummy, 1, 0); + } } //if (fl == 1 || fl == 3) } //opcode == 0x3E diff --git a/src/dmr_flco.c b/src/dmr_flco.c index 9cb7f15..19cd3a6 100644 --- a/src/dmr_flco.c +++ b/src/dmr_flco.c @@ -354,15 +354,18 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C } - - if (restchannel != state->dmr_rest_channel && restchannel != -1) + //only assign this value here if not trunking + // if (opts->p25_trunk == 0) //may be safe to always do this now with code changes, will want to test at some point (tg hold w/ dual voice / slco may optimally need this set) { - state->dmr_rest_channel = restchannel; - //assign to cc freq - if (state->trunk_chan_map[restchannel] != 0) + if (restchannel != state->dmr_rest_channel && restchannel != -1) { - state->p25_cc_freq = state->trunk_chan_map[restchannel]; - } + state->dmr_rest_channel = restchannel; + //assign to cc freq + // if (state->trunk_chan_map[restchannel] != 0) + // { + // state->p25_cc_freq = state->trunk_chan_map[restchannel]; + // } + } } if (type == 1) fprintf (stderr, "%s \n", KGRN); @@ -1004,21 +1007,35 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]) else if (slco == 0xF) { fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest LSN: %d - RS: %02X", capsite, restchannel, cap_reserved); - //assign to cc freq if available - if (state->trunk_chan_map[restchannel] != 0) - { - state->p25_cc_freq = state->trunk_chan_map[restchannel]; - } //extra handling for TG hold while trunking enabled - if (state->tg_hold != 0 && opts->p25_trunk == 1) + if (state->tg_hold != 0 && opts->p25_trunk == 1) //logic seems to be fixed now for new rest lsn logic and other considerations { - //if both slots are voice, - if (state->dmrburstL == 16 && state->dmrburstR == 16) + //debug + // fprintf (stderr, " TG HOLD Both Slots Busy Check; "); + + //if both slots have some cobination of vlc, pi, voice, or tlc + int busy = 0; + if ( (state->dmrburstL == 16 || state->dmrburstL == 0 || state->dmrburstL == 1 || state->dmrburstL == 2) && + (state->dmrburstR == 16 || state->dmrburstR == 0 || state->dmrburstR == 1 || state->dmrburstR == 2)) busy = 1; + if (busy) { + + //debug + // fprintf (stderr, " Busy; "); + //but nether is the TG on hold if ( (state->tg_hold != state->lasttg) && (state->tg_hold != state->lasttgR) ) { + //debug + // fprintf (stderr, " Neither Slot is TG on Hold; "); + + //assign to cc freq if available -- move to right before needed for new logic on rest lsn + if (state->trunk_chan_map[restchannel] != 0) + { + state->p25_cc_freq = state->trunk_chan_map[restchannel]; + } + //tune to the current rest channel so we can observe its channel status csbks for the TG on hold if (state->p25_cc_freq != 0) { diff --git a/src/dpmr_voice.c b/src/dpmr_voice.c index 54a7c53..8981c1b 100644 --- a/src/dpmr_voice.c +++ b/src/dpmr_voice.c @@ -478,7 +478,7 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state) { processMbeFrame (opts, state, NULL, ambe_fr[i], NULL); if (opts->floating_point == 0) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1) playSynthesizedVoiceFM(opts, state); } diff --git a/src/dsd_audio2.c b/src/dsd_audio2.c index e149850..756e468 100644 --- a/src/dsd_audio2.c +++ b/src/dsd_audio2.c @@ -12,6 +12,9 @@ #include "dsd.h" #include +//NOTE: Tones produce ringing sound when put through the hpf_d, may want to look into tweaking it, +//or looking for a way to store is_tone by glancing at ambe_d values and not running hpf_d on them + //TODO: WAV File saving (works fine on shorts, but on float, writing short to wav is not auto-gained, //so super quiet, either convert to float wav files, or run processAudio AFTER memcpy of the temp_buf) @@ -721,6 +724,128 @@ void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state) } +//Mono - Short (SB16LE) - Drop-in replacement for playSyntesizedVoice, but easier to manipulate +void playSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state) +{ + int i; + size_t len = state->audio_out_idx; + + //debug + // fprintf (stderr, " L LEN: %d", len); + + short mono_samp[len]; + memset (mono_samp, 0, len*sizeof(short)); + + if (opts->slot1_on == 0) + goto MS_END; + + if (len == 160) + { + for (i = 0; i < len; i++) + mono_samp[i] = state->s_l[i]; + } + else if (len == 960) + { + state->audio_out_buf_p -= 960; //rewind first + for (i = 0; i < len; i++) + { + mono_samp[i] = *state->audio_out_buf_p; + state->audio_out_buf_p++; + } + } + + if (opts->use_hpf_d == 1) + hpf_dL(state, state->s_l, len); + + if (opts->audio_out_type == 0) //Pulse Audio + pa_simple_write(opts->pulse_digi_dev_out, mono_samp, len*2, NULL); + + if (opts->audio_out_type == 8) //UDP Audio + udp_socket_blaster (opts, state, len*2, mono_samp); + + if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS + write (opts->audio_out_fd, mono_samp, len*2); + + MS_END: + + //run cleanup since we pulled stuff from processAudio + state->audio_out_idx = 0; + + //set short temp buffer to baseline + memset (state->s_l, 0, sizeof(state->s_l)); + + 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; + } + +} + +//Mono - Short (SB16LE) - Drop-in replacement for playSyntesizedVoiceR, but easier to manipulate +void playSynthesizedVoiceMSR (dsd_opts * opts, dsd_state * state) +{ + int i; + size_t len = state->audio_out_idxR; + + //debug + // fprintf (stderr, " R LEN: %d", len); + + short mono_samp[len]; + memset (mono_samp, 0, len*sizeof(short)); + + if (opts->slot2_on == 0) + goto MS_ENDR; + + if (len == 160) + { + for (i = 0; i < len; i++) + mono_samp[i] = state->s_r[i]; + } + else if (len == 960) + { + state->audio_out_buf_pR -= 960; //rewind first + for (i = 0; i < len; i++) + { + mono_samp[i] = *state->audio_out_buf_pR; + state->audio_out_buf_pR++; + } + } + + if (opts->use_hpf_d == 1) + hpf_dR(state, mono_samp, len); + + if (opts->audio_out_type == 0) //Pulse Audio + pa_simple_write(opts->pulse_digi_dev_out, mono_samp, len*2, NULL); + + if (opts->audio_out_type == 8) //UDP Audio + udp_socket_blaster (opts, state, len*2, mono_samp); + + if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS + write (opts->audio_out_fd, mono_samp, len*2); + + MS_ENDR: + + //run cleanup since we pulled stuff from processAudioR + state->audio_out_idxR = 0; + + //set short temp buffer to baseline + memset (state->s_r, 0, sizeof(state->s_r)); + + 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; + } + +} + //Stereo Mix - Short (SB16LE) -- When Playing Short FDMA samples when setup for stereo output void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state) { @@ -728,7 +853,7 @@ void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state) int i; int encL; short stereo_samp1[320]; //8k 2-channel stereo interleave mix - memset (stereo_samp1, 1, sizeof(stereo_samp1)); + memset (stereo_samp1, 0, sizeof(stereo_samp1)); //enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = 0; @@ -785,6 +910,10 @@ void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state) //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; + //test hpf + if (opts->use_hpf_d == 1) + hpf_dL(state, state->s_l, 160); + //interleave left and right channels from the short storage area for (i = 0; i < 160; i++) { @@ -906,8 +1035,8 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state) //CHEAT: Using the slot on/off, use that to set encL or encR back on //as a simple way to turn off voice synthesis in a particular slot //its not really 'disabled', we just aren't playing it - if (opts->slot1_on == 0) encL = 1; - if (opts->slot2_on == 0) encR = 1; + // if (opts->slot1_on == 0) encL = 1; + // if (opts->slot2_on == 0) encR = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; @@ -944,42 +1073,182 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state) if (strcmp(modeL, "B") == 0) encL = 1; if (strcmp(modeR, "B") == 0) encR = 1; + //check to see if we need to enable slot and toggle slot preference here + //this method will always favor slot 2 (this is a patch anyways, so....meh) + // if (strcmp(modeL, "A") == 0) + // { + // opts->slot1_on = 1; + // opts->slot_preference = 0; + // } + // if (strcmp(modeR, "A") == 0) + // { + // opts->slot2_on = 1; + // opts->slot_preference = 1; + // } + + //check to see if we need to enable slot and toggle slot preference here + //if both groups allowed, then give no preference to either one (not sure this is needed now) + // if ( (strcmp(modeL, "A") == 0) && (strcmp(modeR, "A") == 0) ) + // { + // opts->slot1_on = 1; + // opts->slot2_on = 1; + // opts->slot_preference = 2; + // } + // else if (strcmp(modeL, "A") == 0) + // { + // opts->slot1_on = 1; + // opts->slot_preference = 0; + // } + // else if (strcmp(modeR, "A") == 0) + // { + // opts->slot2_on = 1; + // opts->slot_preference = 1; + // } + // else //if any other condition, then give no preference to either one + // { + // opts->slot1_on = 1; + // opts->slot2_on = 1; + // opts->slot_preference = 2; + // } + //if TG Hold in place, mute anything but that TG #132 - if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; - if (state->tg_hold != 0 && state->tg_hold != TGR) encR = 1; - //likewise, override and unmute if TG hold matches TG - if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; - if (state->tg_hold != 0 && state->tg_hold == TGR) encR = 0; + if (state->tg_hold != 0 && state->tg_hold != TGL) + encL = 1; + if (state->tg_hold != 0 && state->tg_hold != TGR) + encR = 1; - //interleave left and right channels from the short storage area - for (i = 0; i < 160; i++) + //likewise, override and unmute if TG hold matches TG (and turn on slot and set preference) + if (state->tg_hold != 0 && state->tg_hold == TGL) { - if (!encL) - stereo_samp1[i*2+0] = state->s_l4[0][i]; - if (!encR) - stereo_samp1[i*2+1] = state->s_r4[0][i]; + encL = 0; + opts->slot1_on = 1; + opts->slot_preference = 0; + } + else if (state->tg_hold != 0 && state->tg_hold == TGR) + { + encR = 0; + opts->slot2_on = 1; + opts->slot_preference = 1; + } + else //otherwise, reset slot preference to either or (both slots enabled) + { + opts->slot_preference = 2; } - for (i = 0; i < 160; i++) + //test hpf + if (opts->use_hpf_d == 1) { - if (!encL) - stereo_samp2[i*2+0] = state->s_l4[1][i]; - if (!encR) - stereo_samp2[i*2+1] = state->s_r4[1][i]; + hpf_dL(state, state->s_l4[0], 160); + hpf_dL(state, state->s_l4[1], 160); + hpf_dL(state, state->s_l4[2], 160); + + hpf_dR(state, state->s_r4[0], 160); + hpf_dR(state, state->s_r4[1], 160); + hpf_dR(state, state->s_r4[2], 160); } - for (i = 0; i < 160; i++) + //convert the left or right channel to both channels if single voice under certain conditions, if defined to do so + #define DMR_STEREO_OUTPUT + + #ifdef DMR_STEREO_OUTPUT + if (encL) memset (state->s_l4, 0, sizeof(state->s_l4)); + if (encR) memset (state->s_r4, 0, sizeof(state->s_r4)); + //this is for playing single voice over both channels, or when to keep them seperated + if (opts->slot1_on == 0 && opts->slot2_on == 1 && encR == 0) //slot 1 is hard off and slot 2 is on + memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left + else if (opts->slot1_on == 1 && opts->slot2_on == 0 && encL == 0) //slot 2 is hard off and slot 1 is on + memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right + else if (opts->slot_preference == 0 && state->dmrburstL == 16 && encL == 0) //slot 1 is preferred, and voice in slot 1 + memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right + else if (opts->slot_preference == 1 && state->dmrburstR == 16 && encR == 0) //slot 2 is preferred, and voice in slot 2 + memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left + else if (state->dmrburstL == 16 && state->dmrburstR != 16 && encL == 0) //voice in left, no voice in right + memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right + else if (state->dmrburstR == 16 && state->dmrburstL != 16 && encR == 0) //voice in right, no voice in left + memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left + //else if voice in both, and both slots on, and no preference on slot, then regular stereo interleave (left and right channels) + + //if both slots are the same now, then let's decimate the audio to keep the audio level consistent + // if (memcmp (state->s_l4, state->s_r4, sizeof(state->s_l4)) == 0) + // { + // for (int j = 0; j < 3; j++) + // { + // for (i = 0; i < 160; i++) + // { + // state->s_l4[j][i] *= 0.85; + // state->s_r4[j][i] *= 0.85; + // } + // } + // } + + #endif + + //check this last + if (opts->slot1_on == 0 && opts->slot2_on == 0) //both slots are hard off, disable playback { - if (!encL) - stereo_samp3[i*2+0] = state->s_l4[2][i]; - if (!encR) - stereo_samp3[i*2+1] = state->s_r4[2][i]; + encL = 1; + encR = 1; } //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions if (encL && encR) goto SS3_END; + //test hpf + // if (opts->use_hpf_d == 1) + // { + // hpf_dL(state, state->s_l4[0], 160); + // hpf_dL(state, state->s_l4[1], 160); + // hpf_dL(state, state->s_l4[2], 160); + + // hpf_dR(state, state->s_r4[0], 160); + // hpf_dR(state, state->s_r4[1], 160); + // hpf_dR(state, state->s_r4[2], 160); + // } + + //interleave left and right channels from the short storage area + for (i = 0; i < 160; i++) + { + #ifdef DMR_STEREO_OUTPUT + #else + if (!encL) + #endif + stereo_samp1[i*2+0] = state->s_l4[0][i]; + #ifdef DMR_STEREO_OUTPUT + #else + if (!encR) + #endif + stereo_samp1[i*2+1] = state->s_r4[0][i]; + } + + for (i = 0; i < 160; i++) + { + #ifdef DMR_STEREO_OUTPUT + #else + if (!encL) + #endif + stereo_samp2[i*2+0] = state->s_l4[1][i]; + #ifdef DMR_STEREO_OUTPUT + #else + if (!encR) + #endif + stereo_samp2[i*2+1] = state->s_r4[1][i]; + } + + for (i = 0; i < 160; i++) + { + #ifdef DMR_STEREO_OUTPUT + #else + if (!encL) + #endif + stereo_samp3[i*2+0] = state->s_l4[2][i]; + #ifdef DMR_STEREO_OUTPUT + #else + if (!encR) + #endif + stereo_samp3[i*2+1] = state->s_r4[2][i]; + } + if (opts->audio_out_type == 0) //Pulse Audio { pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*2, NULL); @@ -1045,7 +1314,7 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state) short stereo_samp2[320]; //8k 2-channel stereo interleave mix short stereo_samp3[320]; //8k 2-channel stereo interleave mix short stereo_samp4[320]; - + short empss[160]; //this is used to see if we want to run HPF on an empty set short empty[320]; //this is used to see if we want to play a single 2v or double 2v or not memset (stereo_samp1, 0, sizeof(stereo_samp1)); @@ -1054,7 +1323,7 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state) memset (stereo_samp4, 0, sizeof(stereo_samp4)); memset (empty, 0, sizeof(empty)); - + memset (empss, 0, sizeof(empss)); //p25p2 enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = encR = 1; @@ -1137,6 +1406,24 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state) if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; if (state->tg_hold != 0 && state->tg_hold == TGR) encR = 0; + //test hpf + if (opts->use_hpf_d == 1) + { + hpf_dL(state, state->s_l4[0], 160); + hpf_dL(state, state->s_l4[1], 160); + if (memcmp(empss, state->s_l4[2], sizeof(empss)) != 0) + hpf_dL(state, state->s_l4[2], 160); + if (memcmp(empss, state->s_l4[3], sizeof(empss)) != 0) + hpf_dL(state, state->s_l4[3], 160); + + hpf_dR(state, state->s_r4[0], 160); + hpf_dR(state, state->s_r4[1], 160); + if (memcmp(empss, state->s_r4[2], sizeof(empss)) != 0) + hpf_dR(state, state->s_r4[2], 160); + if (memcmp(empss, state->s_r4[3], sizeof(empss)) != 0) + hpf_dR(state, state->s_r4[3], 160); + } + //interleave left and right channels from the short storage area for (i = 0; i < 160; i++) { @@ -1238,6 +1525,284 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state) } +//short stereo mix 18v superframe +void playSynthesizedVoiceSS18 (dsd_opts * opts, dsd_state * state) +{ + + //NOTE: This will run once every superframe during a sacch field + //exact implementation to be determined + + int i, j; + uint8_t encL, encR; + + short stereo_sf[18][320]; //8k 2-channel stereo interleave mix for full superframe + // memset (stereo_sf, 1, 18*sizeof(short)); //I don't think 18*sizeof(short) was large enough, should probably be 18*320*sizeof(short) + memset (stereo_sf, 0, sizeof(stereo_sf)); + + short empty[320]; + memset (empty, 0, sizeof(empty)); + + //p25p2 enc checkdown for whether or not to fill the stereo sample or not for playback or writing + encL = encR = 1; + if (state->payload_algid == 0 || state->payload_algid == 0x80) + encL = 0; + if (state->payload_algidR == 0 || state->payload_algidR == 0x80) + encR = 0; + + //checkdown to see if we can lift the 'mute' if a key is available + if (encL) + { + if (state->payload_algid == 0xAA) + { + if (state->R != 0) + { + encL = 0; + } + } + } + + if (encR) + { + if (state->payload_algidR == 0xAA) + { + if (state->RR != 0) + { + encR = 0; + } + } + } + + //WIP: Mute if on B list (or not W list) + char modeL[8]; + sprintf (modeL, "%s", ""); + char modeR[8]; + sprintf (modeR, "%s", ""); + + int TGL = state->lasttg; + int TGR = state->lasttgR; + + //if we are using allow/whitelist mode, then write 'B' to mode for block + //comparison below will look for an 'A' to write to mode if it is allowed + if (opts->trunk_use_allow_list == 1) + { + sprintf (modeL, "%s", "B"); + sprintf (modeR, "%s", "B"); + + } + + for (i = 0; i < state->group_tally; i++) + { + if (state->group_array[i].groupNumber == TGL) + { + strcpy (modeL, state->group_array[i].groupMode); + // break; //need to keep going to check other potential slot group + } + if (state->group_array[i].groupNumber == TGR) + { + strcpy (modeR, state->group_array[i].groupMode); + // break; //need to keep going to check other potential slot group + } + } + + //flag either left or right as 'enc' to mute if B + if (strcmp(modeL, "B") == 0) encL = 1; + if (strcmp(modeR, "B") == 0) encR = 1; + + //check to see if we need to enable slot and toggle slot preference here + //this method will always favor slot 2 (this is a patch anyways, so....meh) + // if (strcmp(modeL, "A") == 0) + // { + // opts->slot1_on = 1; + // opts->slot_preference = 0; + // } + // if (strcmp(modeR, "A") == 0) + // { + // opts->slot2_on = 1; + // opts->slot_preference = 1; + // } + + //check to see if we need to enable slot and toggle slot preference here + //if both groups allowed, then give no preference to either one (not sure this is needed now) + // if ( (strcmp(modeL, "A") == 0) && (strcmp(modeR, "A") == 0) ) + // { + // opts->slot1_on = 1; + // opts->slot2_on = 1; + // opts->slot_preference = 2; + // } + // else if (strcmp(modeL, "A") == 0) + // { + // opts->slot1_on = 1; + // opts->slot_preference = 0; + // } + // else if (strcmp(modeR, "A") == 0) + // { + // opts->slot2_on = 1; + // opts->slot_preference = 1; + // } + // else //if any other condition, then give no preference to either one + // { + // opts->slot1_on = 1; + // opts->slot2_on = 1; + // opts->slot_preference = 2; + // } + + //if TG Hold in place, mute anything but that TG #132 + if (state->tg_hold != 0 && state->tg_hold != TGL) + encL = 1; + if (state->tg_hold != 0 && state->tg_hold != TGR) + encR = 1; + + //likewise, override and unmute if TG hold matches TG (and turn on slot and set preference) + if (state->tg_hold != 0 && state->tg_hold == TGL) + { + encL = 0; + opts->slot1_on = 1; + opts->slot_preference = 0; + } + else if (state->tg_hold != 0 && state->tg_hold == TGR) + { + encR = 0; + opts->slot2_on = 1; + opts->slot_preference = 1; + } + else //otherwise, reset slot preference to either or (both slots enabled) + { + opts->slot_preference = 2; + } + + //run hpf_d filter, if enabled + if (opts->use_hpf_d == 1) + { + for (j = 0; j < 18; j++) + { + hpf_dL(state, state->s_l4[j], 160); + hpf_dR(state, state->s_r4[j], 160); + } + } + + //convert the left or right channel to both channels if single voice under certain conditions, if defined to do so + #define P2_STEREO_OUTPUT + + #ifdef P2_STEREO_OUTPUT + if (encL) memset (state->s_l4, 0, sizeof(state->s_l4)); + if (encR) memset (state->s_r4, 0, sizeof(state->s_r4)); + //this is for playing single voice over both channels, or when to keep them seperated + if (opts->slot1_on == 0 && opts->slot2_on == 1 && encR == 0) //slot 1 is hard off and slot 2 is on + memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left + else if (opts->slot1_on == 1 && opts->slot2_on == 0 && encL == 0) //slot 2 is hard off and slot 1 is on + memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right + else if (opts->slot_preference == 0 && state->dmrburstL == 21 && encL == 0) //slot 1 is preferred, and voice in slot 1 + memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right + else if (opts->slot_preference == 1 && state->dmrburstR == 21 && encR == 0) //slot 2 is preferred, and voice in slot 2 + memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left + else if (state->dmrburstL == 21 && state->dmrburstR != 21 && encL == 0) //voice in left, no voice in right + memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right + else if (state->dmrburstR == 21 && state->dmrburstL != 21 && encR == 0) //voice in right, no voice in left + memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left + //else if voice in both, and both slots on, and no preference on slot, then regular stereo interleave (left and right channels) + + //if both slots are the same now, then let's decimate the audio to keep the audio level consistent + // if (memcmp (state->s_l4, state->s_r4, sizeof(state->s_l4)) == 0) + // { + // for (j = 0; j < 18; j++) + // { + // for (i = 0; i < 160; i++) + // { + // state->s_l4[j][i] *= 0.85; + // state->s_r4[j][i] *= 0.85; + // } + // } + // } + + #endif + + //check this last + if (opts->slot1_on == 0 && opts->slot2_on == 0) //both slots are hard off, disable playback + { + encL = 1; + encR = 1; + } + + //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions + if (encL && encR) + goto SS18_END; + + //interleave left and right channels from the short storage area + for (j = 0; j < 18; j++) + { + for (i = 0; i < 160; i++) + { + #ifdef P2_STEREO_OUTPUT + #else + if (!encL) + #endif + stereo_sf[j][i*2+0] = state->s_l4[j][i]; + #ifdef P2_STEREO_OUTPUT + #else + if (!encR) + #endif + stereo_sf[j][i*2+1] = state->s_r4[j][i]; + } + } + + if (opts->audio_out_type == 0) //Pulse Audio + { + for (j = 0; j < 18; j++) + { + if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) //may not work as intended because its stereo and one will have something in it most likely + pa_simple_write(opts->pulse_digi_dev_out, stereo_sf[j], 320*2, NULL); + } + } + + if (opts->audio_out_type == 8) //UDP Audio + { + for (j = 0; j < 18; j++) + { + if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) //may not work as intended because its stereo and one will have something in it most likely + udp_socket_blaster (opts, state, 320*2, stereo_sf[j]); + } + } + + + if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2channel + { + for (j = 0; j < 18; j++) + { + if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) //may not work as intended because its stereo and one will have something in it most likely + write (opts->audio_out_fd, stereo_sf[j], 320*2); + } + } + + SS18_END: + + //run cleanup since we pulled stuff from processAudio + state->audio_out_idx = 0; + state->audio_out_idxR = 0; + + //set float temp buffer to baseline + memset (state->s_l4, 0, sizeof(state->s_l4)); + memset (state->s_r4, 0, sizeof(state->s_r4)); + + 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; + } + + 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; + } + +} + //largely borrowed from Boatbod OP25 (simplified single tone ID version) void soft_tonef (float samp[160], int n, int ID, int AD) { @@ -1349,64 +1914,75 @@ void agf (dsd_opts * opts, dsd_state * state, float samp[160], int slot) } -//the previous version, not quite as good at audio gain normalization, but keeping it just in case -// void agf (dsd_opts * opts, dsd_state * state, float samp[160], int slot) -// { -// int i, run; -// run = 1; -// float empty[160]; -// memset (empty, 0.0f, sizeof(empty)); +//automatic gain short mono for analog audio and some digital mono (WIP) +void agsm (dsd_opts * opts, dsd_state * state, short * input, int len) +{ + int i; -// float mmax = 0.75f; -// float mmin = -0.75f; -// float aavg = 0.0f; //average of the absolute value -// float df; //decimation value -// df = 8000.0f; //test value -- this would be the ideal perfect value if everybody spoke directly into the mic at a reasonable volume + UNUSED(opts); -// if (slot == 0) -// df = (384.0f / (float)opts->pulse_digi_out_channels) * (50.0f - state->aout_gain); -// if (slot == 1) -// df = (384.0f / (float)opts->pulse_digi_out_channels) * (50.0f - state->aout_gainR); + //NOTE: This seems to be doing better now that I got it worked out properly + //This may produce a mild buzz sound though on the low end -// //this comparison is to determine whether or not to run gain on 'empty' samples (2v last 2, silent frames, etc) -// if (memcmp(empty, samp, sizeof(empty)) == 0) run = 0; -// if (run == 0) goto AGF_END; + float avg = 0.0f; //average of 20 samples + float coeff = 0.0f; //gain coeffiecient + float max = 0.0f; //the highest sample value + float nom = 4800.0f; //nominator value for 48k + float samp[960]; memset (samp, 0.0f, 960*sizeof(float)); -// for (i = 0; i < 160; i++) -// { + //assign internal float from short input + for (i = 0; i < len; i++) + samp[i] = (float)input[i]; -// samp[i] = samp[i] / df; + for (i = 0; i < len; i++) + { + if ( fabsf (samp[i]) > max) + { + max = fabsf (samp[i]); + } + } -// aavg += fabsf(samp[i]); + for (i = 0; i < len; i++) + avg += (float)samp[i]; -// //simple clipping -// if (samp[i] > mmax) -// samp[i] = mmax; -// if (samp[i] < mmin) -// samp[i] = mmin; - -// //may have been a tad too loud on the high end -// // samp[i] *= 0.5f; //testing various values here - -// } - -// aavg /= 160.0f; - -// if (slot == 0) -// { -// if (aavg < 0.075f && state->aout_gain < 42.0f) state->aout_gain += 1.0f; -// if (aavg >= 0.075f && state->aout_gain > 1.0f) state->aout_gain -= 1.0f; -// } - -// if (slot == 1) -// { -// if (aavg < 0.075f && state->aout_gainR < 42.0f) state->aout_gainR += 1.0f; -// if (aavg >= 0.075f && state->aout_gainR > 1.0f) state->aout_gainR -= 1.0f; -// } - -// //debug -// // fprintf (stderr, "\nS%d - DF = %f AAVG = %f", slot, df, aavg); + avg /= (float)len; -// AGF_END: ; //do nothing + coeff = fabsf (nom / max); -// } \ No newline at end of file + //keep coefficient with tolerable range when silence to prevent crackle/buzz + if (coeff > 3.0f) coeff = 3.0f; + + //apply the coefficient to bring the max value to our desired maximum value + for (i = 0; i < 20; i++) + samp[i] *= coeff; + + //debug + // fprintf (stderr, "\n M: %f; C: %f; A: %f; ", max, coeff, avg); + + // debug + // for (i = 0; i < len; i++) + // { + // fprintf (stderr, " in: %d", input[i]); + // fprintf (stderr, " out: %f", samp[i]); + // } + + //return new smaple values post agc + for (i = 0; i < len; i++) + input[i] = (short)samp[i]; + + state->aout_gainA = coeff; //store for internal use + +} + +//until analog agc is fixed, going to use a manual gain control on this +void analog_gain (dsd_opts * opts, dsd_state * state, short * input, int len) +{ + + int i; + UNUSED(state); + + float gain = (opts->audio_gainA / 100.0f) * 5.0f; //scale 0x - 5x + + for (i = 0; i < len; i++) + input[i] *= gain; +} \ No newline at end of file diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index 4a64b7b..e10f3cc 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -195,7 +195,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) int lmin, lmax, lidx; //assign t_max value based on decoding type expected (all non-auto decodes first) - int t_max; //maximum values allowed for t will depend on decoding type - NXDN will be 10, others will be more + int t_max = 24; //initialize as an actual value to prevent any overflow related issues if (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1) { t_max = 10; @@ -218,6 +218,10 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { t_max = 19; //Phase 2 S-ISCH is only 19 } + else if (opts->analog_only == 1) //shim to make sure this is set to a reasonable value + { + t_max = 24; + } else t_max = 24; //24 for everything else int lbuf[48], lbuf2[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max diff --git a/src/dsd_main.c b/src/dsd_main.c index 05df745..3ca7788 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -382,6 +382,8 @@ noCarrier (dsd_opts * opts, dsd_state * state) } state->fourv_counter[0] = 0; state->fourv_counter[1] = 0; + state->voice_counter[0] = 0; + state->voice_counter[1] = 0; //values displayed in ncurses terminal // state->p25_vc_freq[0] = 0; @@ -583,6 +585,7 @@ initOpts (dsd_opts * opts) opts->mbe_out_fR = NULL; //second slot on a TDMA system opts->audio_gain = 0; opts->audio_gainR = 0; + opts->audio_gainA = 50.0f; //scale of 1 - 100 opts->audio_out = 1; opts->wav_out_file[0] = 0; opts->wav_out_fileR[0] = 0; @@ -691,6 +694,7 @@ initOpts (dsd_opts * opts) opts->dmr_mute_encR = 1; opts->monitor_input_audio = 0; //enable with -8 + opts->analog_only = 0; //only turned on with -fA opts->inverted_p2 = 0; opts->p2counter = 0; @@ -760,12 +764,21 @@ initOpts (dsd_opts * opts) //slot preference is used during OSS audio playback to //prefer one tdma voice slot over another when both are playing back //this is a fix to OSS 48k/1 output + #ifdef AERO_BUILD opts->slot_preference = 0; //default prefer slot 1 -- state->currentslot = 0; - + #else + opts->slot_preference = 2; //use 2 since integrating the Stereo Channel Patch; + #endif //hardset slots to synthesize opts->slot1_on = 1; opts->slot2_on = 1; + //enable filter options + opts->use_lpf = 0; + opts->use_hpf = 1; + opts->use_pbf = 1; + opts->use_hpf_d = 1; + //dsp structured file opts->dsp_out_file[0] = 0; opts->use_dsp_output = 0; @@ -899,6 +912,7 @@ initState (dsd_state * state) sprintf (state->slot2light, "%s", ""); state->aout_gain = 25.0f; state->aout_gainR = 25.0f; + state->aout_gainA = 0.0f; //use purely as a display or internal value, no user setting memset (state->aout_max_buf, 0, sizeof (float) * 200); state->aout_max_buf_p = state->aout_max_buf; state->aout_max_buf_idx = 0; @@ -956,6 +970,8 @@ initState (dsd_state * state) } state->fourv_counter[0] = 0; state->fourv_counter[1] = 0; + state->voice_counter[0] = 0; + state->voice_counter[1] = 0; state->K = 0; state->R = 0; @@ -1047,8 +1063,8 @@ initState (dsd_state * state) //edacs - may need to make these user configurable instead for stability on non-ea systems state->ea_mode = -1; //init on -1, 0 is standard, 1 is ea - state->esk_mode = -1; //same as above, but with esk or not - state->esk_mask = 0x0; //toggles from 0x0 to 0xA0 if esk mode enabled + state->edacs_vc_call_type = 0; + state->esk_mask = 0x0; //esk mask value state->edacs_site_id = 0; state->edacs_lcn_count = 0; state->edacs_cc_lcn = 0; @@ -1236,7 +1252,7 @@ usage () printf (" /dev/dsp for OSS audio (Depreciated: Will require padsp wrapper in Linux) \n"); #endif printf (" rtl for rtl dongle (Default Values -- see below)\n"); - printf (" rtl:dev:freq:gain:ppm:bw:sq:udp for rtl dongle (see below)\n"); + printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n"); printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n"); printf (" tcp:192.168.7.5:7355 for custom address and port \n"); printf (" filename.bin for OP25/FME capture bin files\n"); @@ -1283,12 +1299,12 @@ usage () // #ifdef AERO_BUILD printf (" If using /dev/dsp input and output at 48k1, launch two instances of DSD-FME w -V 1 and -V 2 if needed"); // #endif - printf (" (Audio Smoothing is now disabled on all upsampled output by default -- fix crackle/buzz bug)\n"); + // printf (" (Audio Smoothing is now disabled on all upsampled output by default -- fix crackle/buzz bug)\n"); printf (" -z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)\n"); printf (" -y Enable Experimental Pulse Audio Float Audio Output\n"); printf ("\n"); printf ("RTL-SDR options:\n"); - printf (" Usage: rtl:dev:freq:gain:ppm:bw:sq:udp\n"); + printf (" Usage: rtl:dev:freq:gain:ppm:bw:sq:vol\n"); printf (" NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended\n"); printf (" dev RTL-SDR Device Index Number or 8 Digit Serial Number, no strings! (default 0)\n"); printf (" freq RTL-SDR Frequency (851800000 or 851.8M) \n"); @@ -1296,9 +1312,10 @@ usage () printf (" ppm RTL-SDR PPM Error (default = 0)\n"); printf (" bw RTL-SDR Bandwidth kHz (default = 12)(4, 6, 8, 12, 16, 24) \n"); printf (" sq RTL-SDR Squelch Level vs RMS Value (Optional)\n"); - printf (" udp RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only)\n"); + // printf (" udp RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only)\n"); //NOTE: This is still available as an option in the ncurses menu + printf (" vol RTL-SDR Sample 'Volume' Multiplier (default = 1)(1,2,3)\n"); printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n"); - printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:6021\n"); + printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n"); printf ("\n"); printf ("Encoder options:\n"); printf (" -fZ M17 Stream Voice Encoder\n"); @@ -1333,7 +1350,11 @@ usage () printf (" -fz Decode only M17*\n"); printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n"); printf (" -fn Decode only NXDN96* (12.5 kHz)\n"); - printf (" -fp Decode only EDACS/ProVoice*\n"); + printf (" -fp Decode only ProVoice*\n"); + printf (" -fh Decode only EDACS Standard/ProVoice*\n"); + printf (" -fH Decode only EDACS Standard/ProVoice with ESK 0xA0*\n"); + printf (" -fe Decode only EDACS EA/ProVoice*\n"); + printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n"); printf (" -fm Decode only dPMR*\n"); printf (" -l Disable DMR, dPMR, and NXDN input filtering\n"); printf (" -u Unvoiced speech quality (default=3)\n"); @@ -1416,7 +1437,7 @@ usage () printf (" P25 - 12000; NXDN48 - 7000; NXDN96: 12000; DMR - 7000-12000; EDACS/PV - 12000-24000;\n"); //redo this, or check work, or whatever printf (" May vary based on system stregnth, etc.\n"); printf (" -t Set Trunking or Scan Speed VC/sync loss hangtime in seconds. (default = 1 second)\n"); - printf (" -9 Force Enable EDACS Standard or Networked Mode if Auto Detection Fails \n"); + // printf (" -9 Force Enable EDACS Standard or Networked Mode if Auto Detection Fails \n"); printf ("\n"); printf (" Trunking Example TCP: dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans\n"); printf (" Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans\n"); @@ -1636,7 +1657,7 @@ main (int argc, char **argv) initOpts (&opts); initState (&state); - + init_audio_filters(&state); //audio filters InitAllFecFunction(); // CNXDNConvolution_init(); //seems to function better without initting it @@ -1684,9 +1705,10 @@ main (int argc, char **argv) fprintf (stderr, "TG Hold set to %d \n", state.tg_hold); break; - case '9': //This is a temporary fix for RR issue until a permanent fix can be found + case '9': //Leaving Enabled to maintain backwards compatability state.ea_mode = 0; - fprintf (stderr,"Force Enabling EDACS Standard/Networked Mode Mode\n"); + state.esk_mask = 0; + fprintf (stderr,"Force Enabling EDACS Standard/Networked Mode Mode without ESK.\n"); break; //experimental audio monitoring @@ -2022,6 +2044,7 @@ main (int argc, char **argv) case 'g': sscanf (optarg, "%f", &opts.audio_gain); + opts.audio_gainA = opts.audio_gain; //straight assignment if (opts.audio_gain < (float) 0 ) { fprintf (stderr,"Disabling audio out gain setting\n"); @@ -2103,6 +2126,7 @@ main (int argc, char **argv) opts.dmr_mono = 0; state.rf_mod = 0; opts.monitor_input_audio = 1; + opts.analog_only = 1; sprintf (opts.output_name, "Analog Monitor"); fprintf (stderr,"Only Monitoring Passive Analog Signal\n"); } @@ -2173,11 +2197,150 @@ main (int argc, char **argv) opts.dmr_stereo = 0; opts.dmr_mono = 0; state.dmr_stereo = 0; - // opts.setmod_bw = 12500; + // opts.setmod_bw = 16000; sprintf (opts.output_name, "EDACS/PV"); fprintf (stderr,"Setting symbol rate to 9600 / second\n"); fprintf (stderr,"Decoding only ProVoice frames.\n"); fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //misc tweaks + opts.rtl_bandwidth = 24; + } + else if (optarg[0] == 'h') //standard / net w/o ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 0; + state.esk_mask = 0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS STD/NET and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } + else if (optarg[0] == 'H') //standard / net w/ ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 0; + state.esk_mask = 0xA0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS STD/NET w/ ESK and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } + else if (optarg[0] == 'e') //extended addressing w/o ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 1; + state.esk_mask = 0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS Extended Addressing and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } + else if (optarg[0] == 'E') //extended addressing w/ ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 1; + state.esk_mask = 0xA0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS Extended Addressing w/ ESK and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); //rtl specific tweaks opts.rtl_bandwidth = 24; // opts.rtl_gain_value = 36; @@ -2466,6 +2629,11 @@ main (int argc, char **argv) opts.m17encoder = 1; opts.pulse_digi_rate_out = 48000; opts.pulse_digi_out_channels = 1; + //filters disabled by default, use ncurses VBN switches + opts.use_lpf = 0; + opts.use_hpf = 0; + opts.use_pbf = 0; + opts.dmr_stereo = 0; sprintf (opts.output_name, "M17 Encoder"); } else if (optarg[0] == 'B') //Captial B to Run the M17 BRT encoder @@ -2756,8 +2924,12 @@ main (int argc, char **argv) if (curr != NULL) opts.rtl_squelch_level = atoi (curr); else goto RTLEND; - curr = strtok(NULL, ":"); //rtl udp port "-U" - if (curr != NULL) opts.rtl_udp_port = atoi (curr); + // curr = strtok(NULL, ":"); //rtl udp port "-U" + // if (curr != NULL) opts.rtl_udp_port = atoi (curr); + // else goto RTLEND; + + curr = strtok(NULL, ":"); //rtl sample / volume multiplier + if (curr != NULL) opts.rtl_volume_multiplier = atoi (curr); else goto RTLEND; RTLEND: @@ -2789,15 +2961,19 @@ main (int argc, char **argv) } + if (opts.rtl_volume_multiplier > 3 || opts.rtl_volume_multiplier < 0) + opts.rtl_volume_multiplier = 1; //I wonder if you could flip polarity by using -1 + fprintf (stderr, "Dev %d ", opts.rtl_dev_index); fprintf (stderr, "Freq %d ", opts.rtlsdr_center_freq); fprintf (stderr, "Gain %d ", opts.rtl_gain_value); fprintf (stderr, "PPM %d ", opts.rtlsdr_ppm_error); fprintf (stderr, "BW %d ", opts.rtl_bandwidth); fprintf (stderr, "SQ %d ", opts.rtl_squelch_level); - fprintf (stderr, "UDP %d \n", opts.rtl_udp_port); + // fprintf (stderr, "UDP %d \n", opts.rtl_udp_port); + fprintf (stderr, "VOL %d \n", opts.rtl_volume_multiplier); opts.audio_in_type = 3; - state.audio_smoothing = 0; //disable smoothing to prevent random crackling/buzzing + // opts.rtl_volume_multiplier = 2; //TODO: Make this an extra value on the end rtl_ok = 1; #endif diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index d1d58e9..07075c8 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -134,7 +134,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv) if (opts->audio_out == 1 && opts->floating_point == 0) { - playSynthesizedVoice (opts, state); + playSynthesizedVoiceMS (opts, state); } if (opts->floating_point == 1) { @@ -174,7 +174,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv) if (opts->audio_out == 1 && opts->floating_point == 0) { - playSynthesizedVoice (opts, state); + playSynthesizedVoiceMS (opts, state); } if (opts->floating_point == 1) { @@ -1084,7 +1084,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if (opts->audio_out == 1 && opts->floating_point == 0 && opts->audio_out_type == 5 && opts->slot1_on == 1) //for OSS 48k 1 channel configs -- relocate later if possible { - playSynthesizedVoice (opts, state); //it may be more beneficial to move this to each individual decoding type to handle, but ultimately, let's just simpifly mbe handling instead + playSynthesizedVoiceMS (opts, state); //it may be more beneficial to move this to each individual decoding type to handle, but ultimately, let's just simpifly mbe handling instead } } @@ -1161,7 +1161,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a } if (opts->audio_out == 1 && opts->floating_point == 0 && opts->audio_out_type == 5 && opts->slot2_on == 1) //for OSS 48k 1 channel configs -- relocate later if possible { - playSynthesizedVoiceR (opts, state); + playSynthesizedVoiceMSR (opts, state); } } diff --git a/src/dsd_mbe2.c b/src/dsd_mbe2.c index 76dc0fe..55dcebb 100644 --- a/src/dsd_mbe2.c +++ b/src/dsd_mbe2.c @@ -115,7 +115,7 @@ void soft_mbe (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char amb PrintAMBEData (opts, state, ambe_d); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) playSynthesizedVoiceFM(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) @@ -138,7 +138,7 @@ void soft_mbe (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char amb PrintAMBEData (opts, state, ambe_d); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) playSynthesizedVoiceFM(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 4535aa2..73965dc 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -7,6 +7,9 @@ * * LWVMOBILE * 2022-08 DSD-FME Florida Man Edition + * + * ilyacodes + * 2024-03 EDACS-FME display improvements *-----------------------------------------------------------------------------*/ @@ -110,13 +113,13 @@ char * DMRBusrtTypes[32] = { "R34D ", "IDLE ", "R1_D ", - "ERR ", + "ERR ", "DUID ERR ", "R-S ERR ", "CRC ERR ", "NULL ", - "VOICE", - " ", + "VOICE", + " ", "INIT ", "INIT ", "PTT", //20 MAC @@ -188,7 +191,7 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) pa_simple_write(opts->pulse_digi_dev_out, samp_s, 160*2, NULL); - + } else if (opts->audio_out_type == 8) //UDP Audio @@ -204,7 +207,7 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) udp_socket_blaster (opts, state, 160*2, samp_s); - + } else if (opts->audio_out_type == 1) //STDOUT @@ -245,10 +248,10 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) samp_su[(i*6)+4] = outbuf[4]; samp_su[(i*6)+5] = outbuf[5]; } - + write (opts->audio_out_fd, samp_su, 960*2); } - + } } @@ -328,7 +331,7 @@ char *choicesc[] = { "Setup and Start RTL Input ", "Retune RTL Dongle ", "Toggle C4FM/QPSK (P2 TDMA CC)", - "Toggle C4FM/QPSK (P1 FDMA CC)", + "Toggle C4FM/QPSK (P1 FDMA CC)", "Start TCP Direct Link Audio", "Configure RIGCTL", "Stop All Decoded WAV Saving", @@ -415,7 +418,7 @@ void ncursesOpen (dsd_opts * opts, dsd_state * state) //this is primarily used to push a quick audio blip through OSS so it will show up in the mixer immediately // if (opts->audio_out_type == 2 || opts->audio_out_type == 5) // beeper (opts, state, 0); //causes crash in Cygwin when mixed input/output - + //terminate all values for (int i = 0; i < 10; i++) sprintf (alias_ch[i], "%s", ""); @@ -462,7 +465,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) closePulseOutput (opts); } - //close OSS output + //close OSS output if (opts->audio_out_type == 2 || opts->audio_out_type == 5) { close (opts->audio_out_fd); @@ -725,6 +728,17 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) wscanw(entry_win, "%d", &opts->rtl_udp_port); noecho(); + entry_win = newwin(6, WIDTH+18, starty+10, startx+10); + box (entry_win, 0, 0); + mvwprintw(entry_win, 2, 2, " RTL Vol Multiplier (1,2,3) (Default = 1): "); + mvwprintw(entry_win, 3, 3, " "); + echo(); + refresh(); + wscanw(entry_win, "%d", &opts->rtl_volume_multiplier); + if (opts->rtl_volume_multiplier > 3 || opts->rtl_volume_multiplier < 0) + opts->rtl_volume_multiplier = 1; + noecho(); + entry_win = newwin(8, WIDTH+22, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL RMS Squelch Level (NXDN/dPMR/Analog/Raw only): "); @@ -760,22 +774,23 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->rtl_dev_index = i; break; } - + } - entry_win = newwin(17, WIDTH+20, starty+10, startx+10); + entry_win = newwin(18, WIDTH+20, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Starting RTL Input. Cannot Release/Stop Until Exit."); mvwprintw(entry_win, 4, 2, " RTL Frequency: %d Hz", opts->rtlsdr_center_freq); mvwprintw(entry_win, 5, 2, " RTL Device Index Number: %d; SN:%s", opts->rtl_dev_index, serial); mvwprintw(entry_win, 6, 2, " RTL Device Bandwidth: %d kHz", opts->rtl_bandwidth); mvwprintw(entry_win, 7, 2, " RTL Device Gain: %d", opts->rtl_gain_value); - mvwprintw(entry_win, 8, 2, " RTL Device UDP Port: %d", opts->rtl_udp_port); - mvwprintw(entry_win, 9, 2, " RTL Device PPM: %d", opts->rtlsdr_ppm_error); - mvwprintw(entry_win, 10, 2, " RTL RMS Squelch: %d", opts->rtl_squelch_level); - mvwprintw(entry_win, 12, 2, " Are You Sure?"); - mvwprintw(entry_win, 13, 2, " 1 = Yes, 2 = No "); - mvwprintw(entry_win, 14, 3, " "); + mvwprintw(entry_win, 8, 2, " RTL Volume Multiplier: %d", opts->rtl_volume_multiplier); + mvwprintw(entry_win, 9, 2, " RTL Device UDP Port: %d", opts->rtl_udp_port); + mvwprintw(entry_win, 10, 2, " RTL Device PPM: %d", opts->rtlsdr_ppm_error); + mvwprintw(entry_win, 11, 2, " RTL RMS Squelch: %d", opts->rtl_squelch_level); + mvwprintw(entry_win, 13, 2, " Are You Sure?"); + mvwprintw(entry_win, 14, 2, " 1 = Yes, 2 = No "); + mvwprintw(entry_win, 15, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &confirm); @@ -861,7 +876,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) // if (opts->audio_out == 0) // { // opts->audio_out = 1; - // opts->audio_out_type = 0; + // opts->audio_out_type = 0; // // state->audio_out_buf_p = 0; // // state->audio_out_buf_pR = 0; // state->audio_out_idx = 0; @@ -933,7 +948,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->audio_in_type = 0; } else opts->audio_in_type = 5; - + } state->audio_smoothing = 0; //disable smoothing to prevent random crackling/buzzing @@ -1222,7 +1237,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) wscanw(entry_win, "%lld", &state->R); noecho(); if (state->R > 0x7FFF) state->R = 0x7FFF; - + state->keyloader = 0; //turn off keyloader } //toggle enforcement of basic privacy key over enc bit set on traffic @@ -1844,7 +1859,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) { openOSSOutput (opts); } - + if (opts->audio_in_type == 0) //reopen pulse input if it is the specified input method { @@ -1854,7 +1869,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 3) //open rtl input if it is the specified input method { // ncursesPrinter (opts, state); //not sure why this was placed here originally, but causes a double free core dump when calling free(timestr) - #ifdef USE_RTLSDR + #ifdef USE_RTLSDR if (opts->rtl_started == 0) { opts->rtl_started = 1; //set here so ncurses terminal doesn't attempt to open it again @@ -1892,7 +1907,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type != 1) //can't run getch/menu when using STDIN - { - timeout(0); // + timeout(0); // c = getch(); // } @@ -1908,7 +1923,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Variable reset/set section //set lls sync types - if (state->synctype >= 0 && state->synctype < 39) + if (state->synctype >= 0 && state->synctype < 39) { lls = state->synctype; } @@ -2002,16 +2017,23 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Edacs - ProVoice if ( (lls == 14 || lls == 15 || lls == 37 || lls == 38) && state->carrier == 1) { - - if (state->edacs_vc_lcn != -1) + + if (state->edacs_vc_lcn != -1) { call_matrix[state->edacs_vc_lcn][0] = lls; call_matrix[state->edacs_vc_lcn][1] = state->edacs_vc_lcn; - call_matrix[state->edacs_vc_lcn][2] = state->lasttg; - call_matrix[state->edacs_vc_lcn][3] = state->lastsrc; - call_matrix[state->edacs_vc_lcn][4] = 1; + call_matrix[state->edacs_vc_lcn][2] = state->lasttg; + //EDACS standard does not provide source LIDs on channel update messages; instead, for the sake of display, let's + //assume the prior source for a given LCN is still accurate, unless we have an updated one provided (or the call + //type has changed under us). + // + //If you MUST have perfectly-accurate source LIDs, look at the logged CC messages yourself - incorrect source LIDs + //may be displayed if we miss an initial call channel assignment. + if (state->ea_mode == 1 || (state->lastsrc != 0 || call_matrix[state->edacs_vc_lcn][4] != state->edacs_vc_call_type)) + call_matrix[state->edacs_vc_lcn][3] = state->lastsrc; + call_matrix[state->edacs_vc_lcn][4] = state->edacs_vc_call_type; call_matrix[state->edacs_vc_lcn][5] = time(NULL); - } + } } @@ -2074,7 +2096,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } } - + //TODO: Find better placement for these if ( strcmp(state->str50a, "") != 0 ) sprintf (alias_ch[9], "%s", state->str50a); @@ -2257,7 +2279,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // state->lastsrc = 0; // rd = 0; // tg = 0; - // } + // } // if (state->dmrburstR == 2 && state->dmr_end_alert[1] == 0) //if TLC and flag not tripped // { // beeper (opts, state, 1); @@ -2266,7 +2288,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // state->lastsrcR = 0; // rdR = 0; // tgR = 0; - // } + // } // } //Start Printing Section @@ -2276,7 +2298,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("------------------------------------------------------------------------------\n"); printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)"); - printw ("------------------------------------------------------------------------------\n"); + printw ("------------------------------------------------------------------------------\n"); } #elif LIMAZULUTWEAKS if (opts->ncurses_compact == 1) @@ -2318,7 +2340,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) #elif ZDEV_BUILD if (i == 5) printw (" %s ", "AW "); if (i == 6) printw (" %s \n", GIT_TAG); - #else + #else if (i == 5) printw (" %s ", "AW "); if (i == 6) printw (" %s \n", GIT_TAG); #endif @@ -2334,7 +2356,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("--Input Output----------------------------------------------------------------\n"); if (opts->audio_in_type == 0) { - printw ("| Pulse Audio Input: %i kHz; %i Channel; ", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels); + printw ("| Pulse Signal Input: %i kHz; %i Ch; ", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels); if (opts->use_rigctl == 1) printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno); printw ("\n"); @@ -2342,7 +2364,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 5) { - printw ("| OSS Audio Input: %i kHz; 1 Channel;", SAMPLE_RATE_IN/1000); + printw ("| OSS Signal Input: %i kHz; 1 Ch;", SAMPLE_RATE_IN/1000); if (opts->use_rigctl == 1) printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno); printw ("\n"); @@ -2355,7 +2377,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 8) { - printw ("| TCP Audio Input: %s:%d; %d kHz 1 Channel; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000); + printw ("| TCP Signal Input: %s:%d; %d kHz; 1 Ch; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000); if (opts->use_rigctl == 1) printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno); printw ("\n"); @@ -2376,25 +2398,27 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("| RTL: %d;", opts->rtl_dev_index); if (opts->rtl_gain_value == 0) - printw (" Gain: AGC;"); + printw (" G: AGC;"); else - printw (" Gain: %idB;", opts->rtl_gain_value); + printw (" G: %idB;", opts->rtl_gain_value); + printw (" V: %iX;", opts->rtl_volume_multiplier); printw (" PPM: %i;", opts->rtlsdr_ppm_error); printw (" SQ: %i;", opts->rtl_squelch_level); printw (" RMS: %04li;", opts->rtl_rms); - printw (" BW: %i kHz;", opts->rtl_bandwidth); - printw (" FRQ: %i;", opts->rtlsdr_center_freq); + printw (" BW: %i;", opts->rtl_bandwidth); + printw (" FRQ: %i;", opts->rtlsdr_center_freq); if (opts->rtl_udp_port != 0) printw ("\n| External RTL Tuning on UDP Port: %i", opts->rtl_udp_port); printw ("\n"); } if (opts->audio_out_type == 0) { - printw ("| Pulse Audio Output: %i kHz; %i Ch; G: %02.0f%%", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); + printw ("| Pulse Digital Output: %i kHz; %i Ch; G: %02.0f%%", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); if (opts->floating_point == 1) printw (" FLOAT: %02.0f%%;", opts->audio_gain*2); - if (opts->audio_gain == 0) printw (" (+/-) Auto"); - if (opts->audio_gain > 0) printw (" (+/-) Manual"); + if (opts->audio_gain == 0) printw (" (+|-) Auto"); + if (opts->audio_gain > 0) printw (" (+|-) Manual"); + if (opts->use_hpf_d == 1) printw (" HPF"); if (opts->call_alert == 1) printw (" *CA!"); //Call Alert // if (state->audio_smoothing == 1 && opts->floating_point == 0) printw (" Smoothing On;"); //only on short printw (" \n"); @@ -2402,7 +2426,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if ( opts->audio_out_type == 0 && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) { - printw ("| Pulse Audio Output: %i kHz; %i Ch; Analog Monitor RMS: %04ld ", opts->pulse_raw_rate_out/1000, opts->pulse_raw_out_channels, opts->rtl_rms); + printw ("| Pulse Analog Output: %i kHz; %i Ch; G: %02.0f%% (/|*) ", opts->pulse_raw_rate_out/1000, opts->pulse_raw_out_channels, opts->audio_gainA); + if (opts->audio_gainA == 0.0f) printw ("Auto "); + else printw ("Manual "); + if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms); + if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); + if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); + if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); printw (" \n"); } @@ -2412,6 +2442,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); if (opts->audio_gain == 0) printw (" (+/-) Auto"); if (opts->audio_gain > 0) printw (" (+/-) Manual"); + if (opts->use_hpf_d == 1) printw (" HPF"); if (opts->call_alert == 1) printw (" *CA!"); //Call Alert // if (state->audio_smoothing == 1 && opts->floating_point == 0) printw (" Smoothing On;"); //only on short if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) @@ -2421,17 +2452,22 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_out_type == 8) { - printw ("| UDP Audio Output: %s:%d; %d kHz %d Ch; %02.0f%%", opts->udp_hostname, opts->udp_portno, opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); + printw ("| UDP Digital Output: %s:%d; %d kHz %d Ch; %02.0f%%", opts->udp_hostname, opts->udp_portno, opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); if (opts->audio_gain == 0) printw (" (+/-) Auto"); if (opts->audio_gain > 0) printw (" (+/-) Manual"); + if (opts->use_hpf_d == 1) printw (" HPF"); if (opts->call_alert == 1) printw (" *CA!"); //Call Alert if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) printw (" - Monitor RMS: %04ld ", opts->rtl_rms); printw (" \n"); if (opts->udp_sockfdA != 0) //Analog Output on udp port +2 { - printw ("| UDP Audio Output: %s:%d; 48 kHz 1 Ch; Analog Output; ", opts->udp_hostname, opts->udp_portno+2); + printw ("| UDP Analog Output: %s:%d; 48 kHz 1 Ch; ", opts->udp_hostname, opts->udp_portno+2); + if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms); + if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); + if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); + if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); printw (" \n"); } } @@ -2505,6 +2541,66 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else printw (" - Black List Mode\n"); } #endif + //print additional information for EDACS modes and toggles + #ifdef PRETTY_COLORS + if (opts->p25_trunk == 1 && opts->frame_provoice == 1) + { + printw ("| \\--EDACS -"); + if (state->ea_mode == -1) + { + attron(COLOR_PAIR(2)); + printw (" Standard/Network"); + printw (" Extended Address"); + attron(COLOR_PAIR(4)); + } + else if (state->ea_mode == 0) + { + printw (" Standard/Network"); + attron(COLOR_PAIR(2)); + printw (" Extended Address"); + attron(COLOR_PAIR(4)); + } + else if (state->ea_mode == 1) + { + attron(COLOR_PAIR(2)); + printw (" Standard/Network"); + attron(COLOR_PAIR(4)); + printw (" Extended Address"); + } + printw (" Mode (S);"); + + printw(" ESK Mask: %02X", state->esk_mask); + printw (" (A); "); + + attron(COLOR_PAIR(4)); + printw ("\n"); + } + #else //set on to UPPER CASE, off to lower case + if (opts->p25_trunk == 1 && opts->frame_provoice == 1) + { + printw ("| \\--EDACS -"); + if (state->ea_mode == -1) + { + printw (" standard/network"); + printw (" extended address"); + } + else if (state->ea_mode == 0) + { + printw (" STANDARD/NETWORK"); + printw (" extended address"); + } + else if (state->ea_mode == 1) + { + printw (" standard/network"); + printw (" EXTENDED ADDRESS"); + } + printw (" Mode (S);"); + + printw(" ESK Mask: %02X", state->esk_mask); + printw (" (A) Toggle; "); + printw ("\n"); + } + #endif // if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n"); if (state->M == 1) { @@ -2521,9 +2617,9 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->scanner_mode == 1) { printw ("| Scan Mode: "); - if (state->lcn_freq_roll != 0) + if (state->lcn_freq_roll != 0) printw (" Frequency: %.06lf Mhz", (double)state->trunk_lcn_freq[state->lcn_freq_roll-1]/1000000); - printw (" Speed: %.02lf sec \n", opts->trunk_hangtime); //not sure values less than 1 make a difference, may be system/environment dependent + printw (" Speed: %.02lf sec \n", opts->trunk_hangtime); //not sure values less than 1 make a difference, may be system/environment dependent } if (opts->reverse_mute == 1) printw ("| Reverse Mute - Muting Unencrypted Voice\n"); @@ -2551,7 +2647,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->mod_gfsk == 1) printw ("[GFSK]"); printw ( "[%d] \n", (48000*opts->wav_interpolator)/state->samplesPerSymbol); if (opts->m17encoder == 1) printw ("| Encoding: [%s] ", opts->output_name); - if (opts->m17encoder == 1) printw ("(\\) \n"); + if (opts->m17encoder == 1) printw (" Toggle (\\); "); + if (opts->m17encoder == 1) printw (" Mic Gain (/|*): %02.0f%% ", opts->audio_gainA); + if (opts->m17encoder == 1) + { + if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); + if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); + if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); + printw ("\n"); + } printw ("| Decoding: [%s] ", opts->output_name); if (opts->aggressive_framesync == 0) printw ("CRC/(RAS) "); //debug -- troubleshoot voice tuning after grant on DMR CC, subsequent grant may not tune because tuner isn't available @@ -2565,7 +2669,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } printw ("\n"); printw ("| In Level: [%02d%%] \n", level); - + if (opts->dmr_stereo == 0) { printw ("| Voice Error: [%X][%X]", state->errs&0xF, state->errs2&0xF); @@ -2578,17 +2682,17 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("| Voice Error: [%X][%X] Slot 1 (1)", state->errs&0xF, state->errs2&0xF); if (opts->slot1_on == 0) printw (" OFF"); - if (opts->slot1_on == 1) printw (" ON"); + if (opts->slot1_on == 1) printw (" ON"); if (opts->slot_preference == 0) printw (" *Preferred"); printw ("\n"); printw ("| Voice Error: [%X][%X] Slot 2 (2)", state->errsR&0xF, state->errs2R&0xF); if (opts->slot2_on == 0) printw (" OFF"); - if (opts->slot2_on == 1) printw (" ON"); + if (opts->slot2_on == 1) printw (" ON"); if (opts->slot_preference == 1) printw (" *Preferred"); printw ("\n"); } printw ("------------------------------------------------------------------------------\n"); printw ("--Call Info-------------------------------------------------------------------\n"); - + //DSTAR if (lls == 6 || lls == 7 || lls == 18 || lls == 19) { @@ -2629,7 +2733,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw("RESERVED (%012llx) ", state->m17_dst); else printw("%s", state->m17_dst_str); - + printw ("\n"); printw ("| "); @@ -2639,7 +2743,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else printw("%s", state->m17_src_str); - + printw ("\n"); printw ("| "); @@ -2666,7 +2770,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (state->m17_enc == 3) { printw (" Reserved Enc - Type: %d", state->m17_enc_st); - } + } printw ("\n"); @@ -2727,7 +2831,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // printw ("%c", state->ysf_txt[i][j]); // } // // printw (" "); //just a single space between each 'block' - // } + // } printw ("\n"); @@ -2750,7 +2854,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw (" - Frequency: %.06lf Mhz ", (double)state->p25_cc_freq/1000000); } - } + } else if (opts->p25_is_tuned == 1) { if (idas == 0) printw ("Monitoring RTCH Channel"); //Traffic Channel @@ -2763,8 +2867,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("\n"); } - - + + printw ("| "); // #ifdef LIMAZULUTWEAKS @@ -2862,10 +2966,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } //Active Trunking Channels (NXDN and IDAS) - if (1 == 1) //opts->p25_trunk + if (1 == 1) //opts->p25_trunk { printw ("\n"); - printw ("| "); + printw ("| "); //active channel display attron(COLOR_PAIR(4)); @@ -2883,8 +2987,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw("\n"); } - - //P25 and DMR BS/MS + + //P25 and DMR BS/MS if ( lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33 || lls == 34 || lls == 35 || lls == 36) { @@ -2895,7 +2999,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // printw ("%s %s", state->dmr_branding, state->dmr_branding_sub); printw ("%s ", state->dmr_branding); printw ("%s", state->dmr_branding_sub); - printw ("%s", state->dmr_site_parms); //site id, net id, etc + printw ("%s", state->dmr_site_parms); //site id, net id, etc if (state->dmr_rest_channel > 0) { printw ("Rest LSN: %02d; ", state->dmr_rest_channel); @@ -2908,7 +3012,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000); } - + } else if (lls == 32 || lls == 33 || lls == 34) { @@ -2937,7 +3041,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw (" Phase 2 Invalid Parameters "); attron(COLOR_PAIR(3)); } - else + else { if (state->p25_cc_freq != 0) { @@ -3059,7 +3163,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Not always correct, or correct at all, depends on context //this is already in the call_string anyways - + // if(state->dmrburstL == 16 && state->dmr_so == 0x40 && state->R == 0) //0100 0000 // { // attron(COLOR_PAIR(2)); @@ -3096,13 +3200,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Embedded GPS (not LRRP) printw ("%s ", state->dmr_embedded_gps[0]); - + //Embedded Talker Alias Blocks for (int i = 0; i < 4; i++) { for (int j = 0; j < 7; j++) { - printw ("%s", state->dmr_alias_block_segment[0][i][j]); + printw ("%s", state->dmr_alias_block_segment[0][i][j]); } } @@ -3145,7 +3249,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Slot 2 [1] -- Also Including DMR MS now to keep the display more 'uniform' in nature // if (lls < 30 || lls == 35 || lls == 36) - { + { printw ("| SLOT 2 - "); if (state->dmrburstR < 16 && state->carrier == 1 && state->lasttgR > 0 && state->lastsrcR > 0) { @@ -3161,10 +3265,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("%s | ", state->call_string[1]); printw ("%s ", DMRBusrtTypes[state->dmrburstR]); - if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && ( state->dmrburstL == 16 || state->dmrburstL == 21) && (state->dmrburstR == 16 || state->dmrburstR == 21) ) printw ("*M*"); + if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && ( state->dmrburstL == 16 || state->dmrburstL == 21) && (state->dmrburstR == 16 || state->dmrburstR == 21) ) printw ("*M*"); printw ("\n"); - + printw ("| V XTRA | "); //10 spaces if(state->dmrburstR == 16 && state->payload_algidR == 0 && (state->dmr_soR & 0xCF) == 0x40) //4F or CF mask? @@ -3285,7 +3389,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if(state->dmrburstR == 16 || state->dmrburstR == 21) //only during call { - + //Embedded GPS (not LRRP) attron(COLOR_PAIR(4)); printw ("%s ", state->dmr_embedded_gps[1]); @@ -3295,7 +3399,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { for (int j = 0; j < 7; j++) { - printw ("%s", state->dmr_alias_block_segment[1][i][j]); + printw ("%s", state->dmr_alias_block_segment[1][i][j]); } } @@ -3313,7 +3417,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) attron(COLOR_PAIR(4)); printw ("%s", state->dmr_lrrp_gps[1]); } - + //Group Name Labels from CSV import if (state->dmrburstR == 16 || state->dmrburstR > 19) { @@ -3401,7 +3505,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //EDACS and ProVoice if (lls == 14 || lls == 15 || lls == 37 || lls == 38) { - attroff (COLOR_PAIR(3)); //colors off for EDACS + attroff (COLOR_PAIR(3)); //colors off for EDACS if (state->edacs_site_id != 0) { if (opts->p25_is_tuned == 0) @@ -3412,8 +3516,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("| Monitoring Voice Channel - LCN [%02d]\n", state->edacs_tuned_lcn); //since we are tuned, keep updating the time so it doesn't disappear during call - call_matrix[state->edacs_tuned_lcn][5] = time(NULL); - } + call_matrix[state->edacs_tuned_lcn][5] = time(NULL); + } printw ("| SITE [%03lld][%02llX]", state->edacs_site_id, state->edacs_site_id); if (state->ea_mode == 1) @@ -3430,11 +3534,11 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } for (i = 1; i <= state->edacs_lcn_count; i++) { - //shim 443 afs in here for display purposes - int a = (call_matrix[i][3] >> 7) & 0xF; - int fs = call_matrix[i][3] & 0x7F; - printw ("| - LCN [%02d][%.06lf] MHz", i, (double)state->trunk_lcn_freq[i-1]/1000000); - + // Compute 4:4:3 AFS for display purposes only + int a = (call_matrix[i][2] >> 7) & 0xF; + int fs = call_matrix[i][2] & 0x7F; + printw ("| - LCN [%02d][%010.06lf] MHz", i, (double)state->trunk_lcn_freq[i-1]/1000000); + //print Control Channel on LCN line with the current Control Channel if ( (i) == state->edacs_cc_lcn) { @@ -3442,12 +3546,82 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw (" Control Channel"); attroff (COLOR_PAIR(1)); } + + int print_call = 0; //print active calls on corresponding LCN line - if ((i != state->edacs_cc_lcn) && time(NULL) - call_matrix[i][5] < 2) + if ((i != state->edacs_cc_lcn) && time(NULL) - call_matrix[i][5] < 2) { - attron (COLOR_PAIR(3)); - if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] ); - else printw (" AFS [%03lld][%02d-%03d]", call_matrix[i][3], a, fs ); + print_call = 3; + attron (COLOR_PAIR(3)); + } + //print dying or dead calls in red for x seconds longer + else if ( (i != state->edacs_cc_lcn) && (time(NULL) - call_matrix[i][5] >= 2) && (time(NULL) - call_matrix[i][5] < 5) ) + { + print_call = 2; + attron (COLOR_PAIR(2)); + } + + if (print_call != 0) + { + if (state->ea_mode == 1) { + // Voice call + if ((call_matrix[i][4] & EDACS_IS_VOICE) != 0) + { + // System all-call + if ((call_matrix[i][4] & EDACS_IS_GROUP) != 0) + printw (" TGT [%8lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] ); + // I-Call + else if ((call_matrix[i][4] & EDACS_IS_INDIVIDUAL) != 0) + printw (" TGT [%8lld] SRC [%8lld] I-Call", call_matrix[i][2], call_matrix[i][3] ); + // System all-call + else if ((call_matrix[i][4] & EDACS_IS_ALL_CALL) != 0) + printw (" TGT [ SYSTEM ] SRC [%8lld] All-Call", call_matrix[i][3] ); + // Interconnect call + else if ((call_matrix[i][4] & EDACS_IS_INTERCONNECT) != 0) + printw (" TGT [ SYSTEM ] SRC [%8lld] Interconnect", call_matrix[i][3] ); + // Unknown call + else + printw (" Unknown call type" ); + + // Call flags + if ((call_matrix[i][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]"); + else printw (" [Dig]"); + if ((call_matrix[i][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]"); + } + else + // Data call + printw (" TGT [ DATA ] SRC [%8lld] Data", call_matrix[i][3] ); + } + else + { + // Voice call + if ((call_matrix[i][4] & EDACS_IS_VOICE) != 0) + { + // Group call + if ((call_matrix[i][4] & EDACS_IS_GROUP) != 0) + printw (" TGT [%6lld][%02d-%03d] SRC [%5lld]", call_matrix[i][2], a, fs, call_matrix[i][3] ); + // I-Call + else if ((call_matrix[i][4] & EDACS_IS_INDIVIDUAL) != 0) + printw (" TGT [%6lld][ UNIT ] SRC [%5lld] I-Call", call_matrix[i][2], call_matrix[i][3] ); + // System all-call + else if ((call_matrix[i][4] & EDACS_IS_ALL_CALL) != 0) + printw (" TGT [ SYSTEM ] SRC [%5lld] All-Call", call_matrix[i][3] ); + // Interconnect call + else if ((call_matrix[i][4] & EDACS_IS_INTERCONNECT) != 0) + printw (" TGT [ SYSTEM ] SRC [%5lld] Interconnect", call_matrix[i][3] ); + // Unknown call + else + printw (" Unknown call type" ); + + // Call flags + if ((call_matrix[i][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]"); + else printw (" [Dig]"); + if ((call_matrix[i][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]"); + } + else + // Data call + printw (" TGT [ DATA ] SRC [%8lld] Data", call_matrix[i][3] ); + } for (int k = 0; k < state->group_tally; k++) { if (state->group_array[k].groupNumber == call_matrix[i][2] && call_matrix[i][2] != 0) @@ -3463,37 +3637,19 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) break; } } - attroff (COLOR_PAIR(3)); + + if (print_call == 3) + attroff (COLOR_PAIR(3)); + else if (print_call == 2) + attroff (COLOR_PAIR(2)); } - //print dying or dead calls in red for x seconds longer - if ( (i != state->edacs_cc_lcn) && (time(NULL) - call_matrix[i][5] >= 2) && (time(NULL) - call_matrix[i][5] < 5) ) - { - attron (COLOR_PAIR(2)); - if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] ); - else printw (" AFS [%03lld][%02d-%03d]", call_matrix[i][3], a, fs ); - for (int k = 0; k < state->group_tally; k++) - { - if (state->group_array[k].groupNumber == call_matrix[i][2] && call_matrix[i][2] != 0) - { - printw (" [%s]", state->group_array[k].groupName); - printw ("[%s]", state->group_array[k].groupMode); - break; - } - else if (state->group_array[k].groupNumber == call_matrix[i][3] && call_matrix[i][3] != 0) - { - printw (" [%s]", state->group_array[k].groupName); - printw ("[%s]", state->group_array[k].groupMode); - break; - } - } - attroff (COLOR_PAIR(2)); - } - if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is opened + + if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is tuned printw ("\n"); } if (state->carrier == 1) { - attron (COLOR_PAIR(3)); + attron (COLOR_PAIR(3)); } } @@ -3585,23 +3741,76 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //EDACS and ProVoice, outside of timestamp loop if (call_matrix[j][0] == 14 || call_matrix[j][0] == 15 || call_matrix[j][0] == 37 || call_matrix[j][0] == 38 ) { - if (call_matrix[j][3] != 0) + if (call_matrix[j][2] != 0) { printw ("| "); - printw ("%s ", getDateC(call_matrix[j][5]) ); + printw ("%s ", getDateC(call_matrix[j][5]) ); printw ("%s ", getTimeC(call_matrix[j][5]) ); printw ("LCN [%2lld] ", call_matrix[j][1]); if (state->ea_mode == 1) { - printw ("Group [%8lld] ", call_matrix[j][2]); - printw ("Source [%8lld] ", call_matrix[j][3]); + // Voice call + if ((call_matrix[j][4] & EDACS_IS_VOICE) != 0) + { + // Group call + if ((call_matrix[j][4] & EDACS_IS_GROUP) != 0) + printw ("Target [%8lld] Source [%8lld]", call_matrix[j][2], call_matrix[j][3]); + // I-Call + else if ((call_matrix[j][4] & EDACS_IS_INDIVIDUAL) != 0) + printw ("Target [%8lld] Source [%8lld] I-Call", call_matrix[j][2], call_matrix[j][3]); + // System all-call + else if ((call_matrix[j][4] & EDACS_IS_ALL_CALL) != 0) + printw ("Target [ SYSTEM ] Source [%8lld] All-Call", call_matrix[j][3]); + // Interconnect + else if ((call_matrix[j][4] & EDACS_IS_INTERCONNECT) != 0) + printw ("Target [ SYSTEM ] Source [%8lld] Interconnect", call_matrix[j][3]); + // Unknown call + else + printw ("Unknown call type"); + + // Call flags + if ((call_matrix[j][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]"); + else printw (" [Dig]"); + if ((call_matrix[j][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]"); + } + else + // Data call + printw ("Target [ DATA ] Source [%8lld] Data", call_matrix[j][3]); } - else + else { - int a = (call_matrix[j][3] >> 7) & 0xF; - int fs = call_matrix[j][3] & 0x7F; - printw (" AFS [%03lld][%02d-%03d]", call_matrix[j][3], a, fs ); - } + // Voice call + if ((call_matrix[j][4] & EDACS_IS_VOICE) != 0) + { + // Compute 4:4:3 AFS for display purposes only + int a = (call_matrix[j][2] >> 7) & 0xF; + int fs = call_matrix[j][2] & 0x7F; + + // Group call + if ((call_matrix[j][4] & EDACS_IS_GROUP) != 0) + printw ("Target [%6lld][%02d-%03d] Source [%5lld]", call_matrix[j][2], a, fs, call_matrix[j][3]); + // I-Call + else if ((call_matrix[j][4] & EDACS_IS_INDIVIDUAL) != 0) + printw ("Target [%6lld][ UNIT ] Source [%5lld] I-Call", call_matrix[j][2], call_matrix[j][3]); + // System all-call + else if ((call_matrix[j][4] & EDACS_IS_ALL_CALL) != 0) + printw ("Target [ SYSTEM ] Source [%5lld] All-Call", call_matrix[j][3]); + // Interconnect + else if ((call_matrix[j][4] & EDACS_IS_INTERCONNECT) != 0) + printw ("Target [ SYSTEM ] Source [%5lld] Interconnect", call_matrix[j][3]); + // Unknown call + else + printw ("Unknown call type"); + + // Call flags + if ((call_matrix[j][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]"); + else printw (" [Dig]"); + if ((call_matrix[j][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]"); + } + else + // Data call + printw ("Target [ DATA ] Source [%5lld] Data", call_matrix[j][3]); + } //test for (int k = 0; k < state->group_tally; k++) { @@ -3621,7 +3830,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //end test printw ("\n"); } - + } } //end Call History //fence bottom @@ -3665,8 +3874,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //switching, but want to control each seperately plz if (opts->slot1_on == 1) { - opts->slot1_on = 0; - opts->slot_preference = 1; //slot 2 + opts->slot1_on = 0; if (opts->slot_preference == 0) opts->slot_preference = 2; + // opts->slot_preference = 1; //slot 2 //clear any previously buffered audio state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; @@ -3678,7 +3887,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else if (opts->slot1_on == 0) { opts->slot1_on = 1; - if (opts->audio_out_type == 5) //OSS 48k/1 + if (opts->audio_out_type == 5) //OSS 48k/1 { opts->slot_preference = 0; //slot 1 // opts->slot2_on = 0; //turn off slot 2 @@ -3704,7 +3913,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else if (opts->slot2_on == 0) { opts->slot2_on = 1; - if (opts->audio_out_type == 5) //OSS 48k/1 + if (opts->audio_out_type == 5) //OSS 48k/1 { opts->slot_preference = 1; //slot 2 // opts->slot1_on = 0; //turn off slot 1 @@ -3712,6 +3921,12 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } } + if (c == 51) //'3' key, toggle slot preference on 48k/1 + { + if (opts->slot_preference == 1) opts->slot_preference = 0; + else opts->slot_preference = 1; + } + if (c == 43) //+ key, increment audio_gain { @@ -3745,6 +3960,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } + if (c == 42) // * key, increment audio_gainA + { + if (opts->audio_gainA < 100) + opts->audio_gainA++; + } + + if (c == 47) // / key, decrement audio_gainA + { + if (opts->audio_gainA > 0) + opts->audio_gainA--; + } + if (c == 122) //'z' key, toggle payload to console { if (opts->payload == 1) opts->payload = 0; @@ -3910,14 +4137,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { fprintf (stderr, "-T %s wav file directory does not exist\n", wav_file_directory); fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory); - mkdir(wav_file_directory, 0700); + mkdir(wav_file_directory, 0700); } opts->dmr_stereo_wav = 1; //catch all in case of no file name set, won't crash or something sprintf (opts->wav_out_file, "./%s/DSD-FME-T1.wav", opts->wav_out_dir); sprintf (opts->wav_out_fileR, "./%s/DSD-FME-T2.wav", opts->wav_out_dir); - openWavOutFileL (opts, state); - openWavOutFileR (opts, state); + openWavOutFileL (opts, state); + openWavOutFileR (opts, state); } //this one could cause issues, but seems okay @@ -3934,15 +4161,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // #ifdef AERO_BUILD //this might be okay on Aero as well, will need to look into and/or test - // + // #else if (c == 115) //'s' key, stop playing wav or symbol in files { if (opts->symbolfile != NULL) { - if (opts->audio_in_type == 4) + if (opts->audio_in_type == 4) { - fclose(opts->symbolfile); + fclose(opts->symbolfile); } } @@ -3956,12 +4183,12 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) opts->audio_in_type = 0; openPulseInput(opts); } - else opts->audio_in_type = 5; //exitflag = 1; - + else opts->audio_in_type = 5; //exitflag = 1; + } #endif - - //makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running + + //makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running if (state->lasttg != 0 && opts->frame_provoice != 1 && c == 33) //SHIFT+'1' key (exclamation point), lockout slot 1 or conventional tg from tuning/playback during session { state->group_array[state->group_tally].groupNumber = state->lasttg; @@ -4097,40 +4324,40 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // if (c == 48) //'0' key, toggle upsampled audio smoothing // { // if (state->audio_smoothing == 1) state->audio_smoothing = 0; - // else state->audio_smoothing = 1; + // else state->audio_smoothing = 1; // } - if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode + if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode { if (opts->trunk_use_allow_list == 1) opts->trunk_use_allow_list = 0; - else opts->trunk_use_allow_list = 1; + else opts->trunk_use_allow_list = 1; } if (opts->p25_trunk == 1 && c == 117) //'u' key, toggle tune private calls { if (opts->trunk_tune_private_calls == 1) opts->trunk_tune_private_calls = 0; - else opts->trunk_tune_private_calls = 1; + else opts->trunk_tune_private_calls = 1; } if (opts->p25_trunk == 1 && c == 100) //'d' key, toggle tune data calls { if (opts->trunk_tune_data_calls == 1) opts->trunk_tune_data_calls = 0; - else opts->trunk_tune_data_calls = 1; + else opts->trunk_tune_data_calls = 1; } if (opts->p25_trunk == 1 && c == 101) //'e' key, toggle tune enc calls (P25 only on certain grants) { if (opts->trunk_tune_enc_calls == 1) opts->trunk_tune_enc_calls = 0; - else opts->trunk_tune_enc_calls = 1; + else opts->trunk_tune_enc_calls = 1; } if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls { if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0; - else opts->trunk_tune_group_calls = 1; + else opts->trunk_tune_group_calls = 1; } - if (c == 70) //'F' key - toggle agressive sync/crc failure/ras + if (c == 70) //'F' key - toggle agressive sync/crc failure/ras { if (opts->aggressive_framesync == 0) opts->aggressive_framesync = 1; else opts->aggressive_framesync = 0; @@ -4138,7 +4365,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (c == 68) //'D' key - Reset DMR Site Parms/Call Strings, etc. { - //dmr trunking/ncurses stuff + //dmr trunking/ncurses stuff state->dmr_rest_channel = -1; //init on -1 state->dmr_mfid = -1; // @@ -4200,7 +4427,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 0) closePulseInput(opts); fprintf (stderr, "TCP Socket Connected Successfully.\n"); opts->audio_in_type = 8; - } + } } else fprintf (stderr, "TCP Socket Connection Error.\n"); } @@ -4272,7 +4499,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) state->symbolCenter = 3; } - fprintf (stderr, "\n User Activated Return to CC; \n "); + fprintf (stderr, "\n User Activated Return to CC; \n "); } @@ -4318,15 +4545,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { state->lcn_freq_roll++; //check roll again if greater than expected, then go back to zero - if (state->lcn_freq_roll >= state->lcn_freq_count) + if (state->lcn_freq_roll >= state->lcn_freq_count) { state->lcn_freq_roll = 0; //reset to zero - } + } } } //check that we have a non zero value first, then tune next frequency - if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0) + if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0) { //rigctl if (opts->use_rigctl == 1) @@ -4343,7 +4570,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } fprintf (stderr, "\n User Activated Channel Cycle;"); - fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n", + fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n", (double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000); } @@ -4365,22 +4592,72 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { state->samplesPerSymbol = 8; state->symbolCenter = 3; - } + } } - if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX + if (c == 86) // 'V' Key, toggle LPF + { + if (opts->use_lpf == 0) opts->use_lpf = 1; + else opts->use_lpf = 0; + } + + if (c == 66) // 'B' Key, toggle HPF + { + if (opts->use_hpf == 0) opts->use_hpf = 1; + else opts->use_hpf = 0; + } + + if (c == 78) // 'N' Key, toggle PBF + { + if (opts->use_pbf == 0) opts->use_pbf = 1; + else opts->use_pbf = 0; + } + + if (c == 72) // 'H' Key, toggle HPF on digital + { + if (opts->use_hpf_d == 0) opts->use_hpf_d = 1; + else opts->use_hpf_d = 0; + } + + if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX { if (state->m17encoder_tx == 0) state->m17encoder_tx = 1; else state->m17encoder_tx = 0; //flag on the EOT marker to send last frame after toggling encoder to zero if (state->m17encoder_tx == 0) state->m17encoder_eot = 1; - + + } + + if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0 + { + if (state->esk_mask == 0) state->esk_mask = 0xA0; + else state->esk_mask = 0; + } + + if(opts->frame_provoice == 1 && c == 83) //'S' Key, toggle STD or EA mode and reset + { + if (state->ea_mode == -1) state->ea_mode = 0; + else if (state->ea_mode == 0) state->ea_mode = 1; + else state->ea_mode = 0; + + //reset -- test to make sure these don't do weird things when reset + state->edacs_site_id = 0; + state->edacs_lcn_count = 0; + state->edacs_cc_lcn = 0; + state->edacs_vc_lcn = 0; + state->edacs_tuned_lcn = -1; + state->edacs_vc_call_type = 0; + state->p25_cc_freq = 0; + opts->p25_is_tuned = 0; + state->lasttg = 0; + state->lastsrc = 0; + } //anything with an entry box will need the inputs and outputs stopped first - //so probably just write a function to handle c input, and when c = certain values + //so probably just write a function to handle c input, and when c = certain values //needing an entry box, then stop all of those //allocated memory pointer needs to be free'd each time diff --git a/src/dsd_symbol.c b/src/dsd_symbol.c index 32c9e63..46b4e9b 100644 --- a/src/dsd_symbol.c +++ b/src/dsd_symbol.c @@ -17,29 +17,6 @@ #include "dsd.h" -long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as len -{ - - int i; - long int rms; - long p, t, s; - double dc, err; - - p = t = 0L; - for (i=0; iwav_out_raw); } - //test running analog audio through a de-emphasis filter - analog_deemph_filter(state->analog_out, 960); - //and dc_block filter analog_dc_block_filter - analog_dc_block_filter(state->analog_out, 960); - //test running analog audio through a pre-emphasis filter - analog_preemph_filter(state->analog_out, 960); - //test running analog short sample clipping filter - analog_clipping_filter(state->analog_out, 960); + //low pass filter + if (opts->use_lpf == 1) + lpf(state, state->analog_out, 960); + + //high pass filter + if (opts->use_hpf == 1) + hpf (state, state->analog_out, 960); + + //pass band filter + if (opts->use_pbf == 1) + pbf(state, state->analog_out, 960); + + //manual gain control + if (opts->audio_gainA > 0.0f) + analog_gain (opts, state, state->analog_out, 960); + + //automatic gain control + else + agsm(opts, state, state->analog_out, 960); + + //Running RMS after filtering does remove the analog spike from the RMS value + //but noise floor noise will still produce higher values + // if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1) + // opts->rtl_rms = raw_rms(state->analog_out, 960, 1); //seems to be working now, but RMS values are lower on actual analog signal than on no signal but noise if ( (opts->rtl_rms > opts->rtl_squelch_level) && opts->monitor_input_audio == 1 && state->carrier == 0 ) //added carrier check here in lieu of disabling it above diff --git a/src/edacs-fme.c b/src/edacs-fme.c index 0670f94..93cb81c 100644 --- a/src/edacs-fme.c +++ b/src/edacs-fme.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------------- - * EDACS-FME + * EDACS-FME * A program for decoding EDACS (ported to DSD-FME) * https://github.com/lwvmobile/edacs-fm * @@ -15,6 +15,9 @@ * * LWVMOBILE * 2023-11 Version EDACS-FM Florida Man Edition + * + * ilyacodes + * 2024-03 rewrite EDACS standard parsing to spec, add reverse-engineered EA messages *-----------------------------------------------------------------------------*/ #include "dsd.h" @@ -50,6 +53,22 @@ char * getTimeE(void) //get pretty hhmmss timestamp return curr; } +char* get_lcn_status_string(int lcn) +{ + if (lcn == 26 || lcn == 27) + return "[Reserved LCN Status]"; + if (lcn == 28) + return "[Convert To Callee]"; + else if (lcn == 29) + return "[Call Queued]"; + else if (lcn == 30) + return "[System Busy]"; + else if (lcn == 31) + return "[Call Denied]"; + else + return ""; +} + void openWavOutFile48k (dsd_opts * opts, dsd_state * state) { UNUSED(state); @@ -66,118 +85,9 @@ void openWavOutFile48k (dsd_opts * opts, dsd_state * state) } } -//generic rms type function -long int gen_rms(short *samples, int len, int step) -{ - - int i; - long int rms; - long p, t, s; - double dc, err; - - p = t = 0L; - for (i=0; i 32760) - input[i] = 32760; - else if (input[i] < -32760) - input[i] = -32760; - } -} - -//modified from version found in rtl_fm -static short avg = 0; -void analog_deemph_filter(short * input, int len) -{ - // static short avg; // cheating... - int i, d; - - // int a = (int)round(1.0/((1.0-exp(-1.0/(96000 * 75e-6))))); //48000 is rate out, but doubling it sounds 'beefier' - int a = 8; //result of calc above is 8, so save a cycle on the low end CPUs - - // de-emph IIR - // avg = avg * (1 - alpha) + sample * alpha; - - for (i = 0; i < len; i++) - { - d = input[i] - avg; - if (d > 0) - avg += (d + a/2) / a; - else - avg += (d - a/2) / a; - - input[i] = (short)avg; - } -} - -//modified from version found in rtl_fm -static int dc_avg = 0; -void analog_dc_block_filter(short * input, int len) -{ - int i, avg; - int64_t sum = 0; - for (i=0; i < len; i++) - sum += input[i]; - - avg = sum / len; - avg = (avg + dc_avg * 9) / 10; - for (i=0; i < len; i++) - input[i] -= (short)avg; - - dc_avg = avg; -} - //listening to and playing back analog audio void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn) { - //reset static states for new channel - pstate = 1; - avg = 0; - dc_avg = 0; - int i, result; int count = 5; //RMS has a 5 count (5 * 180ms) now before cutting off; short analog1[960]; @@ -185,6 +95,12 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn short analog3[960]; short sample = 0; + // #define DEBUG_ANALOG //enable to digitize analog if 'data' bursts heard + + uint8_t d1[192]; + uint8_t d2[192]; + uint8_t d3[192]; + state->last_cc_sync_time = time(NULL); state->last_vc_sync_time = time(NULL); @@ -192,13 +108,17 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn memset (analog2, 0, sizeof(analog2)); memset (analog3, 0, sizeof(analog3)); + memset (d1, 0, sizeof(d1)); + memset (d2, 0, sizeof(d2)); + memset (d3, 0, sizeof(d3)); + long int rms = opts->rtl_squelch_level + 1; //one more for the initial loop phase long int sql = opts->rtl_squelch_level; fprintf (stderr, "\n"); while (!exitflag && count > 0) - { + { //this will only work on 48k/1 short output if (opts->audio_in_type == 0) { @@ -220,17 +140,21 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn analog3[i] = sample; } //this rms will only work properly (for now) with squelch enabled in SDR++ or other - rms = gen_rms(analog3, 960, 1); + rms = raw_rms(analog3, 960, 1); } //NOTE: The core dumps observed previously were due to SDR++ Remote Server connection dropping due to Internet/Other issues - //and unlike in the main livescanner loop where it just hangs, this loop will cause a core dump. The observed issue + //and unlike in the main livescanner loop where it just hangs, this loop will cause a core dump. The observed issue //has not occurred when using SDR++ on local hardware, just the remote server software over the Internet. //NOTE: The fix below does not apply to above observed issue, as the TCP connection will not drop, there will just //not be a sample to read in and it hangs on sf_short read until it crashes out, the fix below will prevent issues //when SDR++ is closed locally, or the TCP connection closes suddenly. + //NOTE: Observed two segfaults on EDACS STM analog when doing radio tests or otherwise holding the radio + //open for extremely long periods of time, could be an issue in digitize where dibit_buf_p is not + //reset for an extended period of time and overflows, may need to reset buffer occassionally here + //TCP Input w/ Simple TCP Error Detection Implemented to prevent hard crash if TCP drops off if (opts->audio_in_type == 8) { @@ -272,9 +196,9 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn } analog3[i] = sample; } - + //this rms will only work properly (for now) with squelch enabled in SDR++ - rms = gen_rms(analog3, 960, 1); + rms = raw_rms(analog3, 960, 1); } //RTL Input @@ -311,22 +235,71 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn sr += digitize (opts, state, (int)analog1[i]); } - //test running analog audio through a de-emphasis filter - analog_deemph_filter(analog1, 960); - analog_deemph_filter(analog2, 960); - analog_deemph_filter(analog3, 960); - //and dc_block filter analog_dc_block_filter - analog_dc_block_filter(analog1, 960); - analog_dc_block_filter(analog2, 960); - analog_dc_block_filter(analog3, 960); - //test running analog audio through a pre-emphasis filter - analog_preemph_filter(analog1, 960); - analog_preemph_filter(analog2, 960); - analog_preemph_filter(analog3, 960); - //test running analog short sample clipping filter - analog_clipping_filter(analog1, 960); - analog_clipping_filter(analog2, 960); - analog_clipping_filter(analog3, 960); + #ifdef DEBUG_ANALOG + //save digitized samples to array for looking into those 'data' sounding bursts, + //this format assumes the same sample per symbol rateused in EDACS/PV + for (i = 0; i < 192; i++) //Samples Per Symbol is 5, so incrememnt every 5 + { + d1[i] = digitize (opts, state, (int)analog1[i*5]); + d2[i] = digitize (opts, state, (int)analog2[i*5]); + d3[i] = digitize (opts, state, (int)analog3[i*5]); + } + #endif + + //Bugfix for buffer overflow from using digitize function, reset buffers + if (state->dibit_buf_p > state->dibit_buf + 900000) + 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; + + // low pass filter + if (opts->use_lpf == 1) + { + lpf (state, analog1, 960); + lpf (state, analog2, 960); + lpf (state, analog3, 960); + } + + //high pass filter + if (opts->use_hpf == 1) + { + hpf (state, analog1, 960); + hpf (state, analog2, 960); + hpf (state, analog3, 960); + } + + //pass band filter + if (opts->use_pbf == 1) + { + pbf (state, analog1, 960); + pbf (state, analog2, 960); + pbf (state, analog3, 960); + } + + //manual gain control + if (opts->audio_gainA > 0.0f) + { + analog_gain (opts, state, analog1, 960); + analog_gain (opts, state, analog2, 960); + analog_gain (opts, state, analog3, 960); + } + + //automatic gain control + else + { + agsm (opts, state, analog1, 960); + agsm (opts, state, analog2, 960); + agsm (opts, state, analog3, 960); + } + + //NOTE: Ideally, we would run raw_rms for TCP/VS here, but the analog spike on EDACS (STM) + //system gets filtered out, and when they hold the radio open and don't talk, + //it counts against the squelch hit as no audio, so we will just have to use + //the squelch checkbox in SDR++ and similar when using those input methods + // if (opts->audio_in_type != 3) + // rms = raw_rms(analog3, 960, 1); //reconfigured to use seperate audio out stream that is always 48k short if (opts->audio_out_type == 0 && opts->slot1_on == 1) @@ -371,8 +344,15 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn else fprintf(stderr, "%s", KRED); fprintf (stderr, " Analog RMS: %04ld SQL: %ld", rms, sql); - if (afs != 0) + if (state->ea_mode == 0) + { fprintf (stderr, " AFS [%03d] [%02d-%03d] LCN [%02d]", afs, afs >> 7, afs & 0x7F, lcn); + } + else + { + if (afs == -1) fprintf (stderr, " TGT [ SYSTEM ] LCN [%02d] All-Call", lcn); + else fprintf (stderr, " TGT [%08d] LCN [%02d]", afs, lcn); + } //debug, view hit counter // fprintf (stderr, " CNT: %d; ", count); @@ -400,8 +380,32 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn fprintf (stderr, "%s", KNRM); + #ifdef PRETTY_COLORS + {} //do nothing + #else + fprintf (stderr, "SQL HIT: %d; ", 5-cnt); //add cnt since user can't see red or green + #endif + + #ifdef DEBUG_ANALOG + //debug digitized version of analog out when data bursts may be present + //NOTE: Without a 'framesync' these could be shifted into odd positions + if (opts->payload == 1) + { + fprintf (stderr, "\n A_DUMP: "); + for (i = 0; i < 24; i++) + fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&d1[i*8], 8)); + fprintf (stderr, "\n "); + for (i = 0; i < 24; i++) + fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&d2[i*8], 8)); + fprintf (stderr, "\n "); + for (i = 0; i < 24; i++) + fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&d3[i*8], 8)); + // fprintf (stderr, "\n"); + } + #endif + if (count > 0) fprintf (stderr, "\n"); - + } } @@ -411,7 +415,7 @@ void edacs(dsd_opts * opts, dsd_state * state) unsigned long long int fr_1 = 0xFFFFFFFFFF; //40-bit frames unsigned long long int fr_2 = 0; //each is a 40 bit frame that repeats 3 times unsigned long long int fr_3 = 0; //two messages per frame - unsigned long long int fr_4 = 0xFFFFFFFFFF; + unsigned long long int fr_4 = 0xFFFFFFFFFF; unsigned long long int fr_5 = 0; unsigned long long int fr_6 = 0; @@ -421,17 +425,13 @@ void edacs(dsd_opts * opts, dsd_state * state) unsigned long long int fr_4m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler unsigned long long int fr_4t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached - unsigned char command = 0xFF; - unsigned char mt1 = 0x1F; - unsigned char mt2 = 0xF; - unsigned char lcn = 0; - //commands; may not use these anymore unsigned int vcmd = 0xEE; //voice command variable unsigned int idcmd = 0xFD; unsigned int peercmd = 0xF88; //using for EA detection test unsigned int netcmd = 0xF3; //using for Networked Test UNUSED2(vcmd, idcmd); + UNUSED2(peercmd, netcmd); char * timestr; //add timestr here, so we can assign it and also free it to prevent memory leak timestr = getTimeE(); @@ -451,18 +451,18 @@ void edacs(dsd_opts * opts, dsd_state * state) { //only fr_1 and fr4 are going to matter fr_1 = fr_1 << 1; - fr_1 = fr_1 | edacs_bit[i]; + fr_1 = fr_1 | edacs_bit[i]; fr_2 = fr_2 << 1; fr_2 = fr_2 | edacs_bit[i+40]; fr_3 = fr_3 << 1; fr_3 = fr_3 | edacs_bit[i+80]; - - fr_4 = fr_4 << 1; - fr_4 = fr_4 | edacs_bit[i+120]; + + fr_4 = fr_4 << 1; + fr_4 = fr_4 | edacs_bit[i+120]; fr_5 = fr_5 << 1; fr_5 = fr_5 | edacs_bit[i+160]; fr_6 = fr_6 << 1; - fr_6 = fr_6 | edacs_bit[i+200]; + fr_6 = fr_6 | edacs_bit[i+200]; } @@ -486,136 +486,252 @@ void edacs(dsd_opts * opts, dsd_state * state) else //BCH Pass, continue from here. { - //ESK on/off detection, I honestly don't remember the logic for this anymore, but it works fine - if ( (((fr_1t & 0xF000000000) >> 36) != 0xB) && (((fr_1t & 0xF000000000) >> 36) != 0x1) && (((fr_1t & 0xFF00000000) >> 32) != 0xF3) ) - { - //experimenting with values here, not too high, and not too low - if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 )) - { - state->esk_mask = 0xA0; - } - //ideal value would be 5, but some other values exist that don't allow it - if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) ) - { - state->esk_mask = 0x0; - } - } + //Auto Detection Modes Have Been Removed due to reliability issues, + //users will now need to manually specify these options: + /* + -fh Decode only EDACS Standard/ProVoice*\n"); + -fH Decode only EDACS Standard/ProVoice with ESK 0xA0*\n"); + -fe Decode only EDACS EA/ProVoice*\n"); + -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n"); - //Standard/Networked Auto Detection - //if (command == netcmd) //netcmd is F3 Standard/Networked (I think) - if ( (fr_1t >> 32 == netcmd) || (fr_1t >> 32 == (netcmd ^ 0xA0)) ) - { - state->ea_mode = 0; //disable extended addressing mode - } + (A) key toggles mode; (S) key toggles mask value in ncurses + */ - //EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28 - if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) ) - { - state->ea_mode = 1; //enable extended addressing mode - } + //TODO: Consider re-adding the auto code to make a suggestion to users + //as to which mode to proceed in? - //Start Extended Addressing Mode + //Color scheme: + // - KRED - critical information (emergency, failsoft, etc) + // - KYEL - system data + // - KGRN - voice group calls + // - KCYN - voice individual calls + // - KMAG - voice other calls (interconnect, all-call, etc) + // - KBLU - subscriber data + // - KWHT - unknown/reserved + + //Account for ESK, if any + unsigned long long int fr_esk_mask = ((unsigned long long int)state->esk_mask) << 32; + fr_1t = fr_1t ^ fr_esk_mask; + fr_4t = fr_4t ^ fr_esk_mask; + + //Start Extended Addressing Mode if (state->ea_mode == 1) { - command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask; - mt1 = (command & 0xF8) >> 3; - mt2 = (fr_1t & 0x780000000) >> 31; - lcn = (fr_1t & 0x3E0000000) >> 29; //only valid during calls, status + unsigned char mt1 = (fr_1t & 0xF800000000) >> 35; + unsigned char mt2 = (fr_1t & 0x780000000) >> 31; - //Site ID + state->edacs_vc_call_type = 0; + + //TODO: initialize where they are actually used unsigned long long int site_id = 0; //we probably could just make this an int as well as the state variables - if (mt1 == 0x1F && mt2 == 0xA) + unsigned char lcn = 0; + + //Add raw payloads and MT1/MT2 for easy debug + if (opts->payload == 1) { - site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19); - fprintf (stderr, "%s", KYEL); - fprintf (stderr, " Site ID [%02llX][%03lld] Extended Addressing", site_id, site_id); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + fprintf (stderr, " (MT1: %02X", mt1); + // MT2 is meaningless if MT1 is not 0x1F + if (mt1 == 0x1F) + fprintf (stderr, "; MT2: %X) ", mt2); + else + fprintf (stderr, ") "); + } + + //MT1 of 0x1F indicates to use MT2 for the opcode. See US patent US7546135B2, Figure 2b. + if (mt1 == 0x1F) + { + + //Test Call (not seen in the wild, see US patent US7546135B2, Figure 2b) + if (mt2 == 0x0) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Initiate Test Call"); + fprintf (stderr, "%s", KNRM); + } + //Adjacent Sites + else if (mt2 == 0x1) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Adjacent Site"); + if ( ((fr_1t & 0xFF000) >> 12) > 0 ) + { + int adj = (fr_1t & 0xFF000) >> 12; + int adj_l = (fr_1t & 0x1F000000) >> 24; + fprintf (stderr, " :: Site ID [%02X][%03d] on CC LCN [%02d]%s", adj, adj, adj_l, get_lcn_status_string(lcn)); + } + fprintf (stderr, "%s", KNRM); + } + //Status/Message + else if (mt2 == 0x4) + { + int status = (fr_1t & 0xFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KBLU); + if (status == 248) fprintf (stderr, " Status Request :: Target [%08d]", source); + else fprintf (stderr, " Message Acknowledgement :: Status [%03d] Source [%08d]", status, source); + fprintf (stderr, "%s", KNRM); + } + //Control Channel LCN + else if (mt2 == 0x8) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Control Channel"); + if (((fr_4t >> 12) & 0x1F) != 0) + { + state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F); + if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val + { + state->edacs_lcn_count = state->edacs_cc_lcn; + } + fprintf (stderr, " :: LCN [%d]%s", state->edacs_cc_lcn, get_lcn_status_string(lcn)); + + //check for control channel lcn frequency if not provided in channel map or in the lcn list + if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0) + { + long int lcnfreq = 0; + //if using rigctl, we can ask for the currrent frequency + if (opts->use_rigctl == 1) + { + lcnfreq = GetCurrentFreq (opts->rigctl_sockfd); + if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; + } + //if using rtl input, we can ask for the current frequency tuned + if (opts->audio_in_type == 3) + { + lcnfreq = (long int)opts->rtlsdr_center_freq; + if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; + } + } + + //set trunking cc here so we know where to come back to + if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0) + { + state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1 + } + } + fprintf (stderr, "%s", KNRM); + } + //Site ID + else if (mt2 == 0xA) + { + site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19); + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Extended Addressing :: Site ID [%02llX][%03lld]", site_id, site_id); + fprintf (stderr, "%s", KNRM); + state->edacs_site_id = site_id; + } + //disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy + // else if (mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when. + // { + // int kicked = (fr_4t & 0xFFFFF000) >> 12; + // fprintf (stderr, " FR_1 [%010llX]", fr_1t); + // fprintf (stderr, " FR_3 [%010llX]", fr_3); + // fprintf (stderr, " FR_4 [%010llX]", fr_4t); + // fprintf (stderr, " FR_6 [%010llX]", fr_6); + // fprintf (stderr, " %08d REG/DEREG/AUTH?", kicked); + // } + //Patch Groups + else if (mt2 == 0xC) + { + int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed? + int sourcep = ((fr_1t & 0xFFFF000) >> 12); + int targetp = ((fr_4t & 0xFFFF000) >> 12); + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Patch :: Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp); + fprintf (stderr, "%s", KNRM); + } + //Serial Number Request (not seen in the wild, see US patent 20030190923, Figure 2b) + else if (mt2 == 0xD) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Serial Number Request"); + fprintf (stderr, "%s", KNRM); + } + else + { + fprintf (stderr, "%s", KWHT); + fprintf (stderr, " Unknown Command"); + fprintf (stderr, "%s", KNRM); + // Only print the payload if we haven't already printed it + if (opts->payload != 1) + { + fprintf (stderr, " ::"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } + } + + } + //TDMA Group Grant Update (never observed, unknown if ever used on any EDACS system) + else if (mt1 == 0x1) + { + lcn = (fr_1t & 0x3E0000000) >> 29; + int group = (fr_1t & 0xFFFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KGRN); + fprintf (stderr, " TDMA Group Call :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn)); fprintf (stderr, "%s", KNRM); - state->edacs_site_id = site_id; } - //Patch Groups - else if (mt1 == 0x1F && mt2 == 0xC) + //Data Group Grant Update + else if (mt1 == 0x2) { - int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed? - int sourcep = ((fr_1t & 0xFFFF000) >> 12); - int targetp = ((fr_4t & 0xFFFF000) >> 12); - fprintf (stderr, " Patch -- Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp); + lcn = (fr_1t & 0x3E0000000) >> 29; + int group = (fr_1t & 0xFFFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Data Group Call :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); } - //Adjacent Sites - else if (mt1 == 0x1F && mt2 == 0x1) - { - fprintf (stderr, " Adjacent Site"); - if ( ((fr_1t & 0xFF000) >> 12) > 0 ) - { - int adj = (fr_1t & 0xFF000) >> 12; - int adj_l = (fr_1t & 0x1F000000) >> 24; - fprintf (stderr, " [%02X][%03d] on CC LCN [%02d]", adj, adj, adj_l); - } - } - //Control Channel LCN - else if (mt1 == 0x1F && mt2 == 0x8) - { - fprintf (stderr, " Control Channel LCN"); - if (((fr_4t >> 12) & 0x1F) != 0) - { - state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F); - if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val - { - state->edacs_lcn_count = state->edacs_cc_lcn; - } - fprintf (stderr, " [%d]", state->edacs_cc_lcn); - - //check for control channel lcn frequency if not provided in channel map or in the lcn list - if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0) - { - long int lcnfreq = 0; - //if using rigctl, we can ask for the currrent frequency - if (opts->use_rigctl == 1) - { - lcnfreq = GetCurrentFreq (opts->rigctl_sockfd); - if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; - } - //if using rtl input, we can ask for the current frequency tuned - if (opts->audio_in_type == 3) - { - lcnfreq = (long int)opts->rtlsdr_center_freq; - if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; - } - } - - //set trunking cc here so we know where to come back to - if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0) - { - state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1 - } - } - } - //disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy - // else if (mt1 == 0x1F && mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when. - // { - // int kicked = (fr_4t & 0xFFFFF000) >> 12; - // fprintf (stderr, " FR_1 [%010llX]", fr_1t); - // fprintf (stderr, " FR_3 [%010llX]", fr_3); - // fprintf (stderr, " FR_4 [%010llX]", fr_4t); - // fprintf (stderr, " FR_6 [%010llX]", fr_6); - // fprintf (stderr, " Kick Command?"); - // } //Voice Call Grant Update - // mt1 0x3 is Digital group voice call, 0x2 Group Data Channel, 0x1 TDMA call - else if (mt1 >= 0x1 && mt1 <= 0x3) + // MT1 value determines the type of group call: + // - 0x03 digital group voice (ProVoice, standard on SLERS EA) + // - 0x06 analog group voice + else if (mt1 == 0x3 || mt1 == 0x6) { + lcn = (fr_1t & 0x3E0000000) >> 29; + //LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc" - if (lcn > state->edacs_lcn_count && lcn < 26) + if (lcn > state->edacs_lcn_count && lcn < 26) { state->edacs_lcn_count = lcn; } + int is_digital = (mt1 == 0x3) ? 1 : 0; + int is_update = (fr_1t & 0x10000000) >> 28; + int is_emergency = (fr_4t & 0x100000000) >> 32; + int is_tx_trunking = (fr_4t & 0x200000000) >> 33; int group = (fr_1t & 0xFFFF000) >> 12; int source = (fr_4t & 0xFFFFF000) >> 12; - if (group != 0) state->lasttg = group; - if (source != 0) state->lastsrc = source; + + //Call info for state if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = group; // 0 is a valid TG, it's the all-call for agency 0 + if (source != 0) state->lastsrc = source; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_GROUP; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + if (is_emergency == 1) state->edacs_vc_call_type |= EDACS_IS_EMERGENCY; + fprintf (stderr, "%s", KGRN); - fprintf (stderr, " Group [%05d] Source [%08d] LCN[%02d]", group, source, lcn); + if (is_digital == 0) fprintf (stderr, " Analog Group Call"); + else fprintf (stderr, " Digital Group Call"); + if (is_update == 0) fprintf (stderr, " Assignment"); + else fprintf (stderr, " Update"); + fprintf (stderr, " :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn)); + + //Trunking mode is correlated to (but not guaranteed to match) the type of call: + // - emergency calls - usually message trunking + // - normal calls - usually transmission trunking + if (is_tx_trunking == 0) fprintf (stderr, " [Message Trunking]"); + if (is_emergency == 1) + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, " [EMERGENCY]"); + } + fprintf (stderr, "%s", KNRM); char mode[8]; //allow, block, digital enc sprintf (mode, "%s", ""); @@ -638,33 +754,30 @@ void edacs(dsd_opts * opts, dsd_state * state) if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B"); if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A"); - if (mt1 == 0x1) fprintf (stderr, " TDMA Call"); //never observed, wonder if any EDACS systems ever carried a TDMA signal (X2-TDMA?) - if (mt1 == 0x2) fprintf (stderr, " Group Data Call"); //Never Seen this one before - if (mt1 == 0x3) fprintf (stderr, " Digital Call"); //ProVoice, this is what we always get on SLERS EA - fprintf (stderr, "%s", KNRM); - //this is working now with the new import setup - if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + if (opts->trunk_tune_group_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block { - if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) { //openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) { sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %d SRC %d.wav", getDateE(), timestr, state->edacs_site_id, group, source); - openWavOutFile (opts, state); - // openWavOutFile48k (opts, state); //debug for testing analog wav only + if (is_digital == 1) + openWavOutFile (opts, state); + else + openWavOutFile48k (opts, state); } - + //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well if (opts->use_rigctl == 1) { - if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); - SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero + if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero state->edacs_tuned_lcn = lcn; opts->p25_is_tuned = 1; - //debug testing (since I don't have EDACS standard w/ Analog nearby) - // edacs_analog(opts, state, group, lcn); + if (is_digital == 0) + edacs_analog(opts, state, group, lcn); } if (opts->audio_in_type == 3) //rtl dongle @@ -673,91 +786,304 @@ void edacs(dsd_opts * opts, dsd_state * state) rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); state->edacs_tuned_lcn = lcn; opts->p25_is_tuned = 1; - //debug testing (since I don't have EDACS standard w/ Analog nearby) - // edacs_analog(opts, state, group, lcn); + if (is_digital == 0) + edacs_analog(opts, state, group, lcn); #endif } } - + } } - else //print frames for debug/analysis + //I-Call Grant Update + else if (mt1 == 0x10) { - fprintf (stderr, " FR_1 [%010llX]", fr_1t); - fprintf (stderr, " FR_4 [%010llX]", fr_4t); + lcn = (fr_4t & 0x1F00000000) >> 32; + + //LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc" + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + int is_digital = (fr_1t & 0x200000000) >> 33; + int is_update = (fr_1t & 0x100000000) >> 32; + int target = (fr_1t & 0xFFFFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + if (target != 0) state->lasttg = target; + if (source != 0) state->lastsrc = source; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_INDIVIDUAL; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + + fprintf (stderr, "%s", KCYN); + if (is_digital == 0) fprintf (stderr, " Analog I-Call"); + else fprintf (stderr, " Digital I-Call"); + if (is_update == 0) fprintf (stderr, " Assignment"); + else fprintf (stderr, " Update"); + + fprintf (stderr, " :: Target [%08d] Source [%08d] LCN [%02d]%s", target, source, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + char mode[8]; //allow, block, digital enc + sprintf (mode, "%s", ""); + + //if we are using allow/whitelist mode, then write 'B' to mode for block - no allow/whitelist support for i-calls + if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B"); + + //this is working now with the new import setup + if (opts->trunk_tune_private_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + { + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + { + //openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) + if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) + { + sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %d SRC %d I-Call.wav", getDateE(), timestr, state->edacs_site_id, target, source); + if (is_digital == 1) + openWavOutFile (opts, state); + else + openWavOutFile48k (opts, state); + } + + //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well + if (opts->use_rigctl == 1) + { + if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) + edacs_analog(opts, state, target, lcn); + } + + if (opts->audio_in_type == 3) //rtl dongle + { + #ifdef USE_RTLSDR + rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) + edacs_analog(opts, state, target, lcn); + #endif + } + + } + } + } + //Channel assignment (unknown reason, just know it assigns an LCN in the expected order; believed related to data) + else if (mt1 == 0x12) + { + lcn = (fr_4t & 0x1F00000000) >> 32; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Channel Assignment (Unknown Data) :: Source [%08d] LCN [%02d]%s", source, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + //LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc" + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + if (source != 0) state->lastsrc = source; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_INDIVIDUAL; + } + //System All-Call Grant Update + else if (mt1 == 0x16) + { + lcn = (fr_1t & 0x3E0000000) >> 29; + + //LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc" + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + int is_digital = (fr_1t & 0x10000000) >> 28; + int is_update = (fr_1t & 0x8000000) >> 27; + int source = (fr_4t & 0xFFFFF000) >> 12; + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = 0; + if (source != 0) state->lastsrc = source; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_ALL_CALL; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + + fprintf (stderr, "%s", KMAG); + if (is_digital == 0) fprintf (stderr, " Analog System All-Call"); + else fprintf (stderr, " Digital System All-Call"); + if (is_update == 0) fprintf (stderr, " Assignment"); + else fprintf (stderr, " Update"); + + fprintf (stderr, " :: Source [%08d] LCN [%02d]%s", source, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + char mode[8]; //allow, block, digital enc + sprintf (mode, "%s", ""); + + //if we are using allow/whitelist mode, then write 'A' to mode for allow - always allow all-calls by default + if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "A"); + + //this is working now with the new import setup + if (opts->trunk_tune_group_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + { + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + { + //openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) + if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) + { + sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld SRC %d All-Call.wav", getDateE(), timestr, state->edacs_site_id, source); + if (is_digital == 1) + openWavOutFile (opts, state); + else + openWavOutFile48k (opts, state); + } + + //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well + if (opts->use_rigctl == 1) + { + if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) + edacs_analog(opts, state, -1, lcn); + } + + if (opts->audio_in_type == 3) //rtl dongle + { + #ifdef USE_RTLSDR + rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) + edacs_analog(opts, state, -1, lcn); + #endif + } + + } + + } + } + //Login + else if (mt1 == 0x19) + { + int group = (fr_1t & 0xFFFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Login :: Group [%05d] Source [%08d]", group, source); + fprintf (stderr, "%s", KNRM); + } + //Unknown command + else + { + fprintf (stderr, "%s", KWHT); fprintf (stderr, " Unknown Command"); + fprintf (stderr, "%s", KNRM); + // Only print the payload if we haven't already printed it + if (opts->payload != 1) + { + fprintf (stderr, " ::"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } } } //Start Standard or Networked Mode else if (state->ea_mode == 0) { - //standard or networked - command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask; - lcn = (fr_1t & 0xF8000000) >> 27; + unsigned char mt_a = (fr_1t & 0xE000000000) >> 37; + unsigned char mt_b = (fr_1t & 0x1C00000000) >> 34; + unsigned char mt_d = (fr_1t & 0x3E0000000) >> 29; - //site ID and CC LCN - if (command == 0xFD) + state->edacs_vc_call_type = 0; + + //Add raw payloads and MT-A/MT-B/MT-D for easy debug + if (opts->payload == 1) { - int site_id = (fr_1t & 0xFF000) >> 12; - int cc_lcn = (fr_1t & 0x1F000000) >> 24; - - fprintf (stderr, "%s", KYEL); - fprintf (stderr, " Site ID [%02X][%03d] on CC LCN [%02d] Standard/Networked", site_id, site_id, cc_lcn); - fprintf (stderr, "%s", KNRM); - state->edacs_site_id = site_id; - state->edacs_cc_lcn = cc_lcn; - - if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + fprintf (stderr, " (MT-A: %X", mt_a); + // MT-B is meaningless if MT-A is not 0x7 + if (mt_a == 0x7) { - state->edacs_lcn_count = state->edacs_cc_lcn; - } - - //check for control channel lcn frequency if not provided in channel map or in the lcn list - if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0) - { - long int lcnfreq = 0; - //if using rigctl, we can ask for the currrent frequency - if (opts->use_rigctl == 1) + fprintf (stderr, "; MT-B: %X", mt_b); + // MT-D is meaningless if MT-B is not 0x7 + if (mt_b == 0x7) { - lcnfreq = GetCurrentFreq (opts->rigctl_sockfd); - if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; + fprintf (stderr, "; MT-D: %02X) ", mt_d); } - //if using rtl input, we can ask for the current frequency tuned - if (opts->audio_in_type == 3) + else { - lcnfreq = (long int)opts->rtlsdr_center_freq; - if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; + fprintf (stderr, ") "); } } - - //set trunking cc here so we know where to come back to - if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0) + else { - state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1 + fprintf (stderr, ") "); } - } - //voice call assignment - else if (command == 0xEE || command == 0xEF) - { + //The following is heavily based on TIA/EIA Telecommunications System Bulletin 69.3, "Enhanced Digital Access + //Communications System (EDACS) Digital Air Interface for Channel Access, Modulation, Messages, and Formats", + //April 1998. Where real world systems are found to diverge from this bulletin, please note the basis for the + //deviation. - if (lcn > state->edacs_lcn_count && lcn < 26) + //MT-A 0 and 1 as analog/digital mode indicator reverse engineered from Montreal STM and San Antonio/Bexar Co + //systems; occurs immediately prior to Voice Group Channel Update. + // + //Voice Group Channel Assignment (6.2.4.1) + //Emergency Voice Group Channel Assignment (6.2.4.2) + if (mt_a == 0x0 || mt_a == 0x1 || mt_a == 0x2 || mt_a == 0x3) + { + int is_digital = (mt_a == 0x2 || mt_a == 0x3) ? 1 : 0; + int is_emergency = (mt_a == 0x1 || mt_a == 0x3) ? 1 : 0; + int lid = ((fr_1t & 0x1FC0000000) >> 23) | ((fr_4t & 0xFE0000000) >> 29); + int lcn = (fr_1t & 0x1F000000) >> 24; + int is_tx_trunk = (fr_1t & 0x800000) >> 23; + int group = (fr_1t & 0x7FF000) >> 12; + + fprintf (stderr, "%s", KGRN); + fprintf (stderr, " Voice Group Channel Assignment ::"); + if (is_digital == 0) fprintf (stderr, " Analog"); + else fprintf (stderr, " Digital"); + fprintf (stderr, " Group [%04d] LID [%05d] LCN [%02d]%s", group, lid, lcn, get_lcn_status_string(lcn)); + if (is_tx_trunk == 0) fprintf (stderr, " [Message Trunking]"); + if (is_emergency == 1) + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, " [EMERGENCY]"); + } + fprintf (stderr, "%s", KNRM); + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (lcn > state->edacs_lcn_count && lcn < 26) { state->edacs_lcn_count = lcn; } - state->edacs_vc_lcn = lcn; - //just going to use the default 4-4-3 A-FS scheme, making it user configurable would be a pain - int afs = (fr_1t & 0x7FF000) >> 12; - int a = afs >> 7; - int fs = afs & 0x7F; - int status = (fr_1t & 0xF00000000) >> 32; - UNUSED(status); - if (afs > 0) state->lastsrc = afs; - fprintf (stderr, "%s", KGRN); - fprintf (stderr, " AFS [%03d] [%02d-%03d] LCN [%02d]", afs, a, fs, lcn); + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = group; + state->lastsrc = lid; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_GROUP; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + if (is_emergency == 1) state->edacs_vc_call_type |= EDACS_IS_EMERGENCY; char mode[8]; //allow, block, digital enc sprintf (mode, "%s", ""); @@ -766,67 +1092,49 @@ void edacs(dsd_opts * opts, dsd_state * state) //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B"); + //Get group mode for calls that are in the allow/whitelist for (int i = 0; i < state->group_tally; i++) { - if (state->group_array[i].groupNumber == afs) + if (state->group_array[i].groupNumber == group) { - fprintf (stderr, " [%s]", state->group_array[i].groupName); strcpy (mode, state->group_array[i].groupMode); break; } } - - //TG hold on EDACS Standard/Net -- block non-matching target, allow matching afs (decimal) - if (state->tg_hold != 0 && state->tg_hold != afs) sprintf (mode, "%s", "B"); - if (state->tg_hold != 0 && state->tg_hold == afs) sprintf (mode, "%s", "A"); + //TG hold on EDACS Standard/Net -- block non-matching target, allow matching group + if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B"); + if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A"); //NOTE: Restructured below so that analog and digital are handled the same, just that when //its analog, it will now start edacs_analog which will while loop analog samples until //signal level drops (RMS, or a dotting sequence is detected) - //analog working now with edacs_analog - if (command == 0xEE) - fprintf (stderr, " Analog"); - - //Digital Call (ProVoice, hopefully, and not Aegis in 2022) - else - fprintf (stderr, " Digital"); - - //skip analog calls on RTL Input on ARM devices due to High CPU usage from RTL RMS function - // #ifdef __arm__ - // if (command == 0xEE && opts->audio_in_type == 3) - // goto ENDPV; - // #endif - - - - - //this is working now with the new import setup - if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + if (opts->trunk_tune_group_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block { - if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) { //openwav file and do per call right here if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) { - sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld AFS %02d-%03d - %03d.wav", getDateE(), timestr, state->edacs_site_id, a, fs, afs); - if (command == 0xEF) openWavOutFile (opts, state); //digital - if (command == 0xEE) openWavOutFile48k (opts, state); //analog at 48k + sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %04d SRC %05d.wav", getDateE(), timestr, state->edacs_site_id, group, lid); + if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k + else openWavOutFile (opts, state); //digital } if (opts->use_rigctl == 1) { //only set bandwidth IF we have an original one to fall back to (experimental, but requires user to set the -B 12000 or -B 24000 value manually) - if (opts->setmod_bw != 0 && command == 0xEE) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence - else if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); - - // if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + if (opts->setmod_bw != 0) + { + if (is_digital == 0) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence + else SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + } SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero state->edacs_tuned_lcn = lcn; opts->p25_is_tuned = 1; - if (command == 0xEE) edacs_analog(opts, state, afs, lcn); + if (is_digital == 0) edacs_analog(opts, state, group, lcn); } if (opts->audio_in_type == 3) //rtl dongle @@ -835,33 +1143,700 @@ void edacs(dsd_opts * opts, dsd_state * state) rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); state->edacs_tuned_lcn = lcn; opts->p25_is_tuned = 1; - if (command == 0xEE) edacs_analog(opts, state, afs, lcn); + if (is_digital == 0) edacs_analog(opts, state, group, lcn); #endif } } - } + } + //Data Call Channel Assignment (6.2.4.3) + else if (mt_a == 0x5) + { + int is_individual_call = (fr_1t & 0x1000000000) >> 36; + int is_from_lid = (fr_1t & 0x800000000) >> 35; + int port = ((fr_1t & 0x700000000) >> 29) | ((fr_4t & 0x700000000) >> 32); + int lcn = (fr_1t & 0xF8000000) >> 27; + int is_individual_id = (fr_1t & 0x4000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + int group = (fr_1t & 0x7FF000) >> 12; + + //Abstract away to a target, and be sure to check whether it's an individual call later + int target = (is_individual_id == 0) ? group : lid; + + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Data Call Channel Assignment :: Type"); + if (is_individual_call == 1) fprintf (stderr, " [Individual]"); + else fprintf (stderr, " [Group]"); + if (is_individual_id == 1) fprintf (stderr, " LID [%05d]", target); + else fprintf (stderr, " Group [%04d]", target); + if (is_from_lid == 1) fprintf (stderr, " -->"); + else fprintf (stderr, " <--"); + fprintf (stderr, " Port [%02d] LCN [%02d]%s", port, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = target; + state->lastsrc = 0; + + //Call type for state + if (is_individual_call == 0) state->edacs_vc_call_type = EDACS_IS_GROUP; + else state->edacs_vc_call_type = EDACS_IS_INDIVIDUAL; + } + //Login Acknowledge (6.2.4.4) + else if (mt_a == 0x6) + { + int group = (fr_1t & 0x1FFC000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Login Acknowledgement :: Group [%04d] LID [%05d]", group, lid); fprintf (stderr, "%s", KNRM); } - //Network Command, Not sure of how to handle, but just show frames - else if (command == 0xF3) + //Use MT-B + else if (mt_a == 0x7) { - fprintf (stderr, " FR_1 [%010llX]", fr_1t); - fprintf (stderr, " FR_4 [%010llX]", fr_4t); - fprintf (stderr, " Network Command"); - } + //Status Request / Message Acknowledge (6.2.4.5) + if (mt_b == 0x0) + { + int status = (fr_1t & 0x3FC000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; - else //print frames for debug/analysis + fprintf (stderr, "%s", KBLU); + if (status == 248) fprintf (stderr, " Status Request :: LID [%05d]", lid); + else fprintf (stderr, " Message Acknowledgement :: Status [%03d] LID [%05d]", status, lid); + fprintf (stderr, "%s", KNRM); + } + //Interconnect Channel Assignment (6.2.4.6) + else if (mt_b == 0x1) + { + int mt_c = (fr_1t & 0x300000000) >> 32; + int lcn = (fr_1t & 0xF8000000) >> 27; + int is_individual_id = (fr_1t & 0x4000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + int group = (fr_1t & 0x7FF000) >> 12; + + //Abstract away to a target, and be sure to check whether it's an individual call later + int target = (is_individual_id == 0) ? group : lid; + + //Technically only MT-C 0x2 is defined in TSB 69.3 - using and extrapolating on legacy code + int is_digital = (mt_c == 2 || mt_c == 3) ? 1 : 0; + + fprintf (stderr, "%s", KMAG); + fprintf (stderr, " Interconnect Channel Assignment :: Type"); + if (is_digital == 0) fprintf (stderr, " Analog"); + else fprintf (stderr, " Digital"); + if (is_individual_id == 1) fprintf (stderr, " LID [%05d]", target); + else fprintf (stderr, " Group [%04d]", target); + fprintf (stderr, " LCN [%02d]%s", lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = 0; + state->lastsrc = target; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_INTERCONNECT; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + } + //Channel Updates (6.2.4.7) + //Source/caller being present in individual call channel updates reverse engineered from Montreal STM system + else if (mt_b == 0x3) + { + int mt_c = (fr_1t & 0x300000000) >> 32; + int lcn = (fr_1t & 0xF8000000) >> 27; + int is_individual = (fr_1t & 0x4000000) >> 26; + int is_emergency = (is_individual == 0) ? (fr_1t & 0x2000000) >> 25 : 0; + int group = (fr_1t & 0x7FF000) >> 12; + int lid = (fr_1t & 0x3FFF000) >> 12; + int source = (fr_4t & 0x3FFF000) >> 12; //Source only present in individual calls + + //Abstract away to a target, and be sure to check whether it's an individual call later + int target = (is_individual == 0) ? group : lid; + + //Technically only MT-C 0x1 and 0x3 are defined in TSB 69.3 - using and extrapolating on legacy code + int is_tx_trunk = (mt_c == 2 || mt_c == 3) ? 1 : 0; + int is_digital = (mt_c == 1 || mt_c == 3) ? 1 : 0; + + if (is_individual == 0) + { + fprintf (stderr, "%s", KGRN); + fprintf (stderr, " Voice Group Channel Update ::"); + } + else + { + fprintf (stderr, "%s", KCYN); + fprintf (stderr, " Voice Individual Channel Update ::"); + } + if (is_digital == 0) fprintf (stderr, " Analog"); + else fprintf (stderr, " Digital"); + if (is_individual == 0) fprintf (stderr, " Group [%04d]", target); + else fprintf (stderr, " Callee [%05d] Caller [%05d]", target, source); + fprintf (stderr, " LCN [%02d]%s", lcn, get_lcn_status_string(lcn)); + if (is_tx_trunk == 0) fprintf (stderr, " [Message Trunking]"); + if (is_emergency == 1) + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, " [EMERGENCY]"); + } + fprintf (stderr, "%s", KNRM); + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = target; + //Alas, EDACS standard does not provide a source LID on channel updates - try to work around this on the display end instead + state->lastsrc = 0; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE; + if (is_individual == 0) state->edacs_vc_call_type |= EDACS_IS_GROUP; + else state->edacs_vc_call_type |= EDACS_IS_INDIVIDUAL; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + if (is_emergency == 1) state->edacs_vc_call_type |= EDACS_IS_EMERGENCY; + + char mode[8]; //allow, block, digital enc + sprintf (mode, "%s", ""); + + //if we are using allow/whitelist mode, then write 'B' to mode for block + //comparison below will look for an 'A' to write to mode if it is allowed + if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B"); + + //Individual calls always remain blocked if in allow/whitelist mode + if (is_individual == 0) + { + //Get group mode for calls that are in the allow/whitelist + for (int i = 0; i < state->group_tally; i++) + { + if (state->group_array[i].groupNumber == target) + { + strcpy (mode, state->group_array[i].groupMode); + break; + } + } + //TG hold on EDACS Standard/Net -- block non-matching target, allow matching group + if (state->tg_hold != 0 && state->tg_hold != target) sprintf (mode, "%s", "B"); + if (state->tg_hold != 0 && state->tg_hold == target) sprintf (mode, "%s", "A"); + } + + //NOTE: Restructured below so that analog and digital are handled the same, just that when + //its analog, it will now start edacs_analog which will while loop analog samples until + //signal level drops (RMS, or a dotting sequence is detected) + + //this is working now with the new import setup + if (((is_individual == 0 && opts->trunk_tune_group_calls == 1) || (is_individual == 1 && opts->trunk_tune_private_calls == 1)) && + opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + { + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + { + //openwav file and do per call right here + if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) + { + if (is_individual == 0) sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %04d SRC %05d.wav", getDateE(), timestr, state->edacs_site_id, target, state->lastsrc); + else sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %05d SRC %05d I-Call.wav", getDateE(), timestr, state->edacs_site_id, target, state->lastsrc); + if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k + else openWavOutFile (opts, state); //digital + } + + if (opts->use_rigctl == 1) + { + //only set bandwidth IF we have an original one to fall back to (experimental, but requires user to set the -B 12000 or -B 24000 value manually) + if (opts->setmod_bw != 0) + { + if (is_digital == 0) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence + else SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + } + + SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) edacs_analog(opts, state, target, lcn); + } + + if (opts->audio_in_type == 3) //rtl dongle + { + #ifdef USE_RTLSDR + rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) edacs_analog(opts, state, target, lcn); + #endif + } + } + } + } + //System Assigned ID (6.2.4.8) + else if (mt_b == 0x4) + { + int sgid = (fr_1t & 0x3FF800000) >> 23; + int group = (fr_1t & 0x7FF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " System Assigned ID :: SGID [%04d] Group [%04d]", sgid, group); + fprintf (stderr, "%s", KNRM); + } + //Individual Call Channel Assignment (6.2.4.9) + //Analog and digital flag reverse engineered from Montreal STM system + else if (mt_b == 0x5) + { + int is_tx_trunk = (fr_1t & 0x200000000) >> 33; + int lcn = (fr_1t & 0xF8000000) >> 27; + int is_digital = (fr_1t & 0x4000000) >> 26; + int target = (fr_1t & 0x3FFF000) >> 12; + int source = (fr_4t & 0x3FFF000) >> 12; + + fprintf (stderr, "%s", KCYN); + fprintf (stderr, " Individual Call Channel Assignment ::"); + if (is_digital == 0) fprintf (stderr, " Analog"); + else fprintf (stderr, " Digital"); + fprintf (stderr, " Callee [%05d] Caller [%05d] LCN [%02d]%s", target, source, lcn, get_lcn_status_string(lcn)); + if (is_tx_trunk == 0) fprintf (stderr, " [Message Trunking]"); + fprintf (stderr, "%s", KNRM); + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = target; + state->lastsrc = source; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_INDIVIDUAL; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + + char mode[8]; //allow, block, digital enc + sprintf (mode, "%s", ""); + + //if we are using allow/whitelist mode, then write 'B' to mode for block + //Individual calls always remain blocked if in allow/whitelist mode + if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B"); + + //NOTE: Restructured below so that analog and digital are handled the same, just that when + //its analog, it will now start edacs_analog which will while loop analog samples until + //signal level drops (RMS, or a dotting sequence is detected) + + //this is working now with the new import setup + if ((opts->trunk_tune_private_calls == 1) && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + { + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + { + //openwav file and do per call right here + if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) + { + sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %05d SRC %05d I-Call.wav", getDateE(), timestr, state->edacs_site_id, target, state->lastsrc); + if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k + else openWavOutFile (opts, state); //digital + } + + if (opts->use_rigctl == 1) + { + //only set bandwidth IF we have an original one to fall back to (experimental, but requires user to set the -B 12000 or -B 24000 value manually) + if (opts->setmod_bw != 0) + { + if (is_digital == 0) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence + else SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + } + + SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) edacs_analog(opts, state, target, lcn); + } + + if (opts->audio_in_type == 3) //rtl dongle + { + #ifdef USE_RTLSDR + rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) edacs_analog(opts, state, target, lcn); + #endif + } + } + } + } + //Console Unkey / Drop (6.2.4.10) + else if (mt_b == 0x6) + { + int is_drop = (fr_1t & 0x80000000) >> 31; + int lcn = (fr_1t & 0x7C000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Console "); + if (is_drop == 0) fprintf (stderr, " Unkey"); + else fprintf (stderr, " Drop"); + fprintf (stderr, " :: LID [%05d] LCN [%02d]%s", lid, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + } + //Use MT-D + else if (mt_b == 0x7) + { + //Cancel Dynamic Regroup (6.2.4.11) + if (mt_d == 0x00) + { + int knob = (fr_1t & 0x1C000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Cancel Dynamic Regroup :: LID [%05d] Knob position [%1d]", lid, knob + 1); + fprintf (stderr, "%s", KNRM); + } + //Adjacent Site Control Channel (6.2.4.12) + else if (mt_d == 0x01) + { + int adj_cc_lcn = (fr_1t & 0x1F000000) >> 24; + int adj_site_index = (fr_1t & 0xE00000) >> 21; + int adj_site_id = (fr_1t & 0x1F0000) >> 16; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Adjacent Site Control Channel :: Site ID [%02X][%03d] Index [%1d] LCN [%02d]%s", adj_site_id, adj_site_id, adj_site_index, adj_cc_lcn, get_lcn_status_string(adj_cc_lcn)); + if (adj_site_id == 0 && adj_site_index == 0) fprintf (stderr, " [Adjacency Table Reset]"); + else if (adj_site_id != 0 && adj_site_index == 0) fprintf (stderr, " [Priority System Definition]"); + else if (adj_site_id == 0 && adj_site_index != 0) fprintf (stderr, " [Adjacencies Table Length Definition]"); + else fprintf (stderr, " [Adjacent System Definition]"); + fprintf (stderr, "%s", KNRM); + } + //Extended Site Options (6.2.4.13) + else if (mt_d == 0x02) + { + int msg_num = (fr_1t & 0xE000000) >> 25; + int data = (fr_1t & 0x1FFF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Extended Site Options :: Message Num [%1d] Data [%04X]", msg_num, data); + fprintf (stderr, "%s", KNRM); + } + //System Dynamic Regroup Plan Bitmap (6.2.4.14) + else if (mt_d == 0x04) + { + int bank = (fr_1t & 0x10000000) >> 28; + int resident = (fr_1t & 0xFF00000) >> 20; + int active = (fr_1t & 0xFF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " System Dynamic Regroup Plan Bitmap :: Plan Bank [%1d] Resident [", bank); + + int plan = bank * 8; + int first = 1; + while (resident != 0) { + if (resident & 0x1 == 1) { + if (first == 1) + { + first = 0; + fprintf (stderr, "%d", plan); + } + else + { + fprintf (stderr, ", %d", plan); + } + } + resident >>= 1; + plan++; + } + + fprintf (stderr, "] Active ["); + + plan = bank * 8; + first = 1; + while (active != 0) { + if (active & 0x1 == 1) { + if (first == 1) + { + first = 0; + fprintf (stderr, "%d", plan); + } + else + { + fprintf (stderr, ", %d", plan); + } + } + active >>= 1; + plan++; + } + + fprintf (stderr, "]"); + fprintf (stderr, "%s", KNRM); + } + //Assignment to Auxiliary Control Channel (6.2.4.15) + else if (mt_d == 0x05) + { + int aux_cc_lcn = (fr_1t & 0x1F000000) >> 24; + int group = (fr_1t & 0x7FF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Assignment to Auxiliary CC :: Group [%04d] Aux CC LCN [%02d]%s", group, aux_cc_lcn, get_lcn_status_string(aux_cc_lcn)); + fprintf (stderr, "%s", KNRM); + } + //Initiate Test Call Command (6.2.4.16) + else if (mt_d == 0x06) + { + int cc_lcn = (fr_1t & 0x1F000000) >> 24; + int wc_lcn = (fr_1t & 0xF80000) >> 19; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Initiate Test Call Command :: CC LCN [%02d] WC LCN [%02d]", cc_lcn, wc_lcn); + fprintf (stderr, "%s", KNRM); + } + //Unit Enable / Disable (6.2.4.17) + else if (mt_d == 0x07) + { + int qualifier = (fr_1t & 0xC000000) >> 26; + int target = (fr_1t & 0x3FFF000) >> 12; + + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Unit Enable/Disable ::"); + if (qualifier == 0x0) fprintf (stderr, " [Temporary Disable]"); + else if (qualifier == 0x1) fprintf (stderr, " [Corrupt Personality]"); + else if (qualifier == 0x2) fprintf (stderr, " [Revoke Logical ID]"); + else fprintf (stderr, " [Re-enable Unit]"); + fprintf (stderr, " LID [%05d]", target); + fprintf (stderr, "%s", KNRM); + } + //Site ID (6.2.4.18) + else if (mt_d == 0x08 || mt_d == 0x09 || mt_d == 0x0A || mt_d == 0x0B) + { + int cc_lcn = (fr_1t & 0x1F000000) >> 24; + int priority = (fr_1t & 0xE00000) >> 21; + int is_scat = (fr_1t & 0x80000) >> 19; + int is_failsoft = (fr_1t & 0x40000) >> 18; + int is_auxiliary = (fr_1t & 0x20000) >> 17; + int site_id = (fr_1t & 0x1F000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Standard/Networked :: Site ID [%02X][%03d] Priority [%1d] CC LCN [%02d]%s", site_id, site_id, priority, cc_lcn, get_lcn_status_string(cc_lcn)); + if (is_failsoft == 1) + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, " [FAILSOFT]"); + fprintf (stderr, "%s", KYEL); + } + if (is_scat == 1) fprintf (stderr, " [SCAT]"); + if (is_auxiliary == 1) fprintf (stderr, " [Auxiliary]"); + fprintf (stderr, "%s", KNRM); + + //Store our site ID + state->edacs_site_id = site_id; + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (state->edacs_cc_lcn > state->edacs_lcn_count && cc_lcn < 26) + { + state->edacs_lcn_count = state->edacs_cc_lcn; + } + + //If this is only an auxiliary CC, keep searching for the primary CC + if (is_auxiliary == 0) + { + //Store our CC LCN + state->edacs_cc_lcn = cc_lcn; + + //Check for control channel LCN frequency if not provided in channel map or in the LCN list + if (state->trunk_lcn_freq[state->edacs_cc_lcn - 1] == 0) + { + //If using rigctl, we can ask for the currrent frequency + if (opts->use_rigctl == 1) + { + long int lcnfreq = GetCurrentFreq (opts->rigctl_sockfd); + if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn - 1] = lcnfreq; + } + + //If using rtl input, we can ask for the current frequency tuned + if (opts->audio_in_type == 3) + { + long int lcnfreq = (long int)opts->rtlsdr_center_freq; + if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn - 1] = lcnfreq; + } + } + + //Set trunking CC here so we know where to come back to + if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn - 1] != 0) + { + //Index starts at zero, LCNs locally here start at 1 + state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn - 1]; + } + } + } + //System All-Call (6.2.4.19) + //Analog and digital flag extrapolated from reverse engineering of other messages + else if (mt_d == 0x0F) + { + int lcn = (fr_1t & 0x1F000000) >> 24; + int is_digital = (fr_1t & 0x800000) >> 23; + int is_update = (fr_1t & 0x400000) >> 22; + int is_tx_trunk = (fr_1t & 0x200000) >> 21; + int lid = ((fr_1t & 0x7F000) >> 12) | ((fr_4t & 0xFE000) >> 6); + + fprintf (stderr, "%s", KMAG); + fprintf (stderr, " System All-Call Channel"); + if (is_update == 0) fprintf (stderr, " Assignment"); + else fprintf (stderr, " Update"); + fprintf (stderr, " ::"); + if (is_digital == 0) fprintf (stderr, " Analog"); + else fprintf (stderr, " Digital"); + fprintf (stderr, " LID [%05d] LCN [%02d]%s", lid, lcn, get_lcn_status_string(lcn)); + if (is_tx_trunk == 0) fprintf (stderr, " [Message Trunking]"); + fprintf (stderr, "%s", KNRM); + + //LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc) + if (lcn > state->edacs_lcn_count && lcn < 26) + { + state->edacs_lcn_count = lcn; + } + + //Call info for state + if (lcn != 0) state->edacs_vc_lcn = lcn; + state->lasttg = 0; + state->lastsrc = lid; + + //Call type for state + state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_ALL_CALL; + if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL; + + char mode[8]; //allow, block, digital enc + sprintf (mode, "%s", ""); + + //if we are using allow/whitelist mode, then write 'A' to mode for allow - always allow all-calls by default + if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "A"); + + //NOTE: Restructured below so that analog and digital are handled the same, just that when + //its analog, it will now start edacs_analog which will while loop analog samples until + //signal level drops (RMS, or a dotting sequence is detected) + + //this is working now with the new import setup + if ((opts->trunk_tune_group_calls == 1) && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + { + if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + { + //openwav file and do per call right here + if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) + { + sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld SRC %05d All-Call.wav", getDateE(), timestr, state->edacs_site_id, state->lastsrc); + if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k + else openWavOutFile (opts, state); //digital + } + + if (opts->use_rigctl == 1) + { + //only set bandwidth IF we have an original one to fall back to (experimental, but requires user to set the -B 12000 or -B 24000 value manually) + if (opts->setmod_bw != 0) + { + if (is_digital == 0) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence + else SetModulation(opts->rigctl_sockfd, opts->setmod_bw); + } + + SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) edacs_analog(opts, state, 0, lcn); + } + + if (opts->audio_in_type == 3) //rtl dongle + { + #ifdef USE_RTLSDR + rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) edacs_analog(opts, state, 0, lcn); + #endif + } + } + } + } + //Dynamic Regrouping (6.2.4.20) + else if (mt_d == 0x10) + { + int fleet_bits = (fr_1t & 0x1C000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + int plan = (fr_4t & 0x1E0000000) >> 29; + int type = (fr_4t & 0x18000000) >> 27; + int knob = (fr_4t & 0x7000000) >> 24; + int group = (fr_4t & 0x7FF000) >> 12; + + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Dynamic Regrouping :: Plan [%02d] Knob position [%1d] LID [%05d] Group [%04d] Fleet bits [%1d]", plan, knob + 1, lid, group, fleet_bits); + if (type == 0) fprintf (stderr, " [Forced select, no deselect]"); + else if (type == 1) fprintf (stderr, " [Forced select, optional deselect]"); + else if (type == 2) fprintf (stderr, " [Reserved]"); + else fprintf (stderr, " [Optional select]"); + fprintf (stderr, "%s", KNRM); + } + //Reserved command (MT-D) + else + { + fprintf (stderr, "%s", KWHT); + fprintf (stderr, " Reserved Command (MT-D)"); + fprintf (stderr, "%s", KNRM); + // Only print the payload if we haven't already printed it + if (opts->payload != 1) + { + fprintf (stderr, " ::"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } + } + } + //Reserved command (MT-B) + else + { + fprintf (stderr, "%s", KWHT); + fprintf (stderr, " Reserved Command (MT-B)"); + fprintf (stderr, "%s", KNRM); + // Only print the payload if we haven't already printed it + if (opts->payload != 1) + { + fprintf (stderr, " ::"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } + } + } + //Reserved command (MT-A) + else { - fprintf (stderr, " FR_1 [%010llX]", fr_1t); - fprintf (stderr, " FR_4 [%010llX]", fr_4t); - fprintf (stderr, " Unknown Command"); + fprintf (stderr, "%s", KWHT); + fprintf (stderr, " Reserved Command (MT-A)"); + fprintf (stderr, "%s", KNRM); + // Only print the payload if we haven't already printed it + if (opts->payload != 1) + { + fprintf (stderr, " ::"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } } } //end Standard or Networked - //supply user warning to use -9 switch if decoding doesn't start shortly - else fprintf (stderr, " Net/EA Auto Detect; Use -9 CLI Switch For Standard;"); + //let users know they need to select an operational mode with the switches below + else + { + fprintf (stderr, " Detected: Use -fh, -fH, -fe, or -fE for std, esk, ea, or ea-esk;"); + fprintf (stderr, "\n"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } } @@ -883,13 +1858,13 @@ void eot_cc(dsd_opts * opts, dsd_state * state) //jump back to CC here if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1) { - + //rigctl if (opts->use_rigctl == 1) { state->lasttg = 0; state->lastsrc = 0; - state->payload_algid = 0; + state->payload_algid = 0; state->payload_keyid = 0; state->payload_miP = 0; //reset some strings @@ -900,7 +1875,7 @@ void eot_cc(dsd_opts * opts, dsd_state * state) opts->p25_is_tuned = 0; state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0; if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); - SetFreq(opts->rigctl_sockfd, state->p25_cc_freq); + SetFreq(opts->rigctl_sockfd, state->p25_cc_freq); } //rtl @@ -926,4 +1901,4 @@ void eot_cc(dsd_opts * opts, dsd_state * state) } } - \ No newline at end of file + diff --git a/src/m17.c b/src/m17.c index f470cc0..89dd6a3 100644 --- a/src/m17.c +++ b/src/m17.c @@ -359,37 +359,46 @@ void M17processCodec2_1600(dsd_opts * opts, dsd_state * state, uint8_t * payload codec2_decode(state->codec2_1600, samp1, voice1); - if (opts->audio_out_type == 0 && state->m17_enc == 0) //Pulse Audio - { - pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL); - } + //hpf_d on codec2 sounds better than not on those .rrc samples + if (opts->use_hpf_d == 1) + hpf_dL(state, samp1, nsam); - if (opts->audio_out_type == 8 && state->m17_enc == 0) //UDP Audio + if (opts->slot1_on == 1) //playback if enabled { - udp_socket_blaster (opts, state, nsam*2, samp1); - } - if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS 48k/1 - { - //upsample to 48k and then play - prev = samp1[0]; - for (i = 0; i < 160; i++) + if (opts->audio_out_type == 0 && state->m17_enc == 0) //Pulse Audio { - upsampleS (samp1[i], prev, out); - for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j]; + pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL); } - write (opts->audio_out_fd, upsamp, nsam*2*6); - } + if (opts->audio_out_type == 8 && state->m17_enc == 0) //UDP Audio + { + udp_socket_blaster (opts, state, nsam*2, samp1); + } + + if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS 48k/1 + { + //upsample to 48k and then play + prev = samp1[0]; + for (i = 0; i < 160; i++) + { + upsampleS (samp1[i], prev, out); + for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j]; + } + write (opts->audio_out_fd, upsamp, nsam*2*6); - if (opts->audio_out_type == 1 && state->m17_enc == 0) //STDOUT - { - write (opts->audio_out_fd, samp1, nsam*2); - } + } + + if (opts->audio_out_type == 1 && state->m17_enc == 0) //STDOUT + { + write (opts->audio_out_fd, samp1, nsam*2); + } + + if (opts->audio_out_type == 2 && state->m17_enc == 0) //OSS 8k/1 + { + write (opts->audio_out_fd, samp1, nsam*2); + } - if (opts->audio_out_type == 2 && state->m17_enc == 0) //OSS 8k/1 - { - write (opts->audio_out_fd, samp1, nsam*2); } //WIP: Wav file saving -- still need a way to open/close/label wav files similar to call history @@ -459,47 +468,59 @@ void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t * payload codec2_decode(state->codec2_3200, samp1, voice1); codec2_decode(state->codec2_3200, samp2, voice2); - if (opts->audio_out_type == 0 && state->m17_enc == 0) //Pulse Audio + //hpf_d on codec2 sounds better than not on those .rrc samples + if (opts->use_hpf_d == 1) { - pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL); - pa_simple_write(opts->pulse_digi_dev_out, samp2, nsam*2, NULL); + hpf_dL(state, samp1, nsam); + hpf_dL(state, samp2, nsam); } - if (opts->audio_out_type == 8 && state->m17_enc == 0) //UDP Audio + if (opts->slot1_on == 1) //playback if enabled { - udp_socket_blaster (opts, state, nsam*2, samp1); - udp_socket_blaster (opts, state, nsam*2, samp2); - } - - if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS 48k/1 - { - //upsample to 48k and then play - prev = samp1[0]; - for (i = 0; i < 160; i++) + + if (opts->audio_out_type == 0 && state->m17_enc == 0) //Pulse Audio { - upsampleS (samp1[i], prev, out); - for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j]; + pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL); + pa_simple_write(opts->pulse_digi_dev_out, samp2, nsam*2, NULL); } - write (opts->audio_out_fd, upsamp, nsam*2*6); - prev = samp2[0]; - for (i = 0; i < 160; i++) + + if (opts->audio_out_type == 8 && state->m17_enc == 0) //UDP Audio { - upsampleS (samp2[i], prev, out); - for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j]; + udp_socket_blaster (opts, state, nsam*2, samp1); + udp_socket_blaster (opts, state, nsam*2, samp2); + } + + if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS 48k/1 + { + //upsample to 48k and then play + prev = samp1[0]; + for (i = 0; i < 160; i++) + { + upsampleS (samp1[i], prev, out); + for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j]; + } + write (opts->audio_out_fd, upsamp, nsam*2*6); + prev = samp2[0]; + for (i = 0; i < 160; i++) + { + upsampleS (samp2[i], prev, out); + for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j]; + } + write (opts->audio_out_fd, upsamp, nsam*2*6); } - write (opts->audio_out_fd, upsamp, nsam*2*6); - } - if (opts->audio_out_type == 1 && state->m17_enc == 0) //STDOUT - { - write (opts->audio_out_fd, samp1, nsam*2); - write (opts->audio_out_fd, samp2, nsam*2); - } + if (opts->audio_out_type == 1 && state->m17_enc == 0) //STDOUT + { + write (opts->audio_out_fd, samp1, nsam*2); + write (opts->audio_out_fd, samp2, nsam*2); + } + + if (opts->audio_out_type == 2 && state->m17_enc == 0) //OSS 8k/1 + { + write (opts->audio_out_fd, samp1, nsam*2); + write (opts->audio_out_fd, samp2, nsam*2); + } - if (opts->audio_out_type == 2 && state->m17_enc == 0) //OSS 8k/1 - { - write (opts->audio_out_fd, samp1, nsam*2); - write (opts->audio_out_fd, samp2, nsam*2); } //WIP: Wav file saving -- still need a way to open/close/label wav files similar to call history @@ -1840,18 +1861,51 @@ void encodeM17STR(dsd_opts * opts, dsd_state * state) #endif } - //read in RMS value for vox function; NOTE: may not work correctly on STDIN and TCP due to blocking when no samples to read + //read in RMS value for vox function; NOTE: will not work correctly SOCAT STDIO TCP due to blocking when no samples to read if (opts->audio_in_type == 3) opts->rtl_rms = rtl_return_rms(); else opts->rtl_rms = raw_rms(voice1, nsam, 1) / 2; //dividing by two so mic isn't so sensitive on vox - //decimate audio input (default 100% on mic is WAY TOO LOUD for the encoder, fine tune in volume control) - for (i = 0; i < 160; i++) + //low pass filter + if (opts->use_lpf == 1) { - //NOTE: Use + and - in ncurses to fine tune manually - voice1[i] *= (float)state->aout_gain/ (float)25.0f; - voice2[i] *= (float)state->aout_gain/ (float)25.0f; + lpf (state, voice1, 160); + lpf (state, voice2, 160); } + //high pass filter + if (opts->use_hpf == 1) + { + hpf (state, voice1, 160); + hpf (state, voice2, 160); + } + + //passband filter + if (opts->use_pbf == 1) + { + pbf (state, voice1, 160); + pbf (state, voice2, 160); + } + + //manual gain control + if (opts->audio_gainA > 0.0f) + { + analog_gain (opts, state, voice1, 160); + analog_gain (opts, state, voice2, 160); + } + + //automatic gain control + else + { + agsm (opts, state, voice1, 160); + agsm (opts, state, voice2, 160); + } + + //NOTE: Similar to EDACS analog, if calculating raw rms here after filtering, + //anytime the walkie-talkie is held open but no voice, the center spike is removed, + //and counts against the squelch hits making vox mode inconsistent + // if (opts->audio_in_type != 3) + // opts->rtl_rms = raw_rms(voice1, 160, 1); + //convert out audio input into CODEC2 (3200bps) 8 byte data stream uint8_t vc1_bytes[8]; memset (vc1_bytes, 0, sizeof(vc1_bytes)); uint8_t vc2_bytes[8]; memset (vc2_bytes, 0, sizeof(vc2_bytes)); diff --git a/src/nxdn_voice.c b/src/nxdn_voice.c index 927ff65..326151f 100644 --- a/src/nxdn_voice.c +++ b/src/nxdn_voice.c @@ -93,7 +93,7 @@ void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182 memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l)); if (opts->floating_point == 0 ) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1) playSynthesizedVoiceFM(opts, state); } diff --git a/src/p25p1_ldu1.c b/src/p25p1_ldu1.c index 7c784ed..2950a71 100644 --- a/src/p25p1_ldu1.c +++ b/src/p25p1_ldu1.c @@ -60,7 +60,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -74,7 +74,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -95,7 +95,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -116,7 +116,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -137,7 +137,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -158,7 +158,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -179,7 +179,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -200,7 +200,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -261,7 +261,7 @@ processLDU1 (dsd_opts* opts, dsd_state* state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) diff --git a/src/p25p1_ldu2.c b/src/p25p1_ldu2.c index dec7113..54cd32e 100644 --- a/src/p25p1_ldu2.c +++ b/src/p25p1_ldu2.c @@ -65,7 +65,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -79,7 +79,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -100,7 +100,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -121,7 +121,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -142,7 +142,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -163,7 +163,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -184,7 +184,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) playSynthesizedVoiceFM(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2) @@ -203,7 +203,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) @@ -264,7 +264,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state) #endif process_IMBE (opts, state, &status_count); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1) diff --git a/src/p25p1_mdpu.c b/src/p25p1_mdpu.c index 7d29860..f216d4b 100644 --- a/src/p25p1_mdpu.c +++ b/src/p25p1_mdpu.c @@ -77,6 +77,13 @@ static uint32_t crc32mbf(uint8_t * buf, int len) void processMPDU(dsd_opts * opts, dsd_state * state) { + //p25p2 18v reset counters and buffers + state->voice_counter[0] = 0; //reset + state->voice_counter[1] = 0; //reset + memset (state->s_l4, 0, sizeof(state->s_l4)); + memset (state->s_r4, 0, sizeof(state->s_r4)); + opts->slot_preference = 2; + //reset some strings when returning from a call in case they didn't get zipped already sprintf (state->call_string[0], "%s", " "); //21 spaces sprintf (state->call_string[1], "%s", " "); //21 spaces diff --git a/src/p25p1_tsbk.c b/src/p25p1_tsbk.c index 8ca5ce0..234e7db 100644 --- a/src/p25p1_tsbk.c +++ b/src/p25p1_tsbk.c @@ -11,6 +11,14 @@ void processTSBK(dsd_opts * opts, dsd_state * state) { + //p25p2 18v reset counters and buffers + state->voice_counter[0] = 0; //reset + state->voice_counter[1] = 0; //reset + memset (state->s_l4, 0, sizeof(state->s_l4)); + memset (state->s_r4, 0, sizeof(state->s_r4)); + opts->slot_preference = 2; + + //reset some strings when returning from a call in case they didn't get zipped already sprintf (state->call_string[0], "%s", " "); //21 spaces sprintf (state->call_string[1], "%s", " "); //21 spaces diff --git a/src/p25p2_frame.c b/src/p25p2_frame.c index f7cc0f4..a01db06 100644 --- a/src/p25p2_frame.c +++ b/src/p25p2_frame.c @@ -490,18 +490,27 @@ void process_4V (dsd_opts * opts, dsd_state * state) // else state->dmrburstR = 21; // } // #endif + + //unsure of the best location for these counter resets + if (state->voice_counter[0] >= 18) + state->voice_counter[0] = 0; + + if (state->voice_counter[1] >= 18) + state->voice_counter[1] = 0; processMbeFrame (opts, state, NULL, ambe_fr1, NULL); if(state->currentslot == 0) { memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); - memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l)); + // memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l)); + memcpy(state->s_l4[(state->voice_counter[0]++)%18], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu)); } else { memcpy(state->f_r4[0], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR)); - memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r)); + // memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r)); + memcpy(state->s_r4[(state->voice_counter[1]++)%18], state->s_r, sizeof(state->s_r)); memcpy(state->s_r4u[0], state->s_ru, sizeof(state->s_ru)); } @@ -509,13 +518,15 @@ void process_4V (dsd_opts * opts, dsd_state * state) if(state->currentslot == 0) { memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); - memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l)); + // memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l)); + memcpy(state->s_l4[(state->voice_counter[0]++)%18], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu)); } else { memcpy(state->f_r4[1], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR)); - memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r)); + // memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r)); + memcpy(state->s_r4[(state->voice_counter[1]++)%18], state->s_r, sizeof(state->s_r)); memcpy(state->s_r4u[1], state->s_ru, sizeof(state->s_ru)); } @@ -523,13 +534,15 @@ void process_4V (dsd_opts * opts, dsd_state * state) if(state->currentslot == 0) { memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); - memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l)); + // memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l)); + memcpy(state->s_l4[(state->voice_counter[0]++)%18], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu)); } else { memcpy(state->f_r4[2], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR)); - memcpy(state->s_r4[2], state->s_r, sizeof(state->s_r)); + // memcpy(state->s_r4[2], state->s_r, sizeof(state->s_r)); + memcpy(state->s_r4[(state->voice_counter[1]++)%18], state->s_r, sizeof(state->s_r)); memcpy(state->s_r4u[2], state->s_ru, sizeof(state->s_ru)); } @@ -537,13 +550,15 @@ void process_4V (dsd_opts * opts, dsd_state * state) if(state->currentslot == 0) { memcpy(state->f_l4[3], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); - memcpy(state->s_l4[3], state->s_l, sizeof(state->s_l)); + // memcpy(state->s_l4[3], state->s_l, sizeof(state->s_l)); + memcpy(state->s_l4[(state->voice_counter[0]++)%18], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4u[3], state->s_lu, sizeof(state->s_lu)); } else { memcpy(state->f_r4[3], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR)); - memcpy(state->s_r4[3], state->s_r, sizeof(state->s_r)); + // memcpy(state->s_r4[3], state->s_r, sizeof(state->s_r)); + memcpy(state->s_r4[(state->voice_counter[1]++)%18], state->s_r, sizeof(state->s_r)); memcpy(state->s_r4u[3], state->s_ru, sizeof(state->s_ru)); } @@ -773,17 +788,26 @@ void process_2V (dsd_opts * opts, dsd_state * state) // } // #endif + //unsure of the best location for these counter resets + if (state->voice_counter[0] >= 18) + state->voice_counter[0] = 0; + + if (state->voice_counter[1] >= 18) + state->voice_counter[1] = 0; + processMbeFrame (opts, state, NULL, ambe_fr1, NULL); if(state->currentslot == 0) { memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); - memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l)); + // memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l)); + memcpy(state->s_l4[(state->voice_counter[0]++)%18], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu)); } else { memcpy(state->f_r4[0], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR)); - memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r)); + // memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r)); + memcpy(state->s_r4[(state->voice_counter[1]++)%18], state->s_r, sizeof(state->s_r)); memcpy(state->s_r4u[0], state->s_ru, sizeof(state->s_ru)); } @@ -791,17 +815,19 @@ void process_2V (dsd_opts * opts, dsd_state * state) if(state->currentslot == 0) { memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); - memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l)); + // memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l)); + memcpy(state->s_l4[(state->voice_counter[0]++)%18], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu)); } else { memcpy(state->f_r4[1], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR)); - memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r)); + // memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r)); + memcpy(state->s_r4[(state->voice_counter[1]++)%18], state->s_r, sizeof(state->s_r)); memcpy(state->s_r4u[1], state->s_ru, sizeof(state->s_ru)); } - + // if (state->currentslot == 0) state->voice_counter[0] = 0; if (state->currentslot == 1) state->voice_counter[1] = 0; process_ESS(opts, state); //reset drop bytes after a 2V @@ -908,10 +934,20 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state) opts->p25_is_tuned = 0; state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0; memset (state->active_channel, 0, sizeof (state->active_channel)); //zero out here? I think this will be fine + //clear out stale voice samples left in the buffer and reset counter value + state->voice_counter[0] = 0; + state->voice_counter[1] = 0; + memset(state->s_l4, 0, sizeof(state->s_l4)); + memset(state->s_r4, 0, sizeof(state->s_r4)); } else if (duid_decoded == 13 && ((time(NULL) - state->last_active_time) > 2) && opts->p25_is_tuned == 0) //should we use && opts->p25_is_tuned == 1? { memset (state->active_channel, 0, sizeof (state->active_channel)); //zero out here? I think this will be fine + //clear out stale voice samples left in the buffer and reset counter value + state->voice_counter[0] = 0; + state->voice_counter[1] = 0; + memset(state->s_l4, 0, sizeof(state->s_l4)); + memset(state->s_r4, 0, sizeof(state->s_r4)); } if (duid_decoded == 0) @@ -998,6 +1034,8 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state) state->p2_is_lcch = 0; state->fourv_counter[0] = 0; state->fourv_counter[1] = 0; + state->voice_counter[0] = 0; + state->voice_counter[1] = 0; goto END; } @@ -1017,8 +1055,22 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state) if (sacch == 0 && ts_counter & 1 && opts->floating_point == 1 && opts->pulse_digi_rate_out == 8000) playSynthesizedVoiceFS4 (opts, state); - if (sacch == 0 && ts_counter & 1 && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000) - playSynthesizedVoiceSS4 (opts, state); + // if (sacch == 0 && ts_counter & 1 && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000) + // playSynthesizedVoiceSS4 (opts, state); + + // fprintf (stderr, " VCH0: %d;", state->voice_counter[0]); //debug + // fprintf (stderr, " VCH1: %d;", state->voice_counter[1]); //debug + + //this works, but may still have an element of 'dual voice stutter' which was my initial complaint, but shouldn't 'lag' during trunking operations (hopefully) + if ( (state->voice_counter[0] >= 18 || state->voice_counter[1] >= 18 ) && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000 && ts_counter & 1) + { + //debug test, see what each counter is at during playback on dual voice + // fprintf (stderr, " VC1: %02d; VC2: %02d;", state->voice_counter[0], state->voice_counter[1] ); + + playSynthesizedVoiceSS18 (opts, state); + state->voice_counter[0] = 0; //reset + state->voice_counter[1] = 0; //reset + } //debug: fix burst indicator for ncurses if marginal signal // if (voice) diff --git a/src/p25p2_vpdu.c b/src/p25p2_vpdu.c index b7922cf..80f595f 100644 --- a/src/p25p2_vpdu.c +++ b/src/p25p2_vpdu.c @@ -154,16 +154,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (channel & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (channel & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -254,16 +254,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (channel & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (channel & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -390,16 +390,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (tunable_chan & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (tunable_chan & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -515,16 +515,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (channel & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (channel & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -646,16 +646,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (channel & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (channel & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -752,16 +752,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (channel & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (channel & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -922,16 +922,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (tunable_chan & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (tunable_chan & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -1124,16 +1124,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (tunable_chan & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (tunable_chan & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -1265,16 +1265,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (tunable_chan & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (tunable_chan & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } } @@ -1394,16 +1394,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon //shim fix to stutter/lag by only enabling slot on the target/channel we tuned to //this will only occur in realtime tuning, not not required .bin or .wav playback - if (channelt & 1) //VCH1 - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else //VCH0 - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } + // if (channelt & 1) //VCH1 + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else //VCH0 + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } } diff --git a/src/p25p2_xcch.c b/src/p25p2_xcch.c index 8c72f79..efc23fd 100644 --- a/src/p25p2_xcch.c +++ b/src/p25p2_xcch.c @@ -122,6 +122,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180] { //reset fourv_counter and dropbyte on PTT state->fourv_counter[0] = 0; + state->voice_counter[0] = 0; state->dropL = 256; state->dmrburstL = 20; @@ -171,6 +172,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180] { //reset fourv_counter and dropbyte on PTT state->fourv_counter[1] = 0; + state->voice_counter[1] = 0; state->dropR = 256; state->payload_algidR = 0; //zero this out as well @@ -230,6 +232,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180] { state->fourv_counter[0] = 0; + state->voice_counter[0] = 0; state->dropL = 256; state->dmrburstL = 23; state->payload_algid = 0; @@ -258,6 +261,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180] { state->fourv_counter[1] = 0; + state->voice_counter[1] = 0; state->dropR = 256; state->dmrburstR = 23; state->payload_algidR = 0; @@ -370,19 +374,19 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180] process_MAC_VPDU(opts, state, 1, SMAC); fprintf (stderr, "%s", KNRM); - if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1) - { - if (state->currentslot == 1) - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } - } + // if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1) + // { + // if (state->currentslot == 1) + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } + // } //blank the call string here -- slot variable is already flipped accordingly for sacch sprintf (state->call_string[slot], "%s", " "); //21 spaces @@ -512,6 +516,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] { //reset fourv_counter and dropbyte on PTT state->fourv_counter[0] = 0; + state->voice_counter[0] = 0; state->dropL = 256; state->dmrburstL = 20; @@ -552,6 +557,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] { //reset fourv_counter and dropbyte on PTT state->fourv_counter[1] = 0; + state->voice_counter[1] = 0; state->dropR = 256; state->dmrburstR = 20; @@ -608,6 +614,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] { state->fourv_counter[0] = 0; + state->voice_counter[0] = 0; state->dropL = 256; state->dmrburstL = 23; state->payload_algid = 0; //zero this out as well @@ -636,6 +643,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] { state->fourv_counter[1] = 0; + state->voice_counter[1] = 0; state->dropR = 256; state->dmrburstR = 23; state->payload_algidR = 0; //zero this out as well @@ -737,6 +745,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] state->payload_keyid = 0; state->dmrburstL = 24; state->fourv_counter[0] = 0; + state->voice_counter[0] = 0; state->lastsrc = 0; state->lasttg = 0; @@ -747,6 +756,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] state->payload_keyidR = 0; state->dmrburstR = 24; state->fourv_counter[1] = 0; + state->voice_counter[1] = 0; state->lastsrcR = 0; state->lasttgR = 0; @@ -756,19 +766,19 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] process_MAC_VPDU(opts, state, 0, FMAC); fprintf (stderr, "%s", KNRM); - if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1) - { - if (state->currentslot == 0) - { - opts->slot1_on = 0; - opts->slot2_on = 1; - } - else - { - opts->slot1_on = 1; - opts->slot2_on = 0; - } - } + // if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1) + // { + // if (state->currentslot == 0) + // { + // opts->slot1_on = 0; + // opts->slot2_on = 1; + // } + // else + // { + // opts->slot1_on = 1; + // opts->slot2_on = 0; + // } + // } //blank the call string here sprintf (state->call_string[slot], "%s", " "); //21 spaces diff --git a/src/provoice.c b/src/provoice.c index b24f4e6..9809e91 100644 --- a/src/provoice.c +++ b/src/provoice.c @@ -27,12 +27,19 @@ void processProVoice (dsd_opts * opts, dsd_state * state) fprintf (stderr," VOICE"); - //print group and source values if EA trunked + //print group/target and source values if EA trunked if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->ea_mode == 1) { fprintf (stderr, "%s", KGRN); - fprintf (stderr, " Site: %lld Group: %d Source: %d LCN: %d ", - state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn); + if (state->lasttg > 100000) { + // I-Call + fprintf (stderr, " Site: %lld Target: %d Source: %d LCN: %d ", + state->edacs_site_id, state->lasttg - 100000, state->lastsrc, state->edacs_tuned_lcn); + } else { + // Group call + fprintf (stderr, " Site: %lld Group: %d Source: %d LCN: %d ", + state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn); + } fprintf (stderr, "%s", KNRM); } //print afs value if standard/networked trunked @@ -214,12 +221,12 @@ void processProVoice (dsd_opts * opts, dsd_state * state) processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1); if (opts->floating_point == 0) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1) playSynthesizedVoiceFM(opts, state); processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2); if (opts->floating_point == 0) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1) playSynthesizedVoiceFM(opts, state); @@ -392,12 +399,12 @@ void processProVoice (dsd_opts * opts, dsd_state * state) processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1); if (opts->floating_point == 0) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1) playSynthesizedVoiceFM(opts, state); processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2); if (opts->floating_point == 0) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if (opts->floating_point == 1) playSynthesizedVoiceFM(opts, state); diff --git a/src/rtl_sdr_fm.cpp b/src/rtl_sdr_fm.cpp index e571503..2762d47 100644 --- a/src/rtl_sdr_fm.cpp +++ b/src/rtl_sdr_fm.cpp @@ -815,6 +815,34 @@ void dongle_init(struct dongle_state *s) s->demod_target = &demod; } +void demod_init_analog(struct demod_state *s) +{ + s->rate_in = rtl_bandwidth; + s->rate_out = rtl_bandwidth; + s->squelch_level = 0; + s->conseq_squelch = 10; + s->terminate_on_squelch = 0; + s->squelch_hits = 11; + s->downsample_passes = 1; // + s->comp_fir_size = 0; + s->prev_index = 0; + s->post_downsample = 1; //1 -- once this works, default = 4 -- doesn't work on the official rtl-sdr source code either + s->custom_atan = 0; + s->deemph = 1; // + s->rate_out2 = rtl_bandwidth; // -1 flag for disabled -- this enables low_pass_real, seems to work okay + s->mode_demod = &fm_demod; + s->pre_j = s->pre_r = s->now_r = s->now_j = 0; + s->prev_lpr_index = 0; + s->deemph_a = 0; // + s->now_lpr = 0; + s->dc_block = 1; // + s->dc_avg = 0; + pthread_rwlock_init(&s->rw, NULL); + pthread_cond_init(&s->ready, NULL); + pthread_mutex_init(&s->ready_m, NULL); + s->output_target = &output; +} + void demod_init_ro2(struct demod_state *s) { s->rate_in = rtl_bandwidth; @@ -1005,6 +1033,8 @@ void open_rtlsdr_stream(dsd_opts *opts) //init with low pass if decoding P25 or EDACS/Provoice if (opts->frame_p25p1 == 1 || opts->frame_p25p2 == 1 || opts->frame_provoice == 1) demod_init_ro2(&demod); + else if (opts->analog_only == 1 || opts->m17encoder == 1) + demod_init_analog(&demod); else demod_init(&demod); output_init(&output); controller_init(&controller); diff --git a/src/ysf.c b/src/ysf.c index a94bbd3..8ed1910 100644 --- a/src/ysf.c +++ b/src/ysf.c @@ -737,7 +737,7 @@ void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t dbuf[180], int start, writeSynthesizedVoice (opts, state); if (opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); @@ -1027,7 +1027,7 @@ void processYSF(dsd_opts * opts, dsd_state * state) if (opts->wav_out_f != NULL) writeSynthesizedVoice (opts, state); if (opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); @@ -1147,7 +1147,7 @@ void processYSF(dsd_opts * opts, dsd_state * state) writeSynthesizedVoice (opts, state); if (opts->pulse_digi_out_channels == 1) - playSynthesizedVoice(opts, state); + playSynthesizedVoiceMS(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state);