diff --git a/core/src/core.cpp b/core/src/core.cpp index 160c4bb3..86e89b5d 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -118,6 +118,7 @@ int sdrpp_main(int argc, char *argv[]) { defConfig["fastFFT"] = false; defConfig["fftHeight"] = 300; defConfig["fftSize"] = 65536; + defConfig["fftWindow"] = 0; defConfig["frequency"] = 100000000.0; defConfig["fullWaterfallUpdate"] = false; defConfig["max"] = 0.0; diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index a92e34b5..a346feef 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -40,8 +40,8 @@ void MainWindow::init() { gui::waterfall.init(); gui::waterfall.setRawFFTSize(fftSize); - tempFFT = new float[fftSize]; - FFTdata = new float[fftSize]; + appliedWindow = new float[fftSize]; + generateFFTWindow(selectedWindow, fftSize); credits::init(); @@ -201,21 +201,23 @@ void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) { std::lock_guard lck(_this->fft_mtx); if (count != _this->fftSize) { return; } - memcpy(_this->fft_in, samples, count * sizeof(dsp::complex_t)); - fftwf_execute(_this->fftwPlan); - int half = count / 2; + // Apply window + volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fft_in, (lv_32fc_t*)samples, _this->appliedWindow, count); + // Execute FFT + fftwf_execute(_this->fftwPlan); + + // Get the FFT buffer float* fftBuf = gui::waterfall.getFFTBuffer(); if (fftBuf == NULL) { gui::waterfall.pushFFT(); return; } - volk_32fc_s32f_power_spectrum_32f(_this->tempFFT, (lv_32fc_t*)_this->fft_out, count, count); - - memcpy(fftBuf, &_this->tempFFT[half], half * sizeof(float)); - memcpy(&fftBuf[half], _this->tempFFT, half * sizeof(float)); + // Take power of spectrum + volk_32fc_s32f_power_spectrum_32f(fftBuf, (lv_32fc_t*)_this->fft_out, count, count); + // Push back data gui::waterfall.pushFFT(); } @@ -619,6 +621,30 @@ void MainWindow::setFFTSize(int size) { fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); + + delete appliedWindow; + + appliedWindow = new float[fftSize]; + generateFFTWindow(selectedWindow, fftSize); +} + +void MainWindow::setFFTWindow(int win) { + std::lock_guard lck(fft_mtx); + selectedWindow = win; + generateFFTWindow(selectedWindow, fftSize); +} + +void MainWindow::generateFFTWindow(int win, int size) { + if (win == FFT_WINDOW_RECTANGULAR) { + for (int i = 0; i < size; i++) { + appliedWindow[i] = (i%2) ? 1 : -1; + } + } + else if (win == FFT_WINDOW_BLACKMAN) { + for (int i = 0; i < size; i++) { + appliedWindow[i] = ((i%2) ? dsp::window_function::blackman(i, size) : -dsp::window_function::blackman(i, size))*2; + } + } } bool MainWindow::isPlaying() { diff --git a/core/src/gui/main_window.h b/core/src/gui/main_window.h index ab2f494a..50aca8f2 100644 --- a/core/src/gui/main_window.h +++ b/core/src/gui/main_window.h @@ -11,6 +11,12 @@ #define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground +enum { + FFT_WINDOW_RECTANGULAR, + FFT_WINDOW_BLACKMAN, + _FFT_WINDOW_COUNT +}; + class MainWindow { public: void init(); @@ -18,6 +24,7 @@ public: void setViewBandwidthSlider(float bandwidth); bool sdrIsRunning(); void setFFTSize(int size); + void setFFTWindow(int win); // TODO: Replace with it's own class void setVFO(double freq); @@ -27,6 +34,7 @@ public: bool lockWaterfallControls = false; private: + void generateFFTWindow(int win, int size); static void fftHandler(dsp::complex_t* samples, int count, void* ctx); static void vfoAddedHandler(VFOManager::VFO* vfo, void* ctx); @@ -35,8 +43,7 @@ private: std::mutex fft_mtx; fftwf_complex *fft_in, *fft_out; fftwf_plan fftwPlan; - float* tempFFT; - float* FFTdata; + float* appliedWindow; // GUI Variables bool firstMenuRender = true; @@ -56,6 +63,7 @@ private: int tuningMode = tuner::TUNER_MODE_NORMAL; dsp::stream dummyStream; bool demoWindow = false; + int selectedWindow = 0; EventHandler vfoCreatedHandler; diff --git a/core/src/gui/menus/display.cpp b/core/src/gui/menus/display.cpp index fdf7b7ea..fbb5ddd1 100644 --- a/core/src/gui/menus/display.cpp +++ b/core/src/gui/menus/display.cpp @@ -14,6 +14,7 @@ namespace displaymenu { std::vector colorMapNames; std::string colorMapNamesTxt = ""; std::string colorMapAuthor = ""; + int selectedWindow = 0; const int FFTSizes[] = { 65536, @@ -70,6 +71,8 @@ namespace displaymenu { } gui::mainWindow.setFFTSize(FFTSizes[fftSizeId]); + selectedWindow = std::clamp((int)core::configManager.conf["fftWindow"], 0, _FFT_WINDOW_COUNT-1); + gui::mainWindow.setFFTWindow(selectedWindow); } void draw(void* ctx) { @@ -99,13 +102,23 @@ namespace displaymenu { ImGui::Text("FFT Size"); ImGui::SameLine(); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##test_fft_size", &fftSizeId, FFTSizesStr)) { + if (ImGui::Combo("##sdrpp_fft_size", &fftSizeId, FFTSizesStr)) { gui::mainWindow.setFFTSize(FFTSizes[fftSizeId]); core::configManager.aquire(); core::configManager.conf["fftSize"] = FFTSizes[fftSizeId]; core::configManager.release(true); } + ImGui::Text("FFT Window"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::Combo("##sdrpp_fft_window", &selectedWindow, "Rectangular\0Blackman\0")) { + gui::mainWindow.setFFTWindow(selectedWindow); + core::configManager.aquire(); + core::configManager.conf["fftWindow"] = selectedWindow; + core::configManager.release(true); + } + if (colorMapNames.size() > 0) { ImGui::Text("Color Map"); ImGui::SameLine(); diff --git a/frequency_manager/src/main.cpp b/frequency_manager/src/main.cpp index 41000f20..80bb26ac 100644 --- a/frequency_manager/src/main.cpp +++ b/frequency_manager/src/main.cpp @@ -15,7 +15,7 @@ SDRPP_MOD_INFO { /* Name: */ "frequency_manager", /* Description: */ "Frequency manager module for SDR++", - /* Author: */ "Ryzerth;zimm", + /* Author: */ "Ryzerth;Zimm", /* Version: */ 0, 3, 0, /* Max instances */ 1 }; @@ -125,8 +125,9 @@ private: if (core::modComManager.interfaceExists(vfoName)) { if (core::modComManager.getModuleName(vfoName) == "radio") { int mode = bm.mode; + float bandwidth = bm.bandwidth; core::modComManager.callInterface(vfoName, RADIO_IFACE_CMD_SET_MODE, &mode, NULL); - // TODO: Set bandwidth as well + core::modComManager.callInterface(vfoName, RADIO_IFACE_CMD_SET_BANDWIDTH, &bandwidth, NULL); } } tuner::tune(tuner::TUNER_MODE_NORMAL, vfoName, bm.frequency); @@ -594,6 +595,9 @@ private: } ImGui::BeginTooltip(); + ImGui::Text(hoveredBookmarkName.c_str()); + ImGui::Separator(); + ImGui::Text("Frequency: %s", freqToStr(hoveredBookmark.frequency).c_str()); ImGui::Text("Bandwidth: %s", freqToStr(hoveredBookmark.bandwidth).c_str()); ImGui::Text("Mode: %s", demodModeList[hoveredBookmark.mode]); ImGui::EndTooltip(); diff --git a/radio/src/am_demod.h b/radio/src/am_demod.h index 889c8302..3d7d9082 100644 --- a/radio/src/am_demod.h +++ b/radio/src/am_demod.h @@ -162,8 +162,8 @@ public: } } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); float audioBW = std::min(audioSampRate / 2.0f, bw / 2.0f); @@ -173,6 +173,7 @@ private: resamp.updateWindow(&win); } +private: void setSnapInterval(float snapInt) { snapInterval = snapInt; _vfo->setSnapInterval(snapInterval); diff --git a/radio/src/cw_demod.h b/radio/src/cw_demod.h index fbd76b0a..ddc5f120 100644 --- a/radio/src/cw_demod.h +++ b/radio/src/cw_demod.h @@ -169,12 +169,13 @@ public: } } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); } +private: void setSnapInterval(float snapInt) { snapInterval = snapInt; _vfo->setSnapInterval(snapInterval); diff --git a/radio/src/dsb_demod.h b/radio/src/dsb_demod.h index d8aa72e4..182ef933 100644 --- a/radio/src/dsb_demod.h +++ b/radio/src/dsb_demod.h @@ -162,12 +162,13 @@ public: } } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); } +private: void setSnapInterval(float snapInt) { snapInterval = snapInt; _vfo->setSnapInterval(snapInterval); diff --git a/radio/src/fm_demod.h b/radio/src/fm_demod.h index f59bd0f3..6785322d 100644 --- a/radio/src/fm_demod.h +++ b/radio/src/fm_demod.h @@ -154,14 +154,15 @@ public: } } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); demod.setDeviation(bw / 2.0f); setAudioSampleRate(audioSampRate); } +private: void setSnapInterval(float snapInt) { snapInterval = snapInt; _vfo->setSnapInterval(snapInterval); diff --git a/radio/src/lsb_demod.h b/radio/src/lsb_demod.h index b96fb186..78b57a09 100644 --- a/radio/src/lsb_demod.h +++ b/radio/src/lsb_demod.h @@ -161,8 +161,8 @@ public: } } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); demod.setBandWidth(bw); @@ -173,6 +173,7 @@ private: resamp.updateWindow(&win); } +private: void setSnapInterval(float snapInt) { snapInterval = snapInt; _vfo->setSnapInterval(snapInterval); diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 00857e88..9f84d7a0 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -174,6 +174,10 @@ private: int* _in = (int*)in; if (*_in != _this->demodId) { _this->selectDemodById(*_in); } } + else if (code == RADIO_IFACE_CMD_SET_BANDWIDTH) { + float* _in = (float*)in; + _this->currentDemod->setBandwidth(*_in, true); + } } void selectDemod(Demodulator* demod) { diff --git a/radio/src/radio_demod.h b/radio/src/radio_demod.h index 059b2426..09032d5d 100644 --- a/radio/src/radio_demod.h +++ b/radio/src/radio_demod.h @@ -12,6 +12,7 @@ public: virtual VFOManager::VFO* getVFO() = 0; virtual void setAudioSampleRate(float sampleRate) = 0; virtual float getAudioSampleRate() = 0; + virtual void setBandwidth(float bandWidth, bool updateWaterfall = true) = 0; virtual dsp::stream* getOutput() = 0; virtual void showMenu() = 0; }; \ No newline at end of file diff --git a/radio/src/raw_demod.h b/radio/src/raw_demod.h index a25c156e..1175ad91 100644 --- a/radio/src/raw_demod.h +++ b/radio/src/raw_demod.h @@ -116,6 +116,10 @@ public: } // TODO: Allow selection of the bandwidth + } + + void setBandwidth(float bandWidth, bool updateWaterfall = true) { + // Do nothing } private: diff --git a/radio/src/usb_demod.h b/radio/src/usb_demod.h index 071bea65..8885f3f4 100644 --- a/radio/src/usb_demod.h +++ b/radio/src/usb_demod.h @@ -161,8 +161,8 @@ public: } } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); demod.setBandWidth(bw); @@ -173,6 +173,7 @@ private: resamp.updateWindow(&win); } +private: void setSnapInterval(float snapInt) { snapInterval = snapInt; _vfo->setSnapInterval(snapInterval); diff --git a/radio/src/wfm_demod.h b/radio/src/wfm_demod.h index 542dfec5..bf6ec5d1 100644 --- a/radio/src/wfm_demod.h +++ b/radio/src/wfm_demod.h @@ -192,13 +192,15 @@ public: _vfo->setSnapInterval(snapInterval); } -private: void setBandwidth(float bandWidth, bool updateWaterfall = true) { + bandWidth = std::clamp(bandWidth, bwMin, bwMax); bw = bandWidth; _vfo->setBandwidth(bw, updateWaterfall); demod.setDeviation(bw / 2.0f); } +private: + const float bwMax = 250000; const float bwMin = 50000; const float bbSampRate = 250000; diff --git a/readme.md b/readme.md index 0cab0345..dff62122 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # SDR++, The bloat-free SDR software -![Screenshot](https://i.imgur.com/EFOqwQQ.png) +![Screenshot](https://i.imgur.com/Ter2MQJ.png) SDR++ is a cross-platform and open source SDR software with the aim of being bloat free and simple to use. ![Build](https://github.com/AlexandreRouma/SDRPlusPlus/workflows/Build%20Binaries/badge.svg)