From 2a5671878f222849dab1dc123fd667c3bd8bc684 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 8 Dec 2021 02:10:41 +0100 Subject: [PATCH] new FM IF noise reduction + bugfix --- core/src/dsp/noise_reduction.h | 136 ++++++++++++++++++- decoder_modules/radio/src/demod.h | 1 + decoder_modules/radio/src/demodulators/am.h | 1 + decoder_modules/radio/src/demodulators/cw.h | 1 + decoder_modules/radio/src/demodulators/dsb.h | 1 + decoder_modules/radio/src/demodulators/lsb.h | 1 + decoder_modules/radio/src/demodulators/nfm.h | 1 + decoder_modules/radio/src/demodulators/raw.h | 1 + decoder_modules/radio/src/demodulators/usb.h | 1 + decoder_modules/radio/src/demodulators/wfm.h | 1 + decoder_modules/radio/src/radio_module.h | 35 ++++- make_macos_bundle.sh | 1 - make_windows_package.ps1 | 1 - 13 files changed, 177 insertions(+), 5 deletions(-) diff --git a/core/src/dsp/noise_reduction.h b/core/src/dsp/noise_reduction.h index b1eb8021..6cf1b94d 100644 --- a/core/src/dsp/noise_reduction.h +++ b/core/src/dsp/noise_reduction.h @@ -3,9 +3,143 @@ #include #include -#define NR_TAP_COUNT 4096 +#define NR_TAP_COUNT 64 namespace dsp { + class FMIFNoiseReduction : public generic_block { + public: + FMIFNoiseReduction() {} + + FMIFNoiseReduction(stream* in, int tapCount) { init(in, tapCount); } + + ~FMIFNoiseReduction() { + if (!generic_block::_block_init) { return; } + generic_block::stop(); + fftwf_destroy_plan(forwardPlan); + fftwf_destroy_plan(backwardPlan); + fftwf_free(delay); + fftwf_free(fft_in); + fftwf_free(fft_window); + fftwf_free(amp_buf); + fftwf_free(fft_cout); + fftwf_free(fft_fcout); + } + + void init(stream* in, int tapCount) { + _in = in; + _tapCount = tapCount; + + delay = (complex_t*)fftwf_malloc(sizeof(complex_t)*STREAM_BUFFER_SIZE); + fft_in = (complex_t*)fftwf_malloc(sizeof(complex_t)*_tapCount); + fft_window = (float*)fftwf_malloc(sizeof(float)*_tapCount); + amp_buf = (float*)fftwf_malloc(sizeof(float)*_tapCount); + fft_cout = (complex_t*)fftwf_malloc(sizeof(complex_t)*_tapCount); + fft_fcout = (complex_t*)fftwf_malloc(sizeof(complex_t)*_tapCount); + delay_start = &delay[_tapCount]; + + memset(delay, 0, sizeof(complex_t)*STREAM_BUFFER_SIZE); + memset(fft_in, 0, sizeof(complex_t)*_tapCount); + memset(amp_buf, 0, sizeof(float)*_tapCount); + memset(fft_cout, 0, sizeof(complex_t)*_tapCount); + memset(fft_fcout, 0, sizeof(complex_t)*_tapCount); + + for (int i = 0; i < _tapCount; i++) { + fft_window[i] = window_function::blackman(i, _tapCount - 1); + } + + forwardPlan = fftwf_plan_dft_1d(_tapCount, (fftwf_complex*)fft_in, (fftwf_complex*)fft_cout, FFTW_FORWARD, FFTW_ESTIMATE); + backwardPlan = fftwf_plan_dft_1d(_tapCount, (fftwf_complex*)fft_cout, (fftwf_complex*)fft_fcout, FFTW_BACKWARD, FFTW_ESTIMATE); + + generic_block::registerInput(_in); + generic_block::registerOutput(&out); + generic_block::_block_init = true; + } + + void setInput(stream* in) { + assert(generic_block::_block_init); + std::lock_guard lck(generic_block::ctrlMtx); + generic_block::tempStop(); + generic_block::unregisterInput(_in); + _in = in; + generic_block::registerInput(_in); + generic_block::tempStart(); + } + + void setLevel(float level) { + _level = powf(10.0f, level / 10.0f); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + // Bypass + if (!bypass) { + memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t)); + _in->flush(); + if (!out.swap(count)) { return -1; } + return count; + } + + // Write to delay buffer + memcpy(delay_start, _in->readBuf, count * sizeof(complex_t)); + + uint32_t idx = 0; + float actLevel = 0; + + // Iterate the FFT + for (int i = 0; i < count; i++) { + // Apply windows + volk_32fc_32f_multiply_32fc((lv_32fc_t*)fft_in, (lv_32fc_t*)&delay[i], fft_window, _tapCount); + + // Do forward FFT + fftwf_execute(forwardPlan); + + // Process bins here + volk_32fc_magnitude_32f(amp_buf, (lv_32fc_t*)fft_cout, _tapCount); + volk_32f_index_max_32u(&idx, amp_buf, _tapCount); + + for (int j = 0; j < _tapCount; j++) { + if (j == idx) { continue; } + fft_cout[j] = {0, 0}; + } + + // Do reverse FFT and get first element + fftwf_execute(backwardPlan); + out.writeBuf[i] = fft_fcout[_tapCount/2]; + } + + volk_32f_s32f_multiply_32f((float*)out.writeBuf, (float*)out.writeBuf, 1.0f/(float)_tapCount, count * 2); + + // Copy last values to delay + memmove(delay, &delay[count], _tapCount * sizeof(complex_t)); + + _in->flush(); + if (!out.swap(count)) { return -1; } + return count; + } + + bool bypass = true; + stream out; + + float _level = 0.0f; + + private: + stream* _in; + fftwf_plan forwardPlan; + fftwf_plan backwardPlan; + complex_t* delay; + complex_t* fft_in; + float* fft_window; + float* amp_buf; + complex_t* delay_start; + complex_t* fft_cout; + complex_t* fft_fcout; + + int _tapCount; + + }; + class FFTNoiseReduction : public generic_block { public: FFTNoiseReduction() {} diff --git a/decoder_modules/radio/src/demod.h b/decoder_modules/radio/src/demod.h index 93d888b5..5f807de9 100644 --- a/decoder_modules/radio/src/demod.h +++ b/decoder_modules/radio/src/demod.h @@ -37,6 +37,7 @@ namespace demod { virtual bool getPostProcEnabled() = 0; virtual int getDefaultDeemphasisMode() = 0; virtual double getAFBandwidth(double bandwidth) = 0; + virtual bool getFMIFNRAllowed() = 0; virtual bool getDynamicAFBandwidth() = 0; virtual dsp::stream* getOutput() = 0; diff --git a/decoder_modules/radio/src/demodulators/am.h b/decoder_modules/radio/src/demodulators/am.h index 3d3f6855..5ef11cd0 100644 --- a/decoder_modules/radio/src/demodulators/am.h +++ b/decoder_modules/radio/src/demodulators/am.h @@ -67,6 +67,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } bool getDynamicAFBandwidth() { return true; } + bool getFMIFNRAllowed() { return false; } dsp::stream* getOutput() { return &m2s.out; } private: diff --git a/decoder_modules/radio/src/demodulators/cw.h b/decoder_modules/radio/src/demodulators/cw.h index de5efef5..5b8e3dc6 100644 --- a/decoder_modules/radio/src/demodulators/cw.h +++ b/decoder_modules/radio/src/demodulators/cw.h @@ -68,6 +68,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + 1000.0; } bool getDynamicAFBandwidth() { return true; } + bool getFMIFNRAllowed() { return false; } dsp::stream* getOutput() { return &m2s.out; } private: diff --git a/decoder_modules/radio/src/demodulators/dsb.h b/decoder_modules/radio/src/demodulators/dsb.h index 323ab293..08d0c03d 100644 --- a/decoder_modules/radio/src/demodulators/dsb.h +++ b/decoder_modules/radio/src/demodulators/dsb.h @@ -69,6 +69,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } bool getDynamicAFBandwidth() { return true; } + bool getFMIFNRAllowed() { return false; } dsp::stream* getOutput() { return &m2s.out; } private: diff --git a/decoder_modules/radio/src/demodulators/lsb.h b/decoder_modules/radio/src/demodulators/lsb.h index ac1eaf22..0b47b69c 100644 --- a/decoder_modules/radio/src/demodulators/lsb.h +++ b/decoder_modules/radio/src/demodulators/lsb.h @@ -69,6 +69,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth; } bool getDynamicAFBandwidth() { return true; } + bool getFMIFNRAllowed() { return false; } dsp::stream* getOutput() { return &m2s.out; } private: diff --git a/decoder_modules/radio/src/demodulators/nfm.h b/decoder_modules/radio/src/demodulators/nfm.h index e647b480..7c988f2b 100644 --- a/decoder_modules/radio/src/demodulators/nfm.h +++ b/decoder_modules/radio/src/demodulators/nfm.h @@ -61,6 +61,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } bool getDynamicAFBandwidth() { return true; } + bool getFMIFNRAllowed() { return true; } dsp::stream* getOutput() { return &demod.out; } private: diff --git a/decoder_modules/radio/src/demodulators/raw.h b/decoder_modules/radio/src/demodulators/raw.h index 8f889088..19d030ac 100644 --- a/decoder_modules/radio/src/demodulators/raw.h +++ b/decoder_modules/radio/src/demodulators/raw.h @@ -62,6 +62,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth; } bool getDynamicAFBandwidth() { return false; } + bool getFMIFNRAllowed() { return false; } dsp::stream* getOutput() { return &c2s.out; } private: diff --git a/decoder_modules/radio/src/demodulators/usb.h b/decoder_modules/radio/src/demodulators/usb.h index eeef9455..5686a08d 100644 --- a/decoder_modules/radio/src/demodulators/usb.h +++ b/decoder_modules/radio/src/demodulators/usb.h @@ -69,6 +69,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth; } bool getDynamicAFBandwidth() { return true; } + bool getFMIFNRAllowed() { return false; } dsp::stream* getOutput() { return &m2s.out; } private: diff --git a/decoder_modules/radio/src/demodulators/wfm.h b/decoder_modules/radio/src/demodulators/wfm.h index 46cb175a..cab30b44 100644 --- a/decoder_modules/radio/src/demodulators/wfm.h +++ b/decoder_modules/radio/src/demodulators/wfm.h @@ -85,6 +85,7 @@ namespace demod { int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; } double getAFBandwidth(double bandwidth) { return 16000.0; } bool getDynamicAFBandwidth() { return false; } + bool getFMIFNRAllowed() { return true; } dsp::stream* getOutput() { return stereo ? demodStereo.out : &demod.out; } // ============= DEDICATED FUNCTIONS ============= diff --git a/decoder_modules/radio/src/radio_module.h b/decoder_modules/radio/src/radio_module.h index 4b7cc24f..d6bab955 100644 --- a/decoder_modules/radio/src/radio_module.h +++ b/decoder_modules/radio/src/radio_module.h @@ -59,8 +59,10 @@ public: ifChainOutputChanged.handler = ifChainOutputChangeHandler; ifChain.init(vfo->output, &ifChainOutputChanged); + fmnr.block.init(NULL, 64); squelch.block.init(NULL, MIN_SQUELCH); + ifChain.add(&fmnr); ifChain.add(&squelch); // Load configuration for and enabled all demodulators @@ -254,6 +256,13 @@ private: } if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); } + // FM IF Noise Reduction + if (_this->FMIFNRAllowed) { + if (ImGui::Checkbox("IF Noise Reduction##_radio_fmifnr_ena_", &_this->FMIFNREnabled)) { + _this->setFMIFNREnabled(_this->FMIFNREnabled); + } + } + // Demodulator specific menu _this->selectedDemod->showMenu(); @@ -300,7 +309,9 @@ private: deempMode = DEEMP_MODE_NONE; squelchEnabled = false; postProcEnabled = selectedDemod->getPostProcEnabled(); - if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) { + FMIFNRAllowed = selectedDemod->getFMIFNRAllowed(); + FMIFNREnabled = false; + if (config.conf[name][selectedDemod->getName()].contains("bandwidth")) { bandwidth = config.conf[name][selectedDemod->getName()]["bandwidth"]; bandwidth = std::clamp(bandwidth, minBandwidth, maxBandwidth); } @@ -316,6 +327,9 @@ private: if (config.conf[name][selectedDemod->getName()].contains("deempMode")) { deempMode = config.conf[name][selectedDemod->getName()]["deempMode"]; } + if (config.conf[name][selectedDemod->getName()].contains("FMIFNREnabled")) { + FMIFNREnabled = config.conf[name][selectedDemod->getName()]["FMIFNREnabled"]; + } deempMode = std::clamp(deempMode, 0, _DEEMP_MODE_COUNT-1); // Configure VFO @@ -326,7 +340,10 @@ private: vfo->setSampleRate(selectedDemod->getIFSampleRate(), bandwidth); } - // Configure IF chain + // Configure FM IF Noise Reduction + setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false); + + // Configure squelch squelch.block.setLevel(squelchLevel); setSquelchEnabled(squelchEnabled); @@ -439,6 +456,17 @@ private: config.release(true); } + void setFMIFNREnabled(bool enabled) { + FMIFNREnabled = enabled; + if (!selectedDemod) { return; } + ifChain.setState(&fmnr, FMIFNREnabled); + + // Save config + config.acquire(); + config.conf[name][selectedDemod->getName()]["FMIFNREnabled"] = FMIFNREnabled; + config.release(true); + } + static void vfoUserChangedBandwidthHandler(double newBw, void* ctx) { RadioModule* _this = (RadioModule*)ctx; _this->setBandwidth(newBw); @@ -521,6 +549,7 @@ private: // IF chain dsp::Chain ifChain; + dsp::ChainLink fmnr; dsp::ChainLink squelch; // Audio chain @@ -547,6 +576,8 @@ private: int deempMode = DEEMP_MODE_NONE; bool deempAllowed; bool postProcEnabled; + bool FMIFNRAllowed; + bool FMIFNREnabled = false; const double MIN_SQUELCH = -100.0; const double MAX_SQUELCH = 0.0; diff --git a/make_macos_bundle.sh b/make_macos_bundle.sh index c3d5604b..4c909e64 100644 --- a/make_macos_bundle.sh +++ b/make_macos_bundle.sh @@ -53,7 +53,6 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/n bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/m17_decoder/m17_decoder.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/meteor_demodulator/meteor_demodulator.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/radio/radio.dylib -bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/new_radio/new_radio.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/discord_integration/discord_integration.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/frequency_manager/frequency_manager.dylib diff --git a/make_windows_package.ps1 b/make_windows_package.ps1 index 7973b79f..0a3355fb 100644 --- a/make_windows_package.ps1 +++ b/make_windows_package.ps1 @@ -57,7 +57,6 @@ cp "C:/Program Files/codec2/lib/libcodec2.dll" sdrpp_windows_x64/ cp $build_dir/decoder_modules/meteor_demodulator/Release/meteor_demodulator.dll sdrpp_windows_x64/modules/ cp $build_dir/decoder_modules/radio/Release/radio.dll sdrpp_windows_x64/modules/ -cp $build_dir/decoder_modules/new_radio/Release/new_radio.dll sdrpp_windows_x64/modules/ # Copy misc modules