From 62d2dfafd74ced07eef89d571221a1bad9e4dc9b Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 4 Dec 2021 04:49:51 +0100 Subject: [PATCH] More work on the new radio --- CMakeLists.txt | 1 + core/src/credits.cpp | 1 + core/src/gui/dialogs/credits.cpp | 2 +- decoder_modules/new_radio/src/demod.h | 24 ++- .../new_radio/src/demodulators/am.h | 77 +++++++++ .../new_radio/src/demodulators/cw.h | 79 +++++++++ .../new_radio/src/demodulators/dsb.h | 79 +++++++++ .../new_radio/src/demodulators/lsb.h | 79 +++++++++ .../new_radio/src/demodulators/nfm.h | 69 ++++++++ .../new_radio/src/demodulators/usb.h | 79 +++++++++ .../new_radio/src/demodulators/wfm.h | 34 ++-- decoder_modules/new_radio/src/radio_module.h | 150 ++++++++++++++++-- 12 files changed, 638 insertions(+), 36 deletions(-) create mode 100644 decoder_modules/new_radio/src/demodulators/am.h create mode 100644 decoder_modules/new_radio/src/demodulators/cw.h create mode 100644 decoder_modules/new_radio/src/demodulators/dsb.h create mode 100644 decoder_modules/new_radio/src/demodulators/lsb.h create mode 100644 decoder_modules/new_radio/src/demodulators/nfm.h create mode 100644 decoder_modules/new_radio/src/demodulators/usb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b7a11028..4d4fd3f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,7 @@ 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_WEATHER_SAT_DECODER) diff --git a/core/src/credits.cpp b/core/src/credits.cpp index c14c33f1..fd30346f 100644 --- a/core/src/credits.cpp +++ b/core/src/credits.cpp @@ -38,6 +38,7 @@ namespace sdrpp_credits { const char* hardwareDonators[] = { "Airspy", "Analog Devices", + "CaribouLabs", "Howard Su", "MyriadRF", "Nuand", diff --git a/core/src/gui/dialogs/credits.cpp b/core/src/gui/dialogs/credits.cpp index e8103d67..bffe8e53 100644 --- a/core/src/gui/dialogs/credits.cpp +++ b/core/src/gui/dialogs/credits.cpp @@ -31,7 +31,7 @@ namespace credits { ImGui::Spacing(); ImGui::Spacing(); - ImGui::Text("This software is brought to you by Alexandre Rouma with the help of\n\n"); + ImGui::Text("This software is brought to you by Alexandre Rouma (ON5RYZ) with the help of\n\n"); ImGui::Columns(4, "CreditColumns", true); diff --git a/decoder_modules/new_radio/src/demod.h b/decoder_modules/new_radio/src/demod.h index 0d80d210..b24381de 100644 --- a/decoder_modules/new_radio/src/demod.h +++ b/decoder_modules/new_radio/src/demod.h @@ -3,13 +3,20 @@ #include #include #include -#include "radio_module.h" +#include + +enum DeemphasisMode { + DEEMP_MODE_50US, + DEEMP_MODE_75US, + DEEMP_MODE_NONE, + _DEEMP_MODE_COUNT +}; namespace demod { class Demodulator { public: virtual ~Demodulator() {} - virtual void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth) = 0; + virtual void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) = 0; virtual void start() = 0; virtual void stop() = 0; virtual void showMenu() = 0; @@ -21,11 +28,22 @@ namespace demod { virtual double getDefaultBandwidth() = 0; virtual double getMinBandwidth() = 0; virtual double getMaxBandwidth() = 0; + virtual bool getBandwidthLocked() = 0; virtual double getMaxAFBandwidth() = 0; virtual double getDefaultSnapInterval() = 0; virtual int getVFOReference() = 0; + virtual bool getDeempAllowed() = 0; + virtual int getDefaultDeemphasisMode() = 0; + virtual double getAFBandwidth(double bandwidth) = 0; + virtual bool getDynamicAFBandwidth() = 0; virtual dsp::stream* getOutput() = 0; }; } -#include "demodulators/wfm.h" \ No newline at end of file +#include "demodulators/wfm.h" +#include "demodulators/nfm.h" +#include "demodulators/am.h" +#include "demodulators/usb.h" +#include "demodulators/lsb.h" +#include "demodulators/dsb.h" +#include "demodulators/cw.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 new file mode 100644 index 00000000..749bd759 --- /dev/null +++ b/decoder_modules/new_radio/src/demodulators/am.h @@ -0,0 +1,77 @@ +#pragma once +#include "../demod.h" +#include +#include + +namespace demod { + class AM : public Demodulator { + public: + AM() {} + + AM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); + } + + ~AM() { + stop(); + } + + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + this->name = name; + this->outputChangeHandler = outputChangeHandler; + + // Define structure + demod.init(input); + agc.init(&demod.out, 20.0f, getIFSampleRate()); + m2s.init(&agc.out); + } + + void start() { + demod.start(); + agc.start(); + m2s.start(); + } + + void stop() { + demod.stop(); + agc.stop(); + m2s.stop(); + } + + void showMenu() { + // TODO: Adjust AGC settings + } + + void setBandwidth(double bandwidth) {} + + void setInput(dsp::stream* input) { + demod.setInput(input); + } + + // ============= INFO ============= + + const char* getName() { return "AM"; } + double getIFSampleRate() { return 15000.0; } + double getAFSampleRate() { return getIFSampleRate(); } + double getDefaultBandwidth() { return 10000.0; } + double getMinBandwidth() { return 1000.0; } + double getMaxBandwidth() { return getIFSampleRate(); } + bool getBandwidthLocked() { return false; } + double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; } + double getDefaultSnapInterval() { return 1000.0; } + int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } + bool getDeempAllowed() { return false; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } + double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } + bool getDynamicAFBandwidth() { return true; } + dsp::stream* getOutput() { return &m2s.out; } + + private: + dsp::AMDemod demod; + dsp::AGC agc; + dsp::MonoToStereo m2s; + + std::string name; + EventHandler*> outputChangeHandler; + }; +} \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/cw.h b/decoder_modules/new_radio/src/demodulators/cw.h new file mode 100644 index 00000000..67def80d --- /dev/null +++ b/decoder_modules/new_radio/src/demodulators/cw.h @@ -0,0 +1,79 @@ +#pragma once +#include "../demod.h" +#include +#include + +namespace demod { + class CW : public Demodulator { + public: + CW() {} + + CW(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); + } + + ~CW() { + stop(); + } + + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + this->name = name; + this->outputChangeHandler = outputChangeHandler; + + // Define structure + xlator.init(input, getIFSampleRate(), 1000.0); + c2r.init(&xlator.out); + agc.init(&c2r.out, 20.0f, getIFSampleRate()); + m2s.init(&agc.out); + } + + void start() { + xlator.start(); + c2r.start(); + agc.start(); + m2s.start(); + } + + void stop() { + xlator.stop(); + c2r.stop(); + agc.stop(); + m2s.stop(); + } + + void showMenu() {} + + void setBandwidth(double bandwidth) {} + + void setInput(dsp::stream* input) { + xlator.setInput(input); + } + + // ============= INFO ============= + + const char* getName() { return "CW"; } + double getIFSampleRate() { return 3000.0; } + double getAFSampleRate() { return getIFSampleRate(); } + double getDefaultBandwidth() { return 500.0; } + double getMinBandwidth() { return 50.0; } + double getMaxBandwidth() { return 500.0; } + bool getBandwidthLocked() { return false; } + double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; } + double getDefaultSnapInterval() { return 10.0; } + int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } + bool getDeempAllowed() { return false; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } + double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + 1000.0; } + bool getDynamicAFBandwidth() { return true; } + dsp::stream* getOutput() { return &m2s.out; } + + private: + dsp::FrequencyXlator xlator; + dsp::ComplexToReal c2r; + dsp::AGC agc; + dsp::MonoToStereo m2s; + + std::string name; + EventHandler*> outputChangeHandler; + }; +} \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/dsb.h b/decoder_modules/new_radio/src/demodulators/dsb.h new file mode 100644 index 00000000..dbbb51db --- /dev/null +++ b/decoder_modules/new_radio/src/demodulators/dsb.h @@ -0,0 +1,79 @@ +#pragma once +#include "../demod.h" +#include +#include + +namespace demod { + class DSB : public Demodulator { + public: + DSB() {} + + DSB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); + } + + ~DSB() { + stop(); + } + + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + this->name = name; + this->outputChangeHandler = outputChangeHandler; + + // Define structure + demod.init(input, getIFSampleRate(), bandwidth, dsp::SSBDemod::MODE_DSB); + agc.init(&demod.out, 20.0f, getIFSampleRate()); + m2s.init(&agc.out); + } + + void start() { + demod.start(); + agc.start(); + m2s.start(); + } + + void stop() { + demod.stop(); + agc.stop(); + m2s.stop(); + } + + void showMenu() { + // TODO: Adjust AGC settings + } + + void setBandwidth(double bandwidth) { + demod.setBandWidth(bandwidth); + } + + void setInput(dsp::stream* input) { + demod.setInput(input); + } + + // ============= INFO ============= + + const char* getName() { return "DSB"; } + double getIFSampleRate() { return 24000.0; } + double getAFSampleRate() { return getIFSampleRate(); } + double getDefaultBandwidth() { return 4600.0; } + double getMinBandwidth() { return 1000.0; } + double getMaxBandwidth() { return getIFSampleRate() / 2.0; } + bool getBandwidthLocked() { return false; } + double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; } + double getDefaultSnapInterval() { return 100.0; } + int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } + bool getDeempAllowed() { return false; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } + double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } + bool getDynamicAFBandwidth() { return true; } + dsp::stream* getOutput() { return &m2s.out; } + + private: + dsp::SSBDemod demod; + dsp::AGC agc; + dsp::MonoToStereo m2s; + + std::string name; + EventHandler*> outputChangeHandler; + }; +} \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/lsb.h b/decoder_modules/new_radio/src/demodulators/lsb.h new file mode 100644 index 00000000..cf0295d9 --- /dev/null +++ b/decoder_modules/new_radio/src/demodulators/lsb.h @@ -0,0 +1,79 @@ +#pragma once +#include "../demod.h" +#include +#include + +namespace demod { + class LSB : public Demodulator { + public: + LSB() {} + + LSB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); + } + + ~LSB() { + stop(); + } + + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + this->name = name; + this->outputChangeHandler = outputChangeHandler; + + // Define structure + demod.init(input, getIFSampleRate(), bandwidth, dsp::SSBDemod::MODE_LSB); + agc.init(&demod.out, 20.0f, getIFSampleRate()); + m2s.init(&agc.out); + } + + void start() { + demod.start(); + agc.start(); + m2s.start(); + } + + void stop() { + demod.stop(); + agc.stop(); + m2s.stop(); + } + + void showMenu() { + // TODO: Adjust AGC settings + } + + void setBandwidth(double bandwidth) { + demod.setBandWidth(bandwidth); + } + + void setInput(dsp::stream* input) { + demod.setInput(input); + } + + // ============= INFO ============= + + const char* getName() { return "LSB"; } + double getIFSampleRate() { return 24000.0; } + double getAFSampleRate() { return getIFSampleRate(); } + double getDefaultBandwidth() { return 2800.0; } + double getMinBandwidth() { return 500.0; } + double getMaxBandwidth() { return getIFSampleRate() / 2.0; } + bool getBandwidthLocked() { return false; } + double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; } + double getDefaultSnapInterval() { return 100.0; } + int getVFOReference() { return ImGui::WaterfallVFO::REF_UPPER; } + bool getDeempAllowed() { return false; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } + double getAFBandwidth(double bandwidth) { return bandwidth; } + bool getDynamicAFBandwidth() { return true; } + dsp::stream* getOutput() { return &m2s.out; } + + private: + dsp::SSBDemod demod; + dsp::AGC agc; + dsp::MonoToStereo m2s; + + std::string name; + EventHandler*> outputChangeHandler; + }; +} \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/nfm.h b/decoder_modules/new_radio/src/demodulators/nfm.h new file mode 100644 index 00000000..4a072ff2 --- /dev/null +++ b/decoder_modules/new_radio/src/demodulators/nfm.h @@ -0,0 +1,69 @@ +#pragma once +#include "../demod.h" +#include +#include + +namespace demod { + class NFM : public Demodulator { + public: + NFM() {} + + NFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); + } + + ~NFM() { + stop(); + } + + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + this->name = name; + this->outputChangeHandler = outputChangeHandler; + + // Define structure + demod.init(input, getIFSampleRate(), bandwidth / 2.0f); + } + + void start() { + demod.start(); + } + + void stop() { + demod.stop(); + } + + void showMenu() {} + + void setBandwidth(double bandwidth) { + demod.setDeviation(bandwidth / 2.0f); + } + + void setInput(dsp::stream* input) { + demod.setInput(input); + } + + // ============= INFO ============= + + const char* getName() { return "FM"; } + double getIFSampleRate() { return 50000.0; } + double getAFSampleRate() { return getIFSampleRate(); } + double getDefaultBandwidth() { return 12500.0; } + double getMinBandwidth() { return 1000.0; } + double getMaxBandwidth() { return getIFSampleRate(); } + bool getBandwidthLocked() { return false; } + double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; } + double getDefaultSnapInterval() { return 2500.0; } + int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } + bool getDeempAllowed() { return true; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } + double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; } + bool getDynamicAFBandwidth() { return true; } + dsp::stream* getOutput() { return &demod.out; } + + private: + dsp::FMDemod demod; + + std::string name; + EventHandler*> outputChangeHandler; + }; +} \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/usb.h b/decoder_modules/new_radio/src/demodulators/usb.h new file mode 100644 index 00000000..7fe12122 --- /dev/null +++ b/decoder_modules/new_radio/src/demodulators/usb.h @@ -0,0 +1,79 @@ +#pragma once +#include "../demod.h" +#include +#include + +namespace demod { + class USB : public Demodulator { + public: + USB() {} + + USB(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); + } + + ~USB() { + stop(); + } + + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + this->name = name; + this->outputChangeHandler = outputChangeHandler; + + // Define structure + demod.init(input, getIFSampleRate(), bandwidth, dsp::SSBDemod::MODE_USB); + agc.init(&demod.out, 20.0f, getIFSampleRate()); + m2s.init(&agc.out); + } + + void start() { + demod.start(); + agc.start(); + m2s.start(); + } + + void stop() { + demod.stop(); + agc.stop(); + m2s.stop(); + } + + void showMenu() { + // TODO: Adjust AGC settings + } + + void setBandwidth(double bandwidth) { + demod.setBandWidth(bandwidth); + } + + void setInput(dsp::stream* input) { + demod.setInput(input); + } + + // ============= INFO ============= + + const char* getName() { return "USB"; } + double getIFSampleRate() { return 24000.0; } + double getAFSampleRate() { return getIFSampleRate(); } + double getDefaultBandwidth() { return 2800.0; } + double getMinBandwidth() { return 500.0; } + double getMaxBandwidth() { return getIFSampleRate() / 2.0; } + bool getBandwidthLocked() { return false; } + double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; } + double getDefaultSnapInterval() { return 100.0; } + int getVFOReference() { return ImGui::WaterfallVFO::REF_LOWER; } + bool getDeempAllowed() { return false; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } + double getAFBandwidth(double bandwidth) { return bandwidth; } + bool getDynamicAFBandwidth() { return true; } + dsp::stream* getOutput() { return &m2s.out; } + + private: + dsp::SSBDemod demod; + dsp::AGC agc; + dsp::MonoToStereo m2s; + + std::string name; + EventHandler*> outputChangeHandler; + }; +} \ No newline at end of file diff --git a/decoder_modules/new_radio/src/demodulators/wfm.h b/decoder_modules/new_radio/src/demodulators/wfm.h index db9f56d1..acfd139c 100644 --- a/decoder_modules/new_radio/src/demodulators/wfm.h +++ b/decoder_modules/new_radio/src/demodulators/wfm.h @@ -8,29 +8,26 @@ namespace demod { public: WFM() {} - WFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth) { - init(name, config, input, bandwidth); + WFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { + init(name, config, input, bandwidth, outputChangeHandler); } ~WFM() { stop(); } - void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth) { + void init(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, EventHandler*> outputChangeHandler) { this->name = name; + this->outputChangeHandler = outputChangeHandler; _config = config; // Load config _config->acquire(); bool modified =false; if (!config->conf[name].contains(getName())) { - config->conf[name][getName()]["deempMode"] = 0; config->conf[name][getName()]["stereo"] = false; modified = true; } - if (config->conf[name][getName()].contains("deempMode")) { - deempMode = config->conf[name][getName()]["deempMode"]; - } if (config->conf[name][getName()].contains("stereo")) { stereo = config->conf[name][getName()]["stereo"]; } @@ -39,19 +36,15 @@ namespace demod { // Define structure demod.init(input, getIFSampleRate(), bandwidth / 2.0f); demodStereo.init(input, getIFSampleRate(), bandwidth / 2.0f); - //deemp.init(stereo ? demodStereo.out : &demod.out, getAFSampleRate(), 50e-6); - //deemp.bypass = false; } void start() { stereo ? demodStereo.start() : demod.start(); - //deemp.start(); } void stop() { demod.stop(); demodStereo.stop(); - //deemp.stop(); } void showMenu() { @@ -61,7 +54,6 @@ namespace demod { _config->conf[name][getName()]["stereo"] = stereo; _config->release(true); } - //ImGui::Checkbox("Deemp bypass", &deemp.bypass); } void setBandwidth(double bandwidth) { @@ -82,10 +74,15 @@ namespace demod { double getDefaultBandwidth() { return 150000.0; } double getMinBandwidth() { return 50000.0; } double getMaxBandwidth() { return getIFSampleRate(); } + bool getBandwidthLocked() { return false; } double getMaxAFBandwidth() { return 16000.0; } double getDefaultSnapInterval() { return 100000.0; } int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; } - dsp::stream* getOutput() { return demodStereo.out; } + bool getDeempAllowed() { return true; } + int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; } + double getAFBandwidth(double bandwidth) { return 16000.0; } + bool getDynamicAFBandwidth() { return false; } + dsp::stream* getOutput() { return stereo ? demodStereo.out : &demod.out; } // ============= DEDICATED FUNCTIONS ============= @@ -93,12 +90,12 @@ namespace demod { stereo = _stereo; if (stereo) { demod.stop(); - //deemp.setInput(demodStereo.out); + outputChangeHandler.handler(demodStereo.out, outputChangeHandler.ctx); demodStereo.start(); } else { demodStereo.stop(); - //deemp.setInput(&demod.out); + outputChangeHandler.handler(&demod.out, outputChangeHandler.ctx); demod.start(); } } @@ -106,15 +103,12 @@ namespace demod { private: dsp::FMDemod demod; dsp::StereoFMDemod demodStereo; - //dsp::BFMDeemp deemp; - RadioModule* _rad = NULL; ConfigManager* _config = NULL; - int deempMode; - bool stereo; + bool stereo = false; std::string name; - + EventHandler*> outputChangeHandler; }; } \ No newline at end of file diff --git a/decoder_modules/new_radio/src/radio_module.h b/decoder_modules/new_radio/src/radio_module.h index 2d5db47d..fb8671c7 100644 --- a/decoder_modules/new_radio/src/radio_module.h +++ b/decoder_modules/new_radio/src/radio_module.h @@ -10,6 +10,13 @@ ConfigManager config; #define CONCAT(a, b) ((std::string(a) + b).c_str()) +const double DeemphasisModes[] { + 50e-6, + 75e-6 +}; + +const char* DeemhasisModesTxt = "50µS\00075µS\000None\000"; + class RadioModule; #include "demod.h" @@ -29,6 +36,12 @@ public: // Create demodulator instances demods.fill(NULL); demods[RADIO_DEMOD_WFM] = new demod::WFM(); + demods[RADIO_DEMOD_NFM] = new demod::NFM(); + demods[RADIO_DEMOD_AM] = new demod::AM(); + demods[RADIO_DEMOD_USB] = new demod::USB(); + demods[RADIO_DEMOD_LSB] = new demod::LSB(); + demods[RADIO_DEMOD_DSB] = new demod::DSB(); + demods[RADIO_DEMOD_CW] = new demod::CW(); // Initialize the VFO vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 50000, 200000, false); @@ -43,6 +56,9 @@ public: sigpath::sinkManager.registerStream(name, &stream); // Load configuration for all demodulators + EventHandler*> _demodOutputChangeHandler; + _demodOutputChangeHandler.handler = demodOutputChangeHandler; + _demodOutputChangeHandler.ctx = this; for (auto& demod : demods) { if (!demod) { continue; } @@ -52,11 +68,13 @@ public: config.conf[name][demod->getName()]["bandwidth"] = bw; config.conf[name][demod->getName()]["snapInterval"] = demod->getDefaultSnapInterval(); config.conf[name][demod->getName()]["squelchLevel"] = MIN_SQUELCH; + config.conf[name][demod->getName()]["squelchEnabled"] = false; + config.conf[name][demod->getName()]["deempMode"] = demod->getDefaultDeemphasisMode(); } bw = std::clamp(bw, demod->getMinBandwidth(), demod->getMaxBandwidth()); // Initialize - demod->init(name, &config, &squelch.out, bw); + demod->init(name, &config, &squelch.out, bw, _demodOutputChangeHandler); } // Initialize DSP @@ -119,8 +137,6 @@ private: float menuWidth = ImGui::GetContentRegionAvailWidth(); ImGui::BeginGroup(); - // TODO: Change VFO ref in signal path - ImGui::Columns(4, CONCAT("RadioModeColumns##_", _this->name), false); if (ImGui::RadioButton(CONCAT("NFM##_", _this->name), _this->selectedDemodID == 0) && _this->selectedDemodID != 0) { _this->selectDemodByID(RADIO_DEMOD_NFM); @@ -153,16 +169,57 @@ private: ImGui::EndGroup(); - _this->selectedDemod->showMenu(); + if (!_this->bandwidthLocked) { + ImGui::LeftLabel("Bandwidth"); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::InputFloat(("##_radio_bw_" + _this->name).c_str(), &_this->bandwidth, 1, 100, "%.0f")) { + _this->bandwidth = std::clamp(_this->bandwidth, _this->minBandwidth, _this->maxBandwidth); + _this->setBandwidth(_this->bandwidth); + config.acquire(); + config.conf[_this->name][_this->selectedDemod->getName()]["bandwidth"] = _this->bandwidth; + config.release(true); + } + } - ImGui::LeftLabel("Squelch"); + ImGui::LeftLabel("Snap Interval"); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderFloat(("##_radio_sqelch_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) { + if (ImGui::InputInt(("##_radio_snap_" + _this->name).c_str(), &_this->snapInterval, 1, 100)) { + if (_this->snapInterval < 1) { _this->snapInterval = 1; } + _this->vfo->setSnapInterval(_this->snapInterval); + config.acquire(); + config.conf[_this->name][_this->selectedDemod->getName()]["snapInterval"] = _this->snapInterval; + config.release(true); + } + + if (_this->deempAllowed) { + ImGui::LeftLabel("De-emphasis"); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::Combo(("##_radio_wfm_deemp_" + _this->name).c_str(), &_this->deempMode, DeemhasisModesTxt)) { + _this->setDeemphasisMode(_this->deempMode); + config.acquire(); + config.conf[_this->name][_this->selectedDemod->getName()]["deempMode"] = _this->deempMode; + config.release(true); + } + } + + if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) { + _this->setSquelchEnabled(_this->squelchEnabled); + config.acquire(); + config.conf[_this->name][_this->selectedDemod->getName()]["squelchEnabled"] = _this->squelchEnabled; + config.release(true); + } + if (!_this->squelchEnabled && _this->enabled) { style::beginDisabled(); } + ImGui::SameLine(); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) { _this->squelch.setLevel(_this->squelchLevel); config.acquire(); config.conf[_this->name][_this->selectedDemod->getName()]["squelchLevel"] = _this->squelchLevel; config.release(true); } + if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); } + + _this->selectedDemod->showMenu(); if (!_this->enabled) { style::endDisabled(); } } @@ -184,10 +241,13 @@ private: // Load config bandwidth = selectedDemod->getDefaultBandwidth(); - double minBandwidth = selectedDemod->getMinBandwidth(); - double maxBandwidth = selectedDemod->getMaxBandwidth(); + minBandwidth = selectedDemod->getMinBandwidth(); + maxBandwidth = selectedDemod->getMaxBandwidth(); + bandwidthLocked = selectedDemod->getBandwidthLocked(); snapInterval = selectedDemod->getDefaultSnapInterval(); squelchLevel = MIN_SQUELCH; + deempMode = DEEMP_MODE_NONE; + squelchEnabled = false; if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) { bandwidth = config.conf[name][selectedDemod->getName()]["bandwidth"]; bandwidth = std::clamp(bandwidth, minBandwidth, maxBandwidth); @@ -198,6 +258,13 @@ private: if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) { squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"]; } + if (config.conf[name][selectedDemod->getName()].contains("squelchEnabled")) { + squelchEnabled = config.conf[name][selectedDemod->getName()]["squelchEnabled"]; + } + if (config.conf[name][selectedDemod->getName()].contains("deempMode")) { + deempMode = config.conf[name][selectedDemod->getName()]["deempMode"]; + } + deempMode = std::clamp(deempMode, 0, _DEEMP_MODE_COUNT-1); // Configure VFO if (vfo) { @@ -209,6 +276,7 @@ private: // Configure squelch squelch.setLevel(squelchLevel); + setSquelchEnabled(squelchEnabled); // Configure resampler resamp.stop(); @@ -217,6 +285,15 @@ private: setAudioSampleRate(audioSampleRate); resamp.start(); + // Configure deemphasis + deempAllowed = selectedDemod->getDeempAllowed(); + if (deempAllowed) { + setDeemphasisMode(deempMode); + } + else { + setDeemphasisMode(DEEMP_MODE_NONE); + } + // Start new demodulator selectedDemod->start(); } @@ -224,7 +301,14 @@ private: void setBandwidth(double bw) { bandwidth = bw; if (!selectedDemod) { return; } + float audioBW = std::min(selectedDemod->getMaxAFBandwidth(), selectedDemod->getAFBandwidth(bandwidth)); + vfo->setBandwidth(bandwidth); selectedDemod->setBandwidth(bandwidth); + if (selectedDemod->getDynamicAFBandwidth()) { + win.setCutoff(audioBW); + win.setTransWidth(audioBW); + resamp.updateWindow(&win); + } config.acquire(); config.conf[name][selectedDemod->getName()]["bandwidth"] = bandwidth; config.release(true); @@ -233,14 +317,45 @@ private: void setAudioSampleRate(double sr) { audioSampleRate = sr; if (!selectedDemod) { return; } - float audioBW = std::min(selectedDemod->getMaxAFBandwidth(), audioSampleRate / 2.0f); + float audioBW = std::min(selectedDemod->getMaxAFBandwidth(), selectedDemod->getAFBandwidth(bandwidth)); resamp.stop(); + deemp.stop(); + deemp.setSampleRate(audioSampleRate); resamp.setOutSampleRate(audioSampleRate); - win.setSampleRate(audioSampleRate * resamp.getInterpolation()); + win.setSampleRate(selectedDemod->getAFSampleRate() * resamp.getInterpolation()); win.setCutoff(audioBW); win.setTransWidth(audioBW); resamp.updateWindow(&win); resamp.start(); + if (deempMode != DEEMP_MODE_NONE) { deemp.start(); } + } + + void setDeemphasisMode(int mode) { + deempMode = mode; + if (mode != DEEMP_MODE_NONE) { + // TODO: Investigate why not stopping the deemp here causes the DSP to stall + deemp.stop(); + stream.setInput(&deemp.out); + deemp.setTau(DeemphasisModes[deempMode]); + deemp.start(); + } + else { + deemp.stop(); + stream.setInput(&resamp.out); + } + } + + void setSquelchEnabled(bool enable) { + squelchEnabled = enable; + if (!selectedDemod) { return; } + if (squelchEnabled) { + selectedDemod->setInput(&squelch.out); + squelch.start(); + } + else { + squelch.stop(); + selectedDemod->setInput(vfo->output); + } } static void vfoUserChangedBandwidthHandler(double newBw, void* ctx) { @@ -252,6 +367,11 @@ private: RadioModule* _this = (RadioModule*)ctx; _this->setAudioSampleRate(sampleRate); } + + static void demodOutputChangeHandler(dsp::stream* output, void* ctx) { + RadioModule* _this = (RadioModule*)ctx; + _this->resamp.setInput(output); + } EventHandler onUserChangedBandwidthHandler; VFOManager::VFO* vfo; @@ -268,10 +388,16 @@ private: demod::Demodulator* selectedDemod = NULL; double audioSampleRate = 48000.0; - double bandwidth; - double snapInterval; + float minBandwidth; + float maxBandwidth; + float bandwidth; + bool bandwidthLocked; + int snapInterval; + bool squelchEnabled = false; float squelchLevel; int selectedDemodID = 1; + int deempMode = DEEMP_MODE_NONE; + bool deempAllowed; const double MIN_SQUELCH = -100.0; const double MAX_SQUELCH = 0.0;