From fe285c71ff0637731cca5f2050b56b240dd4eea4 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 4 Dec 2021 17:46:48 +0100 Subject: [PATCH] New radio module --- CMakeLists.txt | 6 +- decoder_modules/new_radio/src/demod.h | 8 +- .../new_radio/src/demodulators/am.h | 9 +- .../new_radio/src/demodulators/cw.h | 9 +- .../new_radio/src/demodulators/dsb.h | 9 +- .../new_radio/src/demodulators/lsb.h | 9 +- .../new_radio/src/demodulators/nfm.h | 9 +- .../new_radio/src/demodulators/usb.h | 9 +- .../new_radio/src/demodulators/wfm.h | 9 +- decoder_modules/new_radio/src/radio_module.h | 94 ++++++++++++++----- make_windows_package.ps1 | 1 + 11 files changed, 125 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d4fd3f2..98eded18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (no dependencies required)" OFF) option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON) option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON) +option(OPT_BUILD_NEW_RADIO "Beta Main audio modulation decoder (AM, FM, SSB, etc...)" ON) option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF) # Misc @@ -145,9 +146,12 @@ endif (OPT_BUILD_METEOR_DEMODULATOR) if (OPT_BUILD_RADIO) add_subdirectory("decoder_modules/radio") -add_subdirectory("decoder_modules/new_radio") endif (OPT_BUILD_RADIO) +if (OPT_BUILD_NEW_RADIO) +add_subdirectory("decoder_modules/new_radio") +endif (OPT_BUILD_NEW_RADIO) + if (OPT_BUILD_WEATHER_SAT_DECODER) add_subdirectory("decoder_modules/weather_sat_decoder") endif (OPT_BUILD_WEATHER_SAT_DECODER) diff --git a/decoder_modules/new_radio/src/demod.h b/decoder_modules/new_radio/src/demod.h index b24381de..93d888b5 100644 --- a/decoder_modules/new_radio/src/demod.h +++ b/decoder_modules/new_radio/src/demod.h @@ -16,12 +16,13 @@ namespace demod { class Demodulator { public: virtual ~Demodulator() {} - virtual void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) = 0; + virtual void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) = 0; virtual void start() = 0; virtual void stop() = 0; virtual void showMenu() = 0; virtual void setBandwidth(double bandwidth) = 0; virtual void setInput(dsp::stream* input) = 0; + virtual void AFSampRateChanged(double newSR) = 0; virtual const char* getName() = 0; virtual double getIFSampleRate() = 0; virtual double getAFSampleRate() = 0; @@ -33,8 +34,10 @@ namespace demod { virtual double getDefaultSnapInterval() = 0; virtual int getVFOReference() = 0; virtual bool getDeempAllowed() = 0; + virtual bool getPostProcEnabled() = 0; virtual int getDefaultDeemphasisMode() = 0; virtual double getAFBandwidth(double bandwidth) = 0; + virtual bool getDynamicAFBandwidth() = 0; virtual dsp::stream* getOutput() = 0; }; @@ -46,4 +49,5 @@ namespace demod { #include "demodulators/usb.h" #include "demodulators/lsb.h" #include "demodulators/dsb.h" -#include "demodulators/cw.h" \ No newline at end of file +#include "demodulators/cw.h" +#include "demodulators/raw.h" \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/am.h b/decoder_modules/new_radio/src/demodulators/am.h index 749bd759..3d3f6855 100644 --- a/decoder_modules/new_radio/src/demodulators/am.h +++ b/decoder_modules/new_radio/src/demodulators/am.h @@ -8,15 +8,15 @@ namespace demod { public: AM() {} - AM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + AM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~AM() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; @@ -48,6 +48,8 @@ namespace demod { demod.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "AM"; } @@ -61,6 +63,7 @@ namespace demod { double getDefaultSnapInterval() { return 1000.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } bool getDeempAllowed() { return false; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } bool getDynamicAFBandwidth() { return true; } diff --git a/decoder_modules/new_radio/src/demodulators/cw.h b/decoder_modules/new_radio/src/demodulators/cw.h index 67def80d..de5efef5 100644 --- a/decoder_modules/new_radio/src/demodulators/cw.h +++ b/decoder_modules/new_radio/src/demodulators/cw.h @@ -8,15 +8,15 @@ namespace demod { public: CW() {} - CW(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + CW(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~CW() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; @@ -49,6 +49,8 @@ namespace demod { xlator.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "CW"; } @@ -62,6 +64,7 @@ namespace demod { double getDefaultSnapInterval() { return 10.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } bool getDeempAllowed() { return false; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + 1000.0; } bool getDynamicAFBandwidth() { return true; } diff --git a/decoder_modules/new_radio/src/demodulators/dsb.h b/decoder_modules/new_radio/src/demodulators/dsb.h index dbbb51db..323ab293 100644 --- a/decoder_modules/new_radio/src/demodulators/dsb.h +++ b/decoder_modules/new_radio/src/demodulators/dsb.h @@ -8,15 +8,15 @@ namespace demod { public: DSB() {} - DSB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + DSB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~DSB() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; @@ -50,6 +50,8 @@ namespace demod { demod.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "DSB"; } @@ -63,6 +65,7 @@ namespace demod { double getDefaultSnapInterval() { return 100.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } bool getDeempAllowed() { return false; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } bool getDynamicAFBandwidth() { return true; } diff --git a/decoder_modules/new_radio/src/demodulators/lsb.h b/decoder_modules/new_radio/src/demodulators/lsb.h index cf0295d9..ac1eaf22 100644 --- a/decoder_modules/new_radio/src/demodulators/lsb.h +++ b/decoder_modules/new_radio/src/demodulators/lsb.h @@ -8,15 +8,15 @@ namespace demod { public: LSB() {} - LSB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + LSB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~LSB() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; @@ -50,6 +50,8 @@ namespace demod { demod.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "LSB"; } @@ -63,6 +65,7 @@ namespace demod { double getDefaultSnapInterval() { return 100.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_UPPER; } bool getDeempAllowed() { return false; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth; } bool getDynamicAFBandwidth() { return true; } diff --git a/decoder_modules/new_radio/src/demodulators/nfm.h b/decoder_modules/new_radio/src/demodulators/nfm.h index 4a072ff2..e647b480 100644 --- a/decoder_modules/new_radio/src/demodulators/nfm.h +++ b/decoder_modules/new_radio/src/demodulators/nfm.h @@ -8,15 +8,15 @@ namespace demod { public: NFM() {} - NFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + NFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~NFM() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; @@ -42,6 +42,8 @@ namespace demod { demod.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "FM"; } @@ -55,6 +57,7 @@ namespace demod { double getDefaultSnapInterval() { return 2500.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } bool getDeempAllowed() { return true; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } bool getDynamicAFBandwidth() { return true; } diff --git a/decoder_modules/new_radio/src/demodulators/usb.h b/decoder_modules/new_radio/src/demodulators/usb.h index 7fe12122..eeef9455 100644 --- a/decoder_modules/new_radio/src/demodulators/usb.h +++ b/decoder_modules/new_radio/src/demodulators/usb.h @@ -8,15 +8,15 @@ namespace demod { public: USB() {} - USB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + USB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~USB() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; @@ -50,6 +50,8 @@ namespace demod { demod.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "USB"; } @@ -63,6 +65,7 @@ namespace demod { double getDefaultSnapInterval() { return 100.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_LOWER; } bool getDeempAllowed() { return false; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } double getAFBandwidth(double bandwidth) { return bandwidth; } bool getDynamicAFBandwidth() { return true; } diff --git a/decoder_modules/new_radio/src/demodulators/wfm.h b/decoder_modules/new_radio/src/demodulators/wfm.h index acfd139c..46cb175a 100644 --- a/decoder_modules/new_radio/src/demodulators/wfm.h +++ b/decoder_modules/new_radio/src/demodulators/wfm.h @@ -8,15 +8,15 @@ namespace demod { public: WFM() {} - WFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { - init(name, config, input, bandwidth, outputChangeHandler); + WFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { + init(name, config, input, bandwidth, outputChangeHandler, audioSR); } ~WFM() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler, double audioSR) { this->name = name; this->outputChangeHandler = outputChangeHandler; _config = config; @@ -66,6 +66,8 @@ namespace demod { demodStereo.setInput(input); } + void AFSampRateChanged(double newSR) {} + // ============= INFO ============= const char* getName() { return "WFM"; } @@ -79,6 +81,7 @@ namespace demod { double getDefaultSnapInterval() { return 100000.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } bool getDeempAllowed() { return true; } + bool getPostProcEnabled() { return true; } int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; } double getAFBandwidth(double bandwidth) { return 16000.0; } bool getDynamicAFBandwidth() { return false; } diff --git a/decoder_modules/new_radio/src/radio_module.h b/decoder_modules/new_radio/src/radio_module.h index fb8671c7..02c85076 100644 --- a/decoder_modules/new_radio/src/radio_module.h +++ b/decoder_modules/new_radio/src/radio_module.h @@ -42,6 +42,7 @@ public: demods[RADIO_DEMOD_LSB] = new demod::LSB(); demods[RADIO_DEMOD_DSB] = new demod::DSB(); demods[RADIO_DEMOD_CW] = new demod::CW(); + demods[RADIO_DEMOD_RAW] = new demod::RAW(); // Initialize the VFO vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 50000, 200000, false); @@ -74,7 +75,7 @@ public: bw = std::clamp(bw, demod->getMinBandwidth(), demod->getMaxBandwidth()); // Initialize - demod->init(name, &config, &squelch.out, bw, _demodOutputChangeHandler); + demod->init(name, &config, &squelch.out, bw, _demodOutputChangeHandler, stream.getSampleRate()); } // Initialize DSP @@ -87,13 +88,10 @@ public: // Select the demodulator selectDemodByID((DemodID)selectedDemodID); - // Start DSP - squelch.start(); - resamp.start(); - deemp.start(); + // Start stream, the rest was started when selecting the demodulator stream.start(); - gui::menu.registerEntry(name, menuHandler, this, NULL); + gui::menu.registerEntry(name, menuHandler, this, this); } ~RadioModule() { @@ -104,10 +102,20 @@ public: void enable() { enabled = true; + if (!vfo) { + vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 50000, 200000, false); + } + selectDemodByID((DemodID)selectedDemodID); } void disable() { enabled = false; + squelch.stop(); + if (selectedDemod) { selectedDemod->stop(); } + resamp.stop(); + deemp.stop(); + if (vfo) { sigpath::vfoManager.deleteVFO(vfo); } + vfo = NULL; } bool isEnabled() { @@ -239,6 +247,9 @@ private: if (selectedDemod) { selectedDemod->stop(); } selectedDemod = demod; + // Give the demodulator the most recent audio SR + selectedDemod->AFSampRateChanged(audioSampleRate); + // Load config bandwidth = selectedDemod->getDefaultBandwidth(); minBandwidth = selectedDemod->getMinBandwidth(); @@ -246,6 +257,7 @@ private: bandwidthLocked = selectedDemod->getBandwidthLocked(); snapInterval = selectedDemod->getDefaultSnapInterval(); squelchLevel = MIN_SQUELCH; + deempAllowed = selectedDemod->getDeempAllowed(); deempMode = DEEMP_MODE_NONE; squelchEnabled = false; if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) { @@ -268,7 +280,7 @@ private: // Configure VFO if (vfo) { - vfo->setBandwidthLimits(minBandwidth, maxBandwidth, false); + vfo->setBandwidthLimits(minBandwidth, maxBandwidth, selectedDemod->getBandwidthLocked()); vfo->setReference(selectedDemod->getVFOReference()); vfo->setSnapInterval(snapInterval); vfo->setSampleRate(selectedDemod->getIFSampleRate(), bandwidth); @@ -278,33 +290,38 @@ private: squelch.setLevel(squelchLevel); setSquelchEnabled(squelchEnabled); - // Configure resampler - resamp.stop(); - resamp.setInput(selectedDemod->getOutput()); - resamp.setInSampleRate(selectedDemod->getAFSampleRate()); - setAudioSampleRate(audioSampleRate); - resamp.start(); + // Enable or disable post processing entirely depending on the demodulator's options + setPostProcEnabled(selectedDemod->getPostProcEnabled()); - // Configure deemphasis - deempAllowed = selectedDemod->getDeempAllowed(); - if (deempAllowed) { - setDeemphasisMode(deempMode); - } - else { - setDeemphasisMode(DEEMP_MODE_NONE); + if (postProcEnabled) { + // Configure resampler + resamp.stop(); + resamp.setInput(selectedDemod->getOutput()); + resamp.setInSampleRate(selectedDemod->getAFSampleRate()); + setAudioSampleRate(audioSampleRate); + + // Configure deemphasis + if (deempAllowed) { + setDeemphasisMode(deempMode); + } + else { + setDeemphasisMode(DEEMP_MODE_NONE); + } } // Start new demodulator selectedDemod->start(); } + void setBandwidth(double bw) { bandwidth = bw; if (!selectedDemod) { return; } float audioBW = std::min(selectedDemod->getMaxAFBandwidth(), selectedDemod->getAFBandwidth(bandwidth)); + audioBW = std::min(audioBW, audioSampleRate / 2.0); vfo->setBandwidth(bandwidth); selectedDemod->setBandwidth(bandwidth); - if (selectedDemod->getDynamicAFBandwidth()) { + if (selectedDemod->getDynamicAFBandwidth() && postProcEnabled) { win.setCutoff(audioBW); win.setTransWidth(audioBW); resamp.updateWindow(&win); @@ -317,7 +334,17 @@ private: void setAudioSampleRate(double sr) { audioSampleRate = sr; if (!selectedDemod) { return; } + selectedDemod->AFSampRateChanged(audioSampleRate); + if (!postProcEnabled) { + minBandwidth = selectedDemod->getMinBandwidth(); + maxBandwidth = selectedDemod->getMaxBandwidth(); + bandwidth = selectedDemod->getIFSampleRate(); + vfo->setBandwidthLimits(minBandwidth, maxBandwidth, selectedDemod->getBandwidthLocked()); + vfo->setSampleRate(selectedDemod->getIFSampleRate(), bandwidth); + return; + } float audioBW = std::min(selectedDemod->getMaxAFBandwidth(), selectedDemod->getAFBandwidth(bandwidth)); + audioBW = std::min(audioBW, audioSampleRate / 2.0); resamp.stop(); deemp.stop(); deemp.setSampleRate(audioSampleRate); @@ -330,9 +357,23 @@ private: if (deempMode != DEEMP_MODE_NONE) { deemp.start(); } } + void setPostProcEnabled(bool enable) { + postProcEnabled = enable; + if (!selectedDemod) { return; } + if (postProcEnabled) { + setDeemphasisMode(deempMode); + } + else { + resamp.stop(); + deemp.stop(); + stream.setInput(selectedDemod->getOutput()); + } + } + void setDeemphasisMode(int mode) { deempMode = mode; - if (mode != DEEMP_MODE_NONE) { + if (!postProcEnabled) { return; } + if (deempMode != DEEMP_MODE_NONE) { // TODO: Investigate why not stopping the deemp here causes the DSP to stall deemp.stop(); stream.setInput(&deemp.out); @@ -349,6 +390,7 @@ private: squelchEnabled = enable; if (!selectedDemod) { return; } if (squelchEnabled) { + squelch.setInput(vfo->output); selectedDemod->setInput(&squelch.out); squelch.start(); } @@ -370,7 +412,12 @@ private: static void demodOutputChangeHandler(dsp::stream* output, void* ctx) { RadioModule* _this = (RadioModule*)ctx; - _this->resamp.setInput(output); + if (_this->postProcEnabled) { + _this->resamp.setInput(output); + } + else { + _this->stream.setInput(output); + } } EventHandler onUserChangedBandwidthHandler; @@ -398,6 +445,7 @@ private: int selectedDemodID = 1; int deempMode = DEEMP_MODE_NONE; bool deempAllowed; + bool postProcEnabled; const double MIN_SQUELCH = -100.0; const double MAX_SQUELCH = 0.0; diff --git a/make_windows_package.ps1 b/make_windows_package.ps1 index 0a3355fb..d0bfeb40 100644 --- a/make_windows_package.ps1 +++ b/make_windows_package.ps1 @@ -57,6 +57,7 @@ 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/radio/Release/new_radio.dll sdrpp_windows_x64/modules/ # Copy misc modules