#include "../qo100trx.h" #include void createSSBfilter(); void init_beaconlock(); void close_beaconlock(); void exec_beaconlock(liquid_float_complex samp); float rxvolume = 1.0f; // down mixer nco_crcf dnnco = NULL; // SSB Demodulator float demod_index = 0.99f; // modulation index (bandwidth) ampmodem demod = NULL; // Low pass unsigned int lp_order = 4; // filter order float lp_fc = 0.0050f; // cutoff frequency float lp_f0 = 0.0f; // center frequency float lp_Ap = 1.0f; // pass-band ripple float lp_As = 40.0f; // stop-band attenuation iirfilt_crcf lp_q = NULL; // Down-Sampler unsigned int decim_h_len = 13; // filter semi-length (filter delay) float decim_r = (float)((double)AUDIOSAMPRATE / (double)SAMPRATE); // resampling rate (output/input) float decim_bw=0.001f; // cutoff frequency float decim_slsl= 60.0f; // resampling filter sidelobe suppression level unsigned int decim_npfb=32; // number of filters in bank (timing resolution) resamp_crcf decim_q = NULL; void init_liquid() { printf("init DSP\n"); // Downmixer dnnco = nco_crcf_create(LIQUID_NCO); tune_downmixer(); // SSB Demodulator demod = ampmodem_create(demod_index, LIQUID_AMPMODEM_USB, 1); // create downsampler decim_q = resamp_crcf_create(decim_r,decim_h_len,decim_bw,decim_slsl,decim_npfb); if(decim_q == NULL) printf("decimq error\n"); // low pass createSSBfilter(); init_beaconlock(); } void close_liquid() { printf("close DSP\n"); if(dnnco) nco_crcf_destroy(dnnco); dnnco = NULL; if(demod) ampmodem_destroy(demod); demod = NULL; if(decim_q) resamp_crcf_destroy(decim_q); decim_q = NULL; if(lp_q) iirfilt_crcf_destroy(lp_q); lp_q = NULL; close_beaconlock(); } void createSSBfilter() { static int lastrxfilter = -1; if(rxfilter != lastrxfilter) { printf("create RX SSB filter: %d\n",rxfilter); lastrxfilter = rxfilter; if(lp_q) iirfilt_crcf_destroy(lp_q); switch(rxfilter) { case 0: lp_fc = 0.002f; break; case 1: lp_fc = 0.0036f; break; case 2: lp_fc = 0.0044f; break; case 3: lp_fc = 0.0050f; break; } lp_q = iirfilt_crcf_create_prototype(LIQUID_IIRDES_ELLIP, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, lp_order, lp_fc, lp_f0, lp_Ap, lp_As); } } void tune_downmixer() { static int lastoffset = -1; int newoffset = RXoffsetfreq; if(beaconlock) newoffset += bcnoffset; if(lastoffset != newoffset) { lastoffset = newoffset; printf("tune RX to %f\n",BASEQRG*1e3 + lastoffset); float RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (lastoffset-280000))/(float)SAMPRATE); nco_crcf_set_phase(dnnco, 0.0f); nco_crcf_set_frequency(dnnco, RADIANS_PER_SAMPLE); } } // convert a pluto stream into liquid samples void streamToSamples(uint8_t *stream, int streamlen, liquid_float_complex *samples) { int didx = 0; int16_t xi; int16_t xq; for(int i=0; i 1) { printf("decimator error, num_written=%d\n",num_written); exit(0); } //measure_maxval(fabs(soundsamp.real), 1000); // the rate here is 48000S/s // demodulate ampmodem_demodulate(demod, soundsamp, &z); // send z to soundcard if(audioloop == 0 && pbidx != -1) { static float playvol = 1.0f; if(((ptt && rxmute) || mute) && rfloop == 0) playvol = 0.08f; else playvol = 2.0f; float vol = playvol * rxvolume; if(fabs(vol) > 1.0f) { // reduce volume if overdriven rxvolume = 1.0f / playvol; vol = playvol * rxvolume; } kmaudio_playsamples(pbidx,&z,1,vol); } } } /* ================= beaconlock =================== the beacon lock measures the frequency of the lower CW beacon 1) Frequency tuner is on x,470000 beacon is on x,500000 - x,500400 take 2kHz from x,499200 to x,501200 Downmix x,499200 to baseband 2) Low pass filter with < 2kHz to eliminate aliasing 3) Sample Rate: Downsampling by 480 input: 1,12MS/s output: 4000S/s which gives the required 2kHz range 4) run an FFT with a resolution of bcn_resolution. this give new FFT bins every 1/bcn_resolution seconds */ // FFT fftw_complex *bcn_din = NULL; // input data for fft, output data from ifft fftw_complex *bcn_cpout = NULL; // ouput data from fft, input data to ifft fftw_plan bcn_plan = NULL; const int bcn_FFTsamprate = 4000; // sample rate for FFT (required range * 2) const int bcn_resolution = 2; // Hz per bin const int bcn_fftlength = bcn_FFTsamprate / bcn_resolution; // 4kS/s / 5 = 800 // down mixer nco_crcf bcn_dnnco = NULL; const int startqrg = 499200; // Low pass unsigned int bcn_lp_order = 4; // filter order float bcn_lp_fc = 0.0018f; // cutoff frequency float bcn_lp_f0 = 0.0f; // center frequency float bcn_lp_Ap = 1.0f; // pass-band ripple float bcn_lp_As = 40.0f; // stop-band attenuation iirfilt_crcf bcn_lp_q = NULL; // decimator unsigned int bcn_m_predec = 8; // filter delay float bcn_As_predec = 40.0f; // stop-band att const int bcnInterpolfactor = SAMPRATE / bcn_FFTsamprate; firdecim_crcf bcn_decim = NULL; int bcnoffset = -1; void init_beaconlock() { // Downmixer bcn_dnnco = nco_crcf_create(LIQUID_NCO); int offset = startqrg - 470000; float RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (offset-280000))/(float)SAMPRATE); nco_crcf_set_phase(bcn_dnnco, 0.0f); nco_crcf_set_frequency(bcn_dnnco, RADIANS_PER_SAMPLE); // Low pass filter bcn_lp_q = iirfilt_crcf_create_prototype(LIQUID_IIRDES_ELLIP, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, bcn_lp_order, bcn_lp_fc, bcn_lp_f0, bcn_lp_Ap, bcn_lp_As); // decimator bcn_decim = firdecim_crcf_create_kaiser(bcnInterpolfactor, bcn_m_predec, bcn_As_predec); firdecim_crcf_set_scale(bcn_decim, 1.0f/(float)bcnInterpolfactor); // FFT fftw_import_wisdom_from_filename("bcn_fftcfg"); bcn_din = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * bcn_fftlength); bcn_cpout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * bcn_fftlength); bcn_plan = fftw_plan_dft_1d(bcn_fftlength, bcn_din, bcn_cpout, FFTW_FORWARD, FFTW_MEASURE); fftw_export_wisdom_to_filename("bcn_fftcfg"); } void close_beaconlock() { if(bcn_dnnco) nco_crcf_destroy(bcn_dnnco); bcn_dnnco = NULL; if(bcn_lp_q) iirfilt_crcf_destroy(bcn_lp_q); bcn_lp_q = NULL; if(bcn_decim != NULL) firdecim_crcf_destroy(bcn_decim); bcn_decim = NULL; if(bcn_din) fftw_free(bcn_din); bcn_din = NULL; if(bcn_cpout) fftw_free(bcn_cpout); bcn_cpout = NULL; } void exec_beaconlock(liquid_float_complex samp) { static liquid_float_complex ccol[bcnInterpolfactor]; static int ccol_idx = 0; static int bcn_din_idx = 0; if(!beaconlock) return; if (bcn_dnnco == NULL) return; if (bcn_lp_q == NULL) return; if (bcn_decim == NULL) return; // mix lower beacon into baseband liquid_float_complex c; nco_crcf_step(bcn_dnnco); nco_crcf_mix_down(bcn_dnnco,samp,&c); // Filter liquid_float_complex cfilt; iirfilt_crcf_execute(bcn_lp_q, c, &cfilt); // down sampling 1,2MS/s -> 4kS/s ccol[ccol_idx++] = cfilt; if (ccol_idx < bcnInterpolfactor) return; ccol_idx = 0; // we have bcnInterpolfactor samples in ccol liquid_float_complex y; firdecim_crcf_execute(bcn_decim, ccol, &y); // the output of the pre decimator is exactly one sample in y // the rate here is 4kS/s // FFT // collect samples until we have bcn_fftlength bcn_din[bcn_din_idx][0] = y.real; bcn_din[bcn_din_idx][1] = y.imag; if(++bcn_din_idx < bcn_fftlength) return; bcn_din_idx = 0; fftw_execute(bcn_plan); int numbins = bcn_fftlength/2; // calc absolute values and search max value float bin[numbins]; float real,imag; float max = 0; for(int i=0; imax) max=bin[i]; } // from all samples > 1/2 max search the min and max frequency int minf=99999, maxf=0; for(int i=0; i (max*1/2)) { if(i < minf) minf = i; if(i > maxf) maxf = i; } } int minqrg = minf* bcn_resolution+startqrg; int maxqrg = maxf* bcn_resolution+startqrg; int diff = abs(maxqrg-minqrg); //printf("%d %d %d\n",minqrg,maxqrg,diff); int newoffset = 0; if(diff > 380) { // we have both frequencies, then measure the beacon mir frequency int bcnqrg = minqrg + diff/2; int bcnqrgsoll = 500200; // expected frequency bcnoffset = (bcnqrg - bcnqrgsoll); //printf("lower beacon %d .. %d: mid QRG: %d kHz. Offset: %d Hz\n",minqrg,maxqrg,bcnqrg,bcnoffset); newoffset = 1; } else { // we have only one frequency return; int difflow = minqrg - 500000; int diffhigh = maxqrg - 500400; if(abs(difflow) < abs(diffhigh)) { //printf("lower beacon low QRG: %d kHz. Offset: %d Hz\n",minqrg,difflow); bcnoffset = difflow; newoffset = 1; } else { //printf("lower beacon hi QRG: %d kHz. Offset: %d Hz\n",maxqrg,diffhigh); bcnoffset = diffhigh; newoffset = 1; } } // send offset to GUI if(newoffset) { uint8_t drift[5]; drift[0] = 6; drift[1] = bcnoffset >> 24; drift[2] = bcnoffset >> 16; drift[3] = bcnoffset >> 8; drift[4] = bcnoffset & 0xff; sendUDP(gui_ip, GUI_UDPPORT, drift, 5); tune_downmixer(); } }