mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	Save before changes
This commit is contained in:
		| @@ -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") | ||||
|   | ||||
| @@ -52,6 +52,13 @@ namespace dsp { | ||||
|             output.setMaxLatency(blockSize * 2); | ||||
|         } | ||||
|  | ||||
|         void setInput(stream<complex_t>* input) { | ||||
|             if (running) { | ||||
|                 return; | ||||
|             } | ||||
|             _in = input; | ||||
|         } | ||||
|  | ||||
|         stream<complex_t> output; | ||||
|         bool bypass; | ||||
|  | ||||
|   | ||||
| @@ -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<float> _data; | ||||
| std::vector<float> 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<uint64_t> freq((uint64_t)90500000); | ||||
| int demod = 1; | ||||
| watcher<float> vfoFreq(92000000.0f); | ||||
| float dummyVolume = 1.0f; | ||||
| float* volume = &dummyVolume; | ||||
| @@ -45,7 +40,6 @@ float fftMin = -70.0f; | ||||
| float fftMax = 0.0f; | ||||
| watcher<float> offset(0.0f, true); | ||||
| watcher<float> bw(8000000.0f, true); | ||||
| int sampleRate = 8000000; | ||||
| bool playing = false; | ||||
| watcher<bool> 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<dsp::complex_t> 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(); | ||||
|  | ||||
|   | ||||
| @@ -28,10 +28,12 @@ | ||||
| #include <config.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
| #include <gui/menus/source.h> | ||||
| #include <gui/menus/display.h> | ||||
| #include <gui/menus/bandplan.h> | ||||
| #include <gui/menus/audio.h> | ||||
| #include <gui/dialogs/credits.h> | ||||
| #include <signal_path/source.h> | ||||
|  | ||||
| #define WINDOW_FLAGS    ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,33 @@ | ||||
| #include <gui/menus/source.h> | ||||
| #include <imgui.h> | ||||
| #include <gui/gui.h> | ||||
| #include <core.h> | ||||
| #include <signal_path/signal_path.h> | ||||
|  | ||||
| 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(); | ||||
|     } | ||||
| } | ||||
| @@ -1,3 +1,4 @@ | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <SoapySDR/Device.hpp> | ||||
| #include <SoapySDR/Modules.hpp> | ||||
|   | ||||
| @@ -29,45 +29,6 @@ | ||||
| #endif | ||||
|  | ||||
| namespace mod { | ||||
|     struct API_t { | ||||
|         dsp::stream<dsp::complex_t>* (*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<float>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx); | ||||
|         float (*registerStereoStream)(dsp::stream<dsp::StereoFloat_t>* 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<float>* (*bindToStreamMono)(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx); | ||||
|         dsp::stream<dsp::StereoFloat_t>* (*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<float>* stream); | ||||
|         void (*unbindFromStreamStereo)(std::string name, dsp::stream<dsp::StereoFloat_t>* stream); | ||||
|         std::vector<std::string> (*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<std::string> moduleNames; | ||||
| }; | ||||
|  | ||||
| extern mod::API_t* API; | ||||
| #define MOD_INFO    MOD_EXPORT const mod::ModuleInfo_t _INFO | ||||
| @@ -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<dsp::complex_t>* input) { | ||||
|     dcBiasRemover.stop(); | ||||
|     dcBiasRemover.setInput(input); | ||||
|     dcBiasRemover.start(); | ||||
| } | ||||
| @@ -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<dsp::complex_t>* input); | ||||
|  | ||||
| private: | ||||
|     struct VFO_t { | ||||
|   | ||||
| @@ -3,4 +3,5 @@ | ||||
| namespace sigpath { | ||||
|     SignalPath signalPath; | ||||
|     VFOManager vfoManager; | ||||
|     SourceManager sourceManager; | ||||
| }; | ||||
| @@ -1,9 +1,12 @@ | ||||
| #pragma once | ||||
| #include <signal_path/dsp.h> | ||||
| #include <signal_path/vfo_manager.h> | ||||
| #include <signal_path/source.h> | ||||
| #include <io/soapy.h> | ||||
| #include <module.h> | ||||
|  | ||||
| namespace sigpath { | ||||
|     SDRPP_EXPORT SignalPath signalPath; | ||||
|     SDRPP_EXPORT VFOManager vfoManager; | ||||
|     SDRPP_EXPORT SourceManager sourceManager; | ||||
| }; | ||||
							
								
								
									
										58
									
								
								core/src/signal_path/source.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								core/src/signal_path/source.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| #include <signal_path/source.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <signal_path/signal_path.h> | ||||
|  | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										37
									
								
								core/src/signal_path/source.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								core/src/signal_path/source.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <map> | ||||
| #include <dsp/stream.h> | ||||
| #include <dsp/types.h> | ||||
|  | ||||
| class SourceManager { | ||||
| public: | ||||
|     SourceManager(); | ||||
|  | ||||
|     struct SourceHandler { | ||||
|         dsp::stream<dsp::complex_t>* 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<std::string> sourceNames; | ||||
|  | ||||
| private: | ||||
|     std::map<std::string, SourceHandler*> sources; | ||||
|     std::string selectedName; | ||||
|     SourceHandler* selectedHandler = NULL; | ||||
|  | ||||
| }; | ||||
| @@ -1,38 +1,51 @@ | ||||
| #include <imgui.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <module.h> | ||||
| #include <watcher.h> | ||||
| #include <dsp/types.h> | ||||
| #include <dsp/stream.h> | ||||
| #include <thread> | ||||
| #include <ctime> | ||||
| #include <stdio.h> | ||||
| #include <gui/style.h> | ||||
| #include <gui/gui.h> | ||||
|  | ||||
| #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 | ||||
| } | ||||
| @@ -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<int>(_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<int>(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 | ||||
| } | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include <path.h> | ||||
| #include <signal_path/audio.h> | ||||
|  | ||||
| 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<float>(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<float>(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<float>(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<float>(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<float>(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<float>(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<float>(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); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user