From 524f20bc2f992609e046571e044cf545e2d4bb6a Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Thu, 1 Oct 2020 01:21:15 +0200 Subject: [PATCH] Save before changes --- CMakeLists.txt | 3 +- core/src/dsp/correction.h | 7 + core/src/gui/main_window.cpp | 248 +++------------------------ core/src/gui/main_window.h | 2 + core/src/gui/menus/source.cpp | 24 ++- core/src/io/soapy.h | 1 + core/src/module.h | 57 ++---- core/src/signal_path/dsp.cpp | 9 +- core/src/signal_path/dsp.h | 1 + core/src/signal_path/signal_path.cpp | 1 + core/src/signal_path/signal_path.h | 3 + core/src/signal_path/source.cpp | 58 +++++++ core/src/signal_path/source.h | 37 ++++ demo/src/main.cpp | 61 ++++--- radio/src/main.cpp | 225 ++++++++++++------------ radio/src/path.cpp | 24 +-- 16 files changed, 348 insertions(+), 413 deletions(-) create mode 100644 core/src/signal_path/source.cpp create mode 100644 core/src/signal_path/source.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a9c6c4bf..acc0882f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,8 @@ project(sdrpp_core) add_subdirectory("core") add_subdirectory("radio") -add_subdirectory("recorder") +# Add back recorder when module new system is ready +#add_subdirectory("recorder") add_subdirectory("demo") add_executable(sdrpp "src/main.cpp" "win32/resources.rc") diff --git a/core/src/dsp/correction.h b/core/src/dsp/correction.h index 1db9ea1a..af39f77c 100644 --- a/core/src/dsp/correction.h +++ b/core/src/dsp/correction.h @@ -52,6 +52,13 @@ namespace dsp { output.setMaxLatency(blockSize * 2); } + void setInput(stream* input) { + if (running) { + return; + } + _in = input; + } + stream output; bool bypass; diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 11e7cb95..cb9b0b1b 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -6,12 +6,10 @@ std::mutex fft_mtx; fftwf_complex *fft_in, *fft_out; fftwf_plan p; float* tempData; -float* uiGains; char buf[1024]; int fftSize = 8192 * 8; -io::SoapyWrapper soapy; std::vector _data; std::vector fftTaps; void fftHandler(dsp::complex_t* samples) { @@ -34,10 +32,7 @@ void fftHandler(dsp::complex_t* samples) { } dsp::NullSink sink; -int devId = 0; -int srId = 0; watcher freq((uint64_t)90500000); -int demod = 1; watcher vfoFreq(92000000.0f); float dummyVolume = 1.0f; float* volume = &dummyVolume; @@ -45,7 +40,6 @@ float fftMin = -70.0f; float fftMax = 0.0f; watcher offset(0.0f, true); watcher bw(8000000.0f, true); -int sampleRate = 8000000; bool playing = false; watcher dcbias(false, false); bool showCredits = false; @@ -56,175 +50,14 @@ bool grabbingMenu = false; int newWidth = 300; int fftHeight = 300; bool showMenu = true; - -void saveCurrentSource() { - int i = 0; - for (std::string gainName : soapy.gainList) { - core::configManager.conf["sourceSettings"][sourceName]["gains"][gainName] = uiGains[i]; - i++; - } - core::configManager.conf["sourceSettings"][sourceName]["sampleRate"] = soapy.sampleRates[srId]; -} - -void loadSourceConfig(std::string name) { - json sourceSettings = core::configManager.conf["sourceSettings"][name]; - - sampleRate = sourceSettings["sampleRate"]; - - auto _srIt = std::find(soapy.sampleRates.begin(), soapy.sampleRates.end(), sampleRate); - // If the sample rate isn't valid, set to minimum - if (_srIt == soapy.sampleRates.end()) { - srId = 0; - sampleRate = soapy.sampleRates[0]; - } - else { - srId = std::distance(soapy.sampleRates.begin(), _srIt); - } - sigpath::signalPath.setSampleRate(sampleRate); - soapy.setSampleRate(sampleRate); - - // Set gains - delete[] uiGains; - uiGains = new float[soapy.gainList.size()]; - int i = 0; - for (std::string gainName : soapy.gainList) { - // If gain doesn't exist in config, set it to the minimum value - if (!sourceSettings["gains"].contains(gainName)) { - uiGains[i] = soapy.gainRanges[i].minimum(); - soapy.setGain(i, uiGains[i]); - continue; - } - uiGains[i] = sourceSettings["gains"][gainName]; - soapy.setGain(i, uiGains[i]); - i++; - } - - // Update GUI - gui::waterfall.setBandwidth(sampleRate); - gui::waterfall.setViewBandwidth(sampleRate); - bw.val = sampleRate; -} - -void setVFO(float freq); - -// ======================================================= - -void sourceMenu(void* ctx) { - float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); - if (playing) { style::beginDisabled(); }; - - ImGui::PushItemWidth(menuColumnWidth); - if (ImGui::Combo("##_0_", &devId, soapy.txtDevList.c_str())) { - spdlog::info("Changed input device: {0}", devId); - sourceName = soapy.devNameList[devId]; - soapy.setDevice(soapy.devList[devId]); - - core::configManager.aquire(); - if (core::configManager.conf["sourceSettings"].contains(sourceName)) { - loadSourceConfig(sourceName); - } - else { - srId = 0; - sampleRate = soapy.getSampleRate(); - bw.val = sampleRate; - gui::waterfall.setBandwidth(sampleRate); - gui::waterfall.setViewBandwidth(sampleRate); - sigpath::signalPath.setSampleRate(sampleRate); - - if (soapy.gainList.size() >= 0) { - delete[] uiGains; - uiGains = new float[soapy.gainList.size()]; - for (int i = 0; i < soapy.gainList.size(); i++) { - uiGains[i] = soapy.currentGains[i]; - } - } - } - setVFO(gui::freqSelect.frequency); - core::configManager.conf["source"] = sourceName; - core::configManager.release(true); - } - ImGui::PopItemWidth(); - - if (ImGui::Combo("##_1_", &srId, soapy.txtSampleRateList.c_str())) { - spdlog::info("Changed sample rate: {0}", srId); - sampleRate = soapy.sampleRates[srId]; - soapy.setSampleRate(sampleRate); - gui::waterfall.setBandwidth(sampleRate); - gui::waterfall.setViewBandwidth(sampleRate); - sigpath::signalPath.setSampleRate(sampleRate); - bw.val = sampleRate; - - core::configManager.aquire(); - if (!core::configManager.conf["sourceSettings"].contains(sourceName)) { - saveCurrentSource(); - } - core::configManager.conf["sourceSettings"][sourceName]["sampleRate"] = sampleRate; - core::configManager.release(true); - } - - ImGui::SameLine(); - bool noDevice = (soapy.devList.size() == 0); - if (ImGui::Button("Refresh", ImVec2(menuColumnWidth - ImGui::GetCursorPosX(), 0.0f))) { - soapy.refresh(); - if (noDevice && soapy.devList.size() > 0) { - sourceName = soapy.devNameList[0]; - soapy.setDevice(soapy.devList[0]); - if (core::configManager.conf["sourceSettings"][sourceName]) { - loadSourceConfig(sourceName); - } - } - } - - if (playing) { style::endDisabled(); }; - - float maxTextLength = 0; - float txtLen = 0; - char buf[100]; - - // Calculate the spacing - for (int i = 0; i < soapy.gainList.size(); i++) { - sprintf(buf, "%s gain", soapy.gainList[i].c_str()); - txtLen = ImGui::CalcTextSize(buf).x; - if (txtLen > maxTextLength) { - maxTextLength = txtLen; - } - } - - for (int i = 0; i < soapy.gainList.size(); i++) { - ImGui::Text("%s gain", soapy.gainList[i].c_str()); - ImGui::SameLine(); - sprintf(buf, "##_gain_slide_%d_", i); - - ImGui::SetCursorPosX(maxTextLength + 5); - ImGui::PushItemWidth(menuColumnWidth - (maxTextLength + 5)); - if (ImGui::SliderFloat(buf, &uiGains[i], soapy.gainRanges[i].minimum(), soapy.gainRanges[i].maximum())) { - soapy.setGain(i, uiGains[i]); - core::configManager.aquire(); - if (!core::configManager.conf["sourceSettings"].contains(sourceName)) { - saveCurrentSource(); - } - core::configManager.conf["sourceSettings"][sourceName]["gains"][soapy.gainList[i]] = uiGains[i]; - core::configManager.release(true); - } - ImGui::PopItemWidth(); - } - - ImGui::Spacing(); -} - -// ======================================================= +dsp::stream dummyStream; void windowInit() { spdlog::info("Initializing SoapySDR"); - soapy.init(); credits::init(); - audiomenu::init(); - bandplanmenu::init(); - displaymenu::init(); - - gui::menu.registerEntry("Source", sourceMenu, NULL); + gui::menu.registerEntry("Source", sourecmenu::draw, NULL); gui::menu.registerEntry("Audio", audiomenu::draw, NULL); gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL); gui::menu.registerEntry("Display", displaymenu::draw, NULL); @@ -235,50 +68,19 @@ void windowInit() { fft_out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize); p = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); - sigpath::signalPath.init(sampleRate, 20, fftSize, &soapy.output, (dsp::complex_t*)fft_in, fftHandler); + sigpath::signalPath.init(8000000, 20, fftSize, &dummyStream, (dsp::complex_t*)fft_in, fftHandler); sigpath::signalPath.start(); - uiGains = new float[soapy.gainList.size()]; - spdlog::info("Loading modules"); mod::initAPI(&gui::waterfall); mod::loadFromList(ROOT_DIR "/module_list.json"); + sourecmenu::init(); + audiomenu::init(); + bandplanmenu::init(); + displaymenu::init(); + // Load last source configuration - core::configManager.aquire(); - uint64_t frequency = core::configManager.conf["frequency"]; - sourceName = core::configManager.conf["source"]; - auto _sourceIt = std::find(soapy.devNameList.begin(), soapy.devNameList.end(), sourceName); - if (_sourceIt != soapy.devNameList.end() && core::configManager.conf["sourceSettings"].contains(sourceName)) { - json sourceSettings = core::configManager.conf["sourceSettings"][sourceName]; - devId = std::distance(soapy.devNameList.begin(), _sourceIt); - soapy.setDevice(soapy.devList[devId]); - loadSourceConfig(sourceName); - } - else { - int i = 0; - bool settingsFound = false; - for (std::string devName : soapy.devNameList) { - if (core::configManager.conf["sourceSettings"].contains(devName)) { - sourceName = devName; - settingsFound = true; - devId = i; - soapy.setDevice(soapy.devList[i]); - loadSourceConfig(sourceName); - break; - } - i++; - } - if (!settingsFound) { - if (soapy.devNameList.size() > 0) { - sourceName = soapy.devNameList[0]; - } - sampleRate = soapy.getSampleRate(); - sigpath::signalPath.setSampleRate(sampleRate); - } - // Search for the first source in the list to have a config - // If no pre-defined source, selected default device - } // Also add a loading screen // Adjustable "snap to grid" for each VFO @@ -301,15 +103,17 @@ void windowInit() { gui::waterfall.setWaterfallMin(fftMin); gui::waterfall.setFFTMax(fftMax); gui::waterfall.setWaterfallMax(fftMax); + + float frequency = core::configManager.conf["frequency"]; gui::freqSelect.setFrequency(frequency); gui::freqSelect.frequencyChanged = false; - soapy.setFrequency(frequency); + sigpath::sourceManager.tune(frequency); gui::waterfall.setCenterFrequency(frequency); - gui::waterfall.setBandwidth(sampleRate); - gui::waterfall.setViewBandwidth(sampleRate); - bw.val = sampleRate; + gui::waterfall.setBandwidth(8000000); + gui::waterfall.setViewBandwidth(8000000); + bw.val = 8000000; gui::waterfall.vfoFreqChanged = false; gui::waterfall.centerFreqMoved = false; gui::waterfall.selectFirstVFO(); @@ -357,7 +161,7 @@ void setVFO(float freq) { float newVFOOffset = (BW / 2.0f) - (vfoBW / 2.0f) - (viewBW / 10.0f); sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset); gui::waterfall.setCenterFrequency(freq - newVFOOffset); - soapy.setFrequency(freq - newVFOOffset); + sigpath::sourceManager.tune(freq - newVFOOffset); return; } @@ -367,7 +171,7 @@ void setVFO(float freq) { float newVFOOffset = (vfoBW / 2.0f) - (BW / 2.0f) + (viewBW / 10.0f); sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset); gui::waterfall.setCenterFrequency(freq - newVFOOffset); - soapy.setFrequency(freq - newVFOOffset); + sigpath::sourceManager.tune(freq - newVFOOffset); return; } @@ -387,7 +191,7 @@ void setVFO(float freq) { float newVFOOffset = (BW / 2.0f) - (vfoBW / 2.0f) - (viewBW / 10.0f); sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset); gui::waterfall.setCenterFrequency(freq - newVFOOffset); - soapy.setFrequency(freq - newVFOOffset); + sigpath::sourceManager.tune(freq - newVFOOffset); } else { float newViewOff = vfoBottom + (viewBW / 2.0f) - (viewBW / 10.0f); @@ -404,7 +208,7 @@ void setVFO(float freq) { float newVFOOffset = (vfoBW / 2.0f) - (BW / 2.0f) + (viewBW / 10.0f); sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset); gui::waterfall.setCenterFrequency(freq - newVFOOffset); - soapy.setFrequency(freq - newVFOOffset); + sigpath::sourceManager.tune(freq - newVFOOffset); } } @@ -450,7 +254,7 @@ void drawWindow() { if (gui::waterfall.centerFreqMoved) { gui::waterfall.centerFreqMoved = false; - soapy.setFrequency(gui::waterfall.getCenterFrequency()); + sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency()); gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); core::configManager.aquire(); core::configManager.conf["frequency"] = gui::freqSelect.frequency; @@ -491,14 +295,15 @@ void drawWindow() { if (playing) { if (ImGui::ImageButton(icons::STOP, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) { - soapy.stop(); + sigpath::sourceManager.stop(); playing = false; } } - else { - if (ImGui::ImageButton(icons::PLAY, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0) && soapy.devList.size() > 0) { - soapy.start(); - soapy.setFrequency(gui::waterfall.getCenterFrequency()); + else { // TODO: Might need to check if there even is a device + if (ImGui::ImageButton(icons::PLAY, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) { + sigpath::sourceManager.start(); + // TODO: tune in module instead + sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency()); playing = true; } } @@ -623,7 +428,8 @@ void drawWindow() { ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - (ImGui::CalcTextSize("Zoom").x / 2.0f)); ImGui::Text("Zoom"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - 10); - ImGui::VSliderFloat("##_7_", ImVec2(20.0f, 150.0f), &bw.val, sampleRate, 1000.0f, ""); + // TODO: use global sample rate value from DSP instead of 8000000 + ImGui::VSliderFloat("##_7_", ImVec2(20.0f, 150.0f), &bw.val, 8000000, 1000.0f, ""); ImGui::NewLine(); diff --git a/core/src/gui/main_window.h b/core/src/gui/main_window.h index 493125e4..03eb370f 100644 --- a/core/src/gui/main_window.h +++ b/core/src/gui/main_window.h @@ -28,10 +28,12 @@ #include #include #include +#include #include #include #include #include +#include #define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground diff --git a/core/src/gui/menus/source.cpp b/core/src/gui/menus/source.cpp index a6eec258..c2f06be9 100644 --- a/core/src/gui/menus/source.cpp +++ b/core/src/gui/menus/source.cpp @@ -1,11 +1,33 @@ #include +#include +#include +#include +#include namespace sourecmenu { - void init() { + int sourceId = 0; + void init() { + // Select default + // TODO: Replace by setting + if (sigpath::sourceManager.sourceNames.size() > 0) { + sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[0]); + } } void draw(void* ctx) { + std::string items = ""; + for (std::string name : sigpath::sourceManager.sourceNames) { + items += name; + items += '\0'; + } + float itemWidth = ImGui::GetContentRegionAvailWidth(); + ImGui::SetNextItemWidth(itemWidth); + if (ImGui::Combo("##source", &sourceId, items.c_str())) { + sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[sourceId]); + } + + sigpath::sourceManager.showSelectedMenu(); } } \ No newline at end of file diff --git a/core/src/io/soapy.h b/core/src/io/soapy.h index bb0c97ca..54151833 100644 --- a/core/src/io/soapy.h +++ b/core/src/io/soapy.h @@ -1,3 +1,4 @@ +#pragma once #include #include #include diff --git a/core/src/module.h b/core/src/module.h index 99faaa9a..159de6a7 100644 --- a/core/src/module.h +++ b/core/src/module.h @@ -29,45 +29,6 @@ #endif namespace mod { - struct API_t { - dsp::stream* (*registerVFO)(std::string name, int reference, float offset, float bandwidth, float sampleRate, int blockSize); - void (*setVFOOffset)(std::string name, float offset); - void (*setVFOCenterOffset)(std::string name, float offset); - void (*setVFOBandwidth)(std::string name, float bandwidth); - void (*setVFOSampleRate)(std::string name, float sampleRate, float bandwidth); - int (*getVFOOutputBlockSize)(std::string name); - void (*setVFOReference)(std::string name, int ref); - void (*removeVFO)(std::string name); - - std::string (*getSelectedVFOName)(void); - void (*bindVolumeVariable)(float* vol); - void (*unbindVolumeVariable)(void); - - float (*registerMonoStream)(dsp::stream* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx); - float (*registerStereoStream)(dsp::stream* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx); - void (*startStream)(std::string name); - void (*stopStream)(std::string name); - void (*removeStream)(std::string name); - dsp::stream* (*bindToStreamMono)(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx); - dsp::stream* (*bindToStreamStereo)(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx); - void (*setBlockSize)(std::string name, int blockSize); - void (*unbindFromStreamMono)(std::string name, dsp::stream* stream); - void (*unbindFromStreamStereo)(std::string name, dsp::stream* stream); - std::vector (*getStreamNameList)(void); - - enum { - REF_LOWER, - REF_CENTER, - REF_UPPER, - _REF_COUNT - }; - }; - - enum { - EVENT_STREAM_PARAM_CHANGED, - EVENT_SELECTED_VFO_CHANGED, - _EVENT_COUNT - }; struct Module_t { #ifdef _WIN32 @@ -75,14 +36,20 @@ namespace mod { #else void* inst; #endif - void* (*_INIT_)(API_t*, ImGuiContext*, std::string); - void (*_DRAW_MENU_)(void*); - void (*_NEW_FRAME_)(void*); - void (*_HANDLE_EVENT_)(void*, int); - void (*_STOP_)(void*); + void (*_INIT_)(); + void* (*_CREATE_INSTANCE)(std::string); + void (*_DELETE_INSTANCE)(); + void (*_STOP_)(); void* ctx; }; + struct ModuleInfo_t { + char* name; + char* description; + char* author; + char* version; + }; + void initAPI(ImGui::WaterFall* wtf); void loadModule(std::string path, std::string name); void broadcastEvent(int eventId); @@ -92,4 +59,4 @@ namespace mod { extern std::vector moduleNames; }; -extern mod::API_t* API; \ No newline at end of file +#define MOD_INFO MOD_EXPORT const mod::ModuleInfo_t _INFO \ No newline at end of file diff --git a/core/src/signal_path/dsp.cpp b/core/src/signal_path/dsp.cpp index f2f50333..691893f2 100644 --- a/core/src/signal_path/dsp.cpp +++ b/core/src/signal_path/dsp.cpp @@ -40,7 +40,7 @@ void SignalPath::setSampleRate(float sampleRate) { fftBlockDec.setSkip(skip); dynSplit.setBlockSize(inputBlockSize); - mod::broadcastEvent(mod::EVENT_STREAM_PARAM_CHANGED); + // TODO: Tell modules that the block size has changed for (auto const& [name, vfo] : vfos) { vfo.vfo->setInputSampleRate(sampleRate, inputBlockSize); @@ -87,6 +87,7 @@ dsp::VFO* SignalPath::addVFO(std::string name, float outSampleRate, float bandwi void SignalPath::removeVFO(std::string name) { if (vfos.find(name) == vfos.end()) { return; + } dynSplit.stop(); VFO_t vfo = vfos[name]; @@ -96,4 +97,10 @@ void SignalPath::removeVFO(std::string name) { delete vfo.inputStream; dynSplit.start(); vfos.erase(name); +} + +void SignalPath::setInput(dsp::stream* input) { + dcBiasRemover.stop(); + dcBiasRemover.setInput(input); + dcBiasRemover.start(); } \ No newline at end of file diff --git a/core/src/signal_path/dsp.h b/core/src/signal_path/dsp.h index 929adb18..850f8549 100644 --- a/core/src/signal_path/dsp.h +++ b/core/src/signal_path/dsp.h @@ -22,6 +22,7 @@ public: void setFFTRate(float rate); dsp::VFO* addVFO(std::string name, float outSampleRate, float bandwidth, float offset); void removeVFO(std::string name); + void setInput(dsp::stream* input); private: struct VFO_t { diff --git a/core/src/signal_path/signal_path.cpp b/core/src/signal_path/signal_path.cpp index 13188e15..2ae0939d 100644 --- a/core/src/signal_path/signal_path.cpp +++ b/core/src/signal_path/signal_path.cpp @@ -3,4 +3,5 @@ namespace sigpath { SignalPath signalPath; VFOManager vfoManager; + SourceManager sourceManager; }; \ No newline at end of file diff --git a/core/src/signal_path/signal_path.h b/core/src/signal_path/signal_path.h index c0d10a73..5b3c79bd 100644 --- a/core/src/signal_path/signal_path.h +++ b/core/src/signal_path/signal_path.h @@ -1,9 +1,12 @@ #pragma once #include #include +#include +#include #include namespace sigpath { SDRPP_EXPORT SignalPath signalPath; SDRPP_EXPORT VFOManager vfoManager; + SDRPP_EXPORT SourceManager sourceManager; }; \ No newline at end of file diff --git a/core/src/signal_path/source.cpp b/core/src/signal_path/source.cpp new file mode 100644 index 00000000..e8412b4b --- /dev/null +++ b/core/src/signal_path/source.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +SourceManager::SourceManager() { + +} + +void SourceManager::registerSource(std::string name, SourceHandler* handler) { + if (sources.find(name) != sources.end()) { + spdlog::error("Tried to register new source with existing name: {0}", name); + return; + } + sources[name] = handler; + sourceNames.push_back(name); +} + +void SourceManager::selectSource(std::string name) { + if (sources.find(name) == sources.end()) { + spdlog::error("Tried to select non existant source: {0}", name); + return; + } + if (selectedName != "") { + sources[selectedName]->deselectHandler(sources[selectedName]->ctx); + } + selectedHandler = sources[name]; + selectedHandler->selectHandler(selectedHandler->ctx); + selectedName = name; + sigpath::signalPath.setInput(selectedHandler->stream); +} + +void SourceManager::showSelectedMenu() { + if (selectedHandler == NULL) { + return; + } + selectedHandler->menuHandler(selectedHandler->ctx); +} + +void SourceManager::start() { + if (selectedHandler == NULL) { + return; + } + selectedHandler->startHandler(selectedHandler->ctx); +} + +void SourceManager::stop() { + if (selectedHandler == NULL) { + return; + } + selectedHandler->stopHandler(selectedHandler->ctx); +} + +void SourceManager::tune(double freq) { + if (selectedHandler == NULL) { + return; + } + selectedHandler->tuneHandler(freq, selectedHandler->ctx); +} \ No newline at end of file diff --git a/core/src/signal_path/source.h b/core/src/signal_path/source.h new file mode 100644 index 00000000..8ad53e15 --- /dev/null +++ b/core/src/signal_path/source.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include +#include +#include + +class SourceManager { +public: + SourceManager(); + + struct SourceHandler { + dsp::stream* stream; + void (*menuHandler)(void* ctx); + void (*selectHandler)(void* ctx); + void (*deselectHandler)(void* ctx); + void (*startHandler)(void* ctx); + void (*stopHandler)(void* ctx); + void (*tuneHandler)(double freq, void* ctx); + void* ctx; + }; + + void registerSource(std::string name, SourceHandler* handler); + void selectSource(std::string name); + void showSelectedMenu(); + void start(); + void stop(); + void tune(double freq); + + std::vector sourceNames; + +private: + std::map sources; + std::string selectedName; + SourceHandler* selectedHandler = NULL; + +}; \ No newline at end of file diff --git a/demo/src/main.cpp b/demo/src/main.cpp index 0201e70d..4ae55798 100644 --- a/demo/src/main.cpp +++ b/demo/src/main.cpp @@ -1,38 +1,51 @@ #include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) - -struct DemoContext_t { - std::string name; +MOD_INFO { + /* Name: */ "demo", + /* Description: */ "Demo module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ "0.1.0" }; -MOD_EXPORT void* _INIT_(mod::API_t* _API, ImGuiContext* imctx, std::string _name) { - DemoContext_t* ctx = new DemoContext_t; - ctx->name = _name; - return ctx; +class DemoModule { +public: + DemoModule(std::string name) { + this->name = name; + gui::menu.registerEntry(name, menuHandler, this); + spdlog::info("DemoModule '{0}': Instance created!", name); + } + + ~DemoModule() { + spdlog::info("DemoModule '{0}': Instance deleted!", name); + } + +private: + static void menuHandler(void* ctx) { + DemoModule* _this = (DemoModule*)ctx; + ImGui::Text("Hi from %s!", _this->name.c_str()); + } + + std::string name; + +}; + +MOD_EXPORT void _INIT_() { + // Do your one time init here } -MOD_EXPORT void _NEW_FRAME_(DemoContext_t* ctx) { - +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + return new DemoModule(name); } -MOD_EXPORT void _DRAW_MENU_(DemoContext_t* ctx) { - ImGui::Text(ctx->name.c_str()); +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (DemoModule*)instance; } -MOD_EXPORT void _HANDLE_EVENT_(DemoContext_t* ctx, int eventId) { - -} - -MOD_EXPORT void _STOP_(DemoContext_t* ctx) { - +MOD_EXPORT void _STOP_() { + // Do your one shutdown here } \ No newline at end of file diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 39e98611..54e0a451 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -7,12 +7,115 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) #define DEEMP_LIST "50µS\00075µS\000none\000" -mod::API_t* API; +MOD_INFO { + /* Name: */ "radio", + /* Description: */ "Radio module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ "0.2.5" +}; -ConfigManager config; -bool firstInit = true; +class RadioModule { +public: + RadioModule(std::string name) { + this->name = name; + demod = 1; + bandWidth = 200000; + bandWidthMin = 100000; + bandWidthMax = 200000; + sigPath.init(name, 200000, 1000); + sigPath.start(); + sigPath.setDemodulator(SigPath::DEMOD_FM, bandWidth); + } + + ~RadioModule() { + // TODO: Implement destructor + } + +private: + static void menuHandler(void* ctx) { + RadioModule* _this = (RadioModule*)ctx; + float menuColumnWidth = 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->demod == 0) && _this->demod != 0) { + _this->demod = 0; + _this->bandWidth = 16000; + _this->bandWidthMin = 8000; + _this->bandWidthMax = 16000; + _this->sigPath.setDemodulator(SigPath::DEMOD_NFM, _this->bandWidth); + } + if (ImGui::RadioButton(CONCAT("WFM##_", _this->name), _this->demod == 1) && _this->demod != 1) { + _this->demod = 1; + _this->bandWidth = 200000; + _this->bandWidthMin = 100000; + _this->bandWidthMax = 200000; + _this->sigPath.setDemodulator(SigPath::DEMOD_FM, _this->bandWidth); + } + ImGui::NextColumn(); + if (ImGui::RadioButton(CONCAT("AM##_", _this->name), _this->demod == 2) && _this->demod != 2) { + _this->demod = 2; + _this->bandWidth = 12500; + _this->bandWidthMin = 6250; + _this->bandWidthMax = 12500; + _this->sigPath.setDemodulator(SigPath::DEMOD_AM, _this->bandWidth); + } + if (ImGui::RadioButton(CONCAT("DSB##_", _this->name), _this->demod == 3) && _this->demod != 3) { + _this->demod = 3; + _this->bandWidth = 6000; + _this->bandWidthMin = 3000; + _this->bandWidthMax = 6000; + _this->sigPath.setDemodulator(SigPath::DEMOD_DSB, _this->bandWidth); + } + ImGui::NextColumn(); + if (ImGui::RadioButton(CONCAT("USB##_", _this->name), _this->demod == 4) && _this->demod != 4) { + _this->demod = 4; + _this->bandWidth = 3000; + _this->bandWidthMin = 1500; + _this->bandWidthMax = 3000; + _this->sigPath.setDemodulator(SigPath::DEMOD_USB, _this->bandWidth); + } + if (ImGui::RadioButton(CONCAT("CW##_", _this->name), _this->demod == 5) && _this->demod != 5) { _this->demod = 5; }; + ImGui::NextColumn(); + if (ImGui::RadioButton(CONCAT("LSB##_", _this->name), _this->demod == 6) && _this->demod != 6) { + _this->demod = 6; + _this->bandWidth = 3000; + _this->bandWidthMin = 1500; + _this->bandWidthMax = 3000; + _this->sigPath.setDemodulator(SigPath::DEMOD_LSB, _this->bandWidth); + } + if (ImGui::RadioButton(CONCAT("RAW##_", _this->name), _this->demod == 7) && _this->demod != 7) { + _this->demod = 7; + _this->bandWidth = 10000; + _this->bandWidthMin = 3000; + _this->bandWidthMax = 10000; + _this->sigPath.setDemodulator(SigPath::DEMOD_RAW, _this->bandWidth); + }; + ImGui::Columns(1, CONCAT("EndRadioModeColumns##_", _this->name), false); + + ImGui::EndGroup(); + + ImGui::Text("WFM Deemphasis"); + ImGui::SameLine(); + ImGui::PushItemWidth(menuColumnWidth - ImGui::GetCursorPosX()); + if (ImGui::Combo(CONCAT("##_deemp_select_", _this->name), &_this->deemp, DEEMP_LIST)) { + _this->sigPath.setDeemphasis(_this->deemp); + } + ImGui::PopItemWidth(); + + ImGui::Text("Bandwidth"); + ImGui::SameLine(); + ImGui::PushItemWidth(menuColumnWidth - ImGui::GetCursorPosX()); + if (ImGui::InputInt(CONCAT("##_bw_select_", _this->name), &_this->bandWidth, 100, 1000)) { + _this->bandWidth = std::clamp(_this->bandWidth, _this->bandWidthMin, _this->bandWidthMax); + _this->sigPath.setBandwidth(_this->bandWidth); + } + ImGui::PopItemWidth(); + } -struct RadioContext_t { std::string name; int demod = 1; int deemp = 0; @@ -20,117 +123,21 @@ struct RadioContext_t { int bandWidthMin; int bandWidthMax; SigPath sigPath; + }; -MOD_EXPORT void* _INIT_(mod::API_t* _API, ImGuiContext* imctx, std::string _name) { - API = _API; - RadioContext_t* ctx = new RadioContext_t; - - ctx->name = _name; - - ctx->demod = 1; - ctx->bandWidth = 200000; - ctx->bandWidthMin = 100000; - ctx->bandWidthMax = 200000; - ctx->sigPath.init(_name, 200000, 1000); - ctx->sigPath.start(); - ctx->sigPath.setDemodulator(SigPath::DEMOD_FM, ctx->bandWidth); - ImGui::SetCurrentContext(imctx); - return ctx; +MOD_EXPORT void _INIT_() { + // Do your one time init here } -MOD_EXPORT void _NEW_FRAME_(RadioContext_t* ctx) { - +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + return new RadioModule(name); } -MOD_EXPORT void _DRAW_MENU_(RadioContext_t* ctx) { - float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); - - ImGui::BeginGroup(); - - // TODO: Change VFO ref in signal path - - ImGui::Columns(4, CONCAT("RadioModeColumns##_", ctx->name), false); - if (ImGui::RadioButton(CONCAT("NFM##_", ctx->name), ctx->demod == 0) && ctx->demod != 0) { - ctx->demod = 0; - ctx->bandWidth = 16000; - ctx->bandWidthMin = 8000; - ctx->bandWidthMax = 16000; - ctx->sigPath.setDemodulator(SigPath::DEMOD_NFM, ctx->bandWidth); - } - if (ImGui::RadioButton(CONCAT("WFM##_", ctx->name), ctx->demod == 1) && ctx->demod != 1) { - ctx->demod = 1; - ctx->bandWidth = 200000; - ctx->bandWidthMin = 100000; - ctx->bandWidthMax = 200000; - ctx->sigPath.setDemodulator(SigPath::DEMOD_FM, ctx->bandWidth); - } - ImGui::NextColumn(); - if (ImGui::RadioButton(CONCAT("AM##_", ctx->name), ctx->demod == 2) && ctx->demod != 2) { - ctx->demod = 2; - ctx->bandWidth = 12500; - ctx->bandWidthMin = 6250; - ctx->bandWidthMax = 12500; - ctx->sigPath.setDemodulator(SigPath::DEMOD_AM, ctx->bandWidth); - } - if (ImGui::RadioButton(CONCAT("DSB##_", ctx->name), ctx->demod == 3) && ctx->demod != 3) { - ctx->demod = 3; - ctx->bandWidth = 6000; - ctx->bandWidthMin = 3000; - ctx->bandWidthMax = 6000; - ctx->sigPath.setDemodulator(SigPath::DEMOD_DSB, ctx->bandWidth); - } - ImGui::NextColumn(); - if (ImGui::RadioButton(CONCAT("USB##_", ctx->name), ctx->demod == 4) && ctx->demod != 4) { - ctx->demod = 4; - ctx->bandWidth = 3000; - ctx->bandWidthMin = 1500; - ctx->bandWidthMax = 3000; - ctx->sigPath.setDemodulator(SigPath::DEMOD_USB, ctx->bandWidth); - } - if (ImGui::RadioButton(CONCAT("CW##_", ctx->name), ctx->demod == 5) && ctx->demod != 5) { ctx->demod = 5; }; - ImGui::NextColumn(); - if (ImGui::RadioButton(CONCAT("LSB##_", ctx->name), ctx->demod == 6) && ctx->demod != 6) { - ctx->demod = 6; - ctx->bandWidth = 3000; - ctx->bandWidthMin = 1500; - ctx->bandWidthMax = 3000; - ctx->sigPath.setDemodulator(SigPath::DEMOD_LSB, ctx->bandWidth); - } - if (ImGui::RadioButton(CONCAT("RAW##_", ctx->name), ctx->demod == 7) && ctx->demod != 7) { - ctx->demod = 7; - ctx->bandWidth = 10000; - ctx->bandWidthMin = 3000; - ctx->bandWidthMax = 10000; - ctx->sigPath.setDemodulator(SigPath::DEMOD_RAW, ctx->bandWidth); - }; - ImGui::Columns(1, CONCAT("EndRadioModeColumns##_", ctx->name), false); - - ImGui::EndGroup(); - - ImGui::Text("WFM Deemphasis"); - ImGui::SameLine(); - ImGui::PushItemWidth(menuColumnWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_deemp_select_", ctx->name), &ctx->deemp, DEEMP_LIST)) { - ctx->sigPath.setDeemphasis(ctx->deemp); - } - ImGui::PopItemWidth(); - - ImGui::Text("Bandwidth"); - ImGui::SameLine(); - ImGui::PushItemWidth(menuColumnWidth - ImGui::GetCursorPosX()); - if (ImGui::InputInt(CONCAT("##_bw_select_", ctx->name), &ctx->bandWidth, 100, 1000)) { - ctx->bandWidth = std::clamp(ctx->bandWidth, ctx->bandWidthMin, ctx->bandWidthMax); - ctx->sigPath.setBandwidth(ctx->bandWidth); - } - ImGui::PopItemWidth(); +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (RadioModule*)instance; } -MOD_EXPORT void _HANDLE_EVENT_(RadioContext_t* ctx, int eventId) { - -} - -MOD_EXPORT void _STOP_(RadioContext_t* ctx) { - API->removeVFO(ctx->name); - delete ctx; +MOD_EXPORT void _STOP_() { + // Do your one shutdown here } \ No newline at end of file diff --git a/radio/src/path.cpp b/radio/src/path.cpp index f0547757..e2a8c87c 100644 --- a/radio/src/path.cpp +++ b/radio/src/path.cpp @@ -1,4 +1,5 @@ #include +#include SigPath::SigPath() { @@ -23,7 +24,7 @@ void SigPath::init(std::string vfoName, uint64_t sampleRate, int blockSize) { this->blockSize = blockSize; this->vfoName = vfoName; - vfo = sigpath::vfoManager.createVFO(vfoName, mod::API_t::REF_CENTER, 0, 200000, 200000, 1000); + vfo = sigpath::vfoManager.createVFO(vfoName, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 1000); _demod = DEMOD_FM; _deemp = DEEMP_50US; @@ -40,8 +41,9 @@ void SigPath::init(std::string vfoName, uint64_t sampleRate, int blockSize) { audioResamp.init(&demod.output, 200000, 48000, 800); deemp.init(&audioResamp.output, 800, 50e-6, 48000); - outputSampleRate = API->registerMonoStream(&deemp.output, vfoName, vfoName, sampleRateChangeHandler, this); - API->setBlockSize(vfoName, audioResamp.getOutputBlockSize()); + + outputSampleRate = audio::registerMonoStream(&deemp.output, vfoName, vfoName, sampleRateChangeHandler, this); + audio::setBlockSize(vfoName, audioResamp.getOutputBlockSize()); setDemodulator(_demod, bandwidth); } @@ -101,7 +103,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(200000, vfo->getOutputBlockSize(), audioBw, audioBw); deemp.bypass = (_deemp == DEEMP_NONE); - vfo->setReference(mod::API_t::REF_CENTER); + vfo->setReference(ImGui::WaterfallVFO::REF_CENTER); demod.start(); } else if (demId == DEMOD_NFM) { @@ -113,7 +115,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(16000, vfo->getOutputBlockSize(), audioBw, audioBw); deemp.bypass = true; - vfo->setReference(mod::API_t::REF_CENTER); + vfo->setReference(ImGui::WaterfallVFO::REF_CENTER); demod.start(); } else if (demId == DEMOD_AM) { @@ -123,7 +125,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(12500, vfo->getOutputBlockSize(), audioBw, audioBw); deemp.bypass = true; - vfo->setReference(mod::API_t::REF_CENTER); + vfo->setReference(ImGui::WaterfallVFO::REF_CENTER); amDemod.start(); } else if (demId == DEMOD_USB) { @@ -134,7 +136,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(6000, vfo->getOutputBlockSize(), audioBw, audioBw); deemp.bypass = true; - vfo->setReference(mod::API_t::REF_LOWER); + vfo->setReference(ImGui::WaterfallVFO::REF_LOWER); ssbDemod.start(); } else if (demId == DEMOD_LSB) { @@ -145,7 +147,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(6000, vfo->getOutputBlockSize(), audioBw, audioBw); deemp.bypass = true; - vfo->setReference(mod::API_t::REF_UPPER); + vfo->setReference(ImGui::WaterfallVFO::REF_UPPER); ssbDemod.start(); } else if (demId == DEMOD_DSB) { @@ -156,7 +158,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(6000, vfo->getOutputBlockSize(), audioBw, audioBw); deemp.bypass = true; - vfo->setReference(mod::API_t::REF_CENTER); + vfo->setReference(ImGui::WaterfallVFO::REF_CENTER); ssbDemod.start(); } else if (demId == DEMOD_RAW) { @@ -165,7 +167,7 @@ void SigPath::setDemodulator(int demId, float bandWidth) { //audioResamp.setInput(&cpx2stereo.output); audioBw = std::min(bandwidth, outputSampleRate / 2.0f); audioResamp.setInputSampleRate(10000, vfo->getOutputBlockSize(), audioBw, audioBw); - vfo->setReference(mod::API_t::REF_LOWER); + vfo->setReference(ImGui::WaterfallVFO::REF_LOWER); cpx2stereo.start(); } else { @@ -249,5 +251,5 @@ void SigPath::start() { demod.start(); audioResamp.start(); deemp.start(); - API->startStream(vfoName); + audio::startStream(vfoName); } \ No newline at end of file