From cb8bbd7ccc4928f1d2126c410fd701a7766e703d Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Mon, 12 Apr 2021 23:02:45 +0200 Subject: [PATCH] New optional detailed FFT system --- core/src/gui/main_window.cpp | 81 ++++++++++++++------- core/src/gui/menus/display.cpp | 5 ++ core/src/gui/widgets/waterfall.cpp | 41 +++++++++-- core/src/gui/widgets/waterfall.h | 4 ++ sdrplay_source/src/main.cpp | 110 ++++++++++++++++++++++++----- 5 files changed, 191 insertions(+), 50 deletions(-) diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 6394ceeb..1c2620b5 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -33,25 +33,25 @@ #include #include -// const int FFTSizes[] = { -// 65536, -// 32768, -// 16384, -// 8192, -// 4096, -// 2048 -// }; +const int FFTSizes[] = { + 65536, + 32768, + 16384, + 8192, + 4096, + 2048, + 1024 +}; -// const char* FFTSizesStr[] = { -// "65536", -// "32768", -// "16384", -// "8192", -// "4096", -// "2048" -// }; +const char* FFTSizesStr = "65536\0" + "32768\0" + "16384\0" + "8192\0" + "4096\0" + "2048\0" + "1024\0"; -// int fftSizeId = 0; +int fftSizeId = 0; int fftSize = 8192 * 8; std::mutex fft_mtx; @@ -63,26 +63,39 @@ char buf[1024]; bool experimentalZoom = false; void fftHandler(dsp::complex_t* samples, int count, void* ctx) { + std::lock_guard lck(fft_mtx); + memcpy(fft_in, samples, count * sizeof(dsp::complex_t)); fftwf_execute(p); int half = count / 2; - volk_32fc_s32f_power_spectrum_32f(FFTdata, (lv_32fc_t*)fft_out, count, count); + // volk_32fc_s32f_power_spectrum_32f(FFTdata, (lv_32fc_t*)fft_out, count, count); - memcpy(tempFFT, &FFTdata[half], half * sizeof(float)); - memmove(&FFTdata[half], FFTdata, half * sizeof(float)); - memcpy(FFTdata, tempFFT, half * sizeof(float)); + // memcpy(tempFFT, &FFTdata[half], half * sizeof(float)); + // memmove(&FFTdata[half], FFTdata, half * sizeof(float)); + // memcpy(FFTdata, tempFFT, half * sizeof(float)); + + // float* fftBuf = gui::waterfall.getFFTBuffer(); + // if (fftBuf == NULL) { + // gui::waterfall.pushFFT(); + // return; + // } + + // memcpy(fftBuf, FFTdata, count * sizeof(float)); + + // gui::waterfall.pushFFT(); float* fftBuf = gui::waterfall.getFFTBuffer(); if (fftBuf == NULL) { gui::waterfall.pushFFT(); return; } - float last = FFTdata[0]; - for (int i = 0; i < count; i++) { - last = (FFTdata[i] * 0.1f) + (last * 0.9f); - fftBuf[i] = last; - } + + volk_32fc_s32f_power_spectrum_32f(tempFFT, (lv_32fc_t*)fft_out, count, count); + + memcpy(fftBuf, &tempFFT[half], half * sizeof(float)); + memcpy(&fftBuf[half], tempFFT, half * sizeof(float)); + gui::waterfall.pushFFT(); } @@ -555,6 +568,22 @@ void drawWindow() { spdlog::error("Will this make the software crash?"); } + if (ImGui::Combo("##test_fft_size", &fftSizeId, FFTSizesStr)) { + std::lock_guard lck(fft_mtx); + fftSize = FFTSizes[fftSizeId]; + + gui::waterfall.setRawFFTSize(fftSize); + sigpath::signalPath.setFFTSize(fftSize); + + fftwf_free(fft_in); + fftwf_free(fft_out); + fftwf_destroy_plan(p); + + fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); + fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); + p = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); + } + ImGui::Spacing(); } diff --git a/core/src/gui/menus/display.cpp b/core/src/gui/menus/display.cpp index a0e3b8e7..fbc21955 100644 --- a/core/src/gui/menus/display.cpp +++ b/core/src/gui/menus/display.cpp @@ -7,6 +7,7 @@ namespace displaymenu { bool showWaterfall; + bool fastFFT = true; int colorMapId = 0; std::vector colorMapNames; std::string colorMapNamesTxt = ""; @@ -55,5 +56,9 @@ namespace displaymenu { } ImGui::Text("Color map Author: %s", colorMapAuthor.c_str()); } + + if (ImGui::Checkbox("Fast FFT", &fastFFT)) { + gui::waterfall.setFastFFT(fastFFT); + } } } \ No newline at end of file diff --git a/core/src/gui/widgets/waterfall.cpp b/core/src/gui/widgets/waterfall.cpp index 11c32f0f..5a54624c 100644 --- a/core/src/gui/widgets/waterfall.cpp +++ b/core/src/gui/widgets/waterfall.cpp @@ -23,7 +23,7 @@ float DEFAULT_COLOR_MAP[][3] = { {0x4A, 0x00, 0x00} }; -void doZoom(int offset, int width, int outWidth, float* data, float* out) { +void doZoom(int offset, int width, int outWidth, float* data, float* out, bool fast) { // NOTE: REMOVE THAT SHIT, IT'S JUST A HACKY FIX if (offset < 0) { offset = 0; @@ -33,8 +33,24 @@ void doZoom(int offset, int width, int outWidth, float* data, float* out) { } float factor = (float)width / (float)outWidth; + + if (fast) { + for (int i = 0; i < outWidth; i++) { + out[i] = data[(int)(offset + ((float)i * factor))]; + } + return; + } + + float id = offset; + float val, maxVal; + float next; for (int i = 0; i < outWidth; i++) { - out[i] = data[(int)(offset + ((float)i * factor))]; + maxVal = -INFINITY; + for (int j = 0; j < factor; j++) { + if (data[(int)id + j] > maxVal) { maxVal = data[(int)id + j]; } + } + out[i] = maxVal; + id += factor; } } @@ -310,6 +326,11 @@ namespace ImGui { } } + void WaterFall::setFastFFT(bool fastFFT) { + std::lock_guard lck(buf_mtx); + _fastFFT = fastFFT; + } + void WaterFall::updateWaterfallFb() { if (!waterfallVisible || rawFFTs == NULL) { return; @@ -326,7 +347,7 @@ namespace ImGui { for (int i = 0; i < count; i++) { drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize; drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2); - doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[((i + currentFFTLine) % waterfallHeight) * rawFFTSize], tempData); + doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[((i + currentFFTLine) % waterfallHeight) * rawFFTSize], tempData, _fastFFT); for (int j = 0; j < dataWidth; j++) { pixel = (std::clamp(tempData[j], waterfallMin, waterfallMax) - waterfallMin) / dataRange; waterfallFb[(i * dataWidth) + j] = waterfallPallet[(int)(pixel * (WATERFALL_RESOLUTION - 1))]; @@ -571,10 +592,18 @@ namespace ImGui { int drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize; int drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2); - + // If in fast mode, apply IIR filtering + float* buf = &rawFFTs[currentFFTLine * rawFFTSize]; + if (_fastFFT) { + float last = buf[0]; + for (int i = 0; i < rawFFTSize; i++) { + last = (buf[i] * 0.1f) + (last * 0.9f); + buf[i] = last; + } + } if (waterfallVisible) { - doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT); + doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT, _fastFFT); memmove(&waterfallFb[dataWidth], waterfallFb, dataWidth * (waterfallHeight - 1) * sizeof(uint32_t)); float pixel; float dataRange = waterfallMax - waterfallMin; @@ -586,7 +615,7 @@ namespace ImGui { waterfallUpdate = true; } else { - doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs, latestFFT); + doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs, latestFFT, _fastFFT); fftLines = 1; } diff --git a/core/src/gui/widgets/waterfall.h b/core/src/gui/widgets/waterfall.h index 567805ff..8b811d86 100644 --- a/core/src/gui/widgets/waterfall.h +++ b/core/src/gui/widgets/waterfall.h @@ -105,6 +105,8 @@ namespace ImGui { void setRawFFTSize(int size, bool lock = true); + void setFastFFT(bool fastFFT); + bool centerFreqMoved = false; bool vfoFreqChanged = false; bool bandplanEnabled = false; @@ -203,5 +205,7 @@ namespace ImGui { bool waterfallVisible = true; bool bandplanVisible = false; + + bool _fastFFT = true; }; }; \ No newline at end of file diff --git a/sdrplay_source/src/main.cpp b/sdrplay_source/src/main.cpp index 7ff824a0..7fb1990b 100644 --- a/sdrplay_source/src/main.cpp +++ b/sdrplay_source/src/main.cpp @@ -102,6 +102,8 @@ const sdrplay_api_AgcControlT agcModes[] = { sdrplay_api_AGC_100HZ }; +const char* rspduo_antennaPortsTxt = "Tuner 1 (50Ohm)\0Tuner 1 (Hi-Z)\0Tuner 2 (50Ohm)\0"; + const char* agcModesTxt = "Off\0005Hz\00050Hz\000100Hz"; class SDRPlaySourceModule : public ModuleManager::Instance { @@ -235,6 +237,7 @@ public: sdrplay_api_ReleaseDevice(&openDev); } + openDev.rspDuoMode = sdrplay_api_RspDuoMode_Single_Tuner; err = sdrplay_api_SelectDevice(&openDev); if (err != sdrplay_api_Success) { const char* errStr = sdrplay_api_GetErrorString(err); @@ -304,8 +307,10 @@ public: config.conf["devices"][selectedName]["biast"] = false; } else if (openDev.hwVer == SDRPLAY_RSPduo_ID) { + config.conf["devices"][selectedName]["antenna"] = 0; config.conf["devices"][selectedName]["fmNotch"] = false; config.conf["devices"][selectedName]["dabNotch"] = false; + config.conf["devices"][selectedName]["amNotch"] = false; config.conf["devices"][selectedName]["biast"] = false; } else if (openDev.hwVer == SDRPLAY_RSPdx_ID) { @@ -371,12 +376,18 @@ public: } } else if (openDev.hwVer == SDRPLAY_RSPduo_ID) { + if (config.conf["devices"][selectedName].contains("antenna")) { + rspduo_antennaPort = config.conf["devices"][selectedName]["antenna"]; + } if (config.conf["devices"][selectedName].contains("fmNotch")) { rspduo_fmNotch = config.conf["devices"][selectedName]["fmNotch"]; } if (config.conf["devices"][selectedName].contains("dabNotch")) { rspduo_dabNotch = config.conf["devices"][selectedName]["dabNotch"]; } + if (config.conf["devices"][selectedName].contains("amNotch")) { + rspduo_amNotch = config.conf["devices"][selectedName]["amNotch"]; + } if (config.conf["devices"][selectedName].contains("biast")) { rspduo_biasT = config.conf["devices"][selectedName]["biast"]; } @@ -403,9 +414,18 @@ public: deviceOpen = true; } - void selectShittyTuner(sdrplay_api_TunerSelectT tuner, sdrplay_api_RspDuo_AmPortSelectT amPort) { - // What the fuck? + void rspDuoSelectTuner(sdrplay_api_TunerSelectT tuner, sdrplay_api_RspDuo_AmPortSelectT amPort) { if (openDev.tuner != tuner) { sdrplay_api_SwapRspDuoActiveTuner(openDev.dev, &openDev.tuner, amPort); } + + // NOTE: Might need to select either A or B + openDevParams->rxChannelA->rspDuoTunerParams.tuner1AmPortSel = amPort; + sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_AmPortSelect, sdrplay_api_Update_Ext1_None); + } + + void rspDuoSelectAntennaPort(int port) { + if (port == 0) { rspDuoSelectTuner(sdrplay_api_Tuner_A, sdrplay_api_RspDuo_AMPORT_1); } + if (port == 1) { rspDuoSelectTuner(sdrplay_api_Tuner_A, sdrplay_api_RspDuo_AMPORT_2); } + if (port == 2) { rspDuoSelectTuner(sdrplay_api_Tuner_B, sdrplay_api_RspDuo_AMPORT_2); } } private: @@ -446,19 +466,6 @@ private: _this->bufferIndex = 0; _this->bufferSize = 8000000 / 200; - // General options - _this->openDevParams->devParams->fsFreq.fsHz = _this->sampleRate; - _this->openDevParams->rxChannelA->tunerParams.bwType = _this->bandwidth; - _this->openDevParams->rxChannelA->tunerParams.rfFreq.rfHz = _this->freq; - _this->openDevParams->rxChannelA->tunerParams.gain.gRdB = _this->gain; - _this->openDevParams->rxChannelA->tunerParams.gain.LNAstate = _this->lnaGain; - _this->openDevParams->rxChannelA->ctrlParams.agc.enable = agcModes[_this->agc]; - sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Dev_Fs, sdrplay_api_Update_Ext1_None); - sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_BwType, sdrplay_api_Update_Ext1_None); - sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_Frf, sdrplay_api_Update_Ext1_None); - sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_Gr, sdrplay_api_Update_Ext1_None); - sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Ctrl_Agc, sdrplay_api_Update_Ext1_None); - // RSP1A Options if (_this->openDev.hwVer == SDRPLAY_RSP1A_ID) { _this->openDevParams->devParams->rsp1aParams.rfNotchEnable = _this->rsp1a_fmNotch; @@ -479,7 +486,16 @@ private: sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Rsp2_AmPortSelect, sdrplay_api_Update_Ext1_None); } else if (_this->openDev.hwVer == SDRPLAY_RSPduo_ID) { - + // NOTE: mmight require setting it on both RXA and RXB + _this->rspDuoSelectAntennaPort(_this->rspduo_antennaPort); + _this->openDevParams->rxChannelA->rspDuoTunerParams.biasTEnable = _this->rspduo_biasT; + _this->openDevParams->rxChannelA->rspDuoTunerParams.rfNotchEnable = _this->rspduo_fmNotch; + _this->openDevParams->rxChannelA->rspDuoTunerParams.rfDabNotchEnable = _this->rspduo_dabNotch; + _this->openDevParams->rxChannelA->rspDuoTunerParams.tuner1AmNotchEnable = _this->rspduo_amNotch; + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_RspDuo_BiasTControl, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_RspDuo_RfNotchControl, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_RspDuo_RfDabNotchControl, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_RspDuo_Tuner1AmNotchControl, sdrplay_api_Update_Ext1_None); } else if (_this->openDev.hwVer == SDRPLAY_RSPdx_ID) { _this->openDevParams->devParams->rspDxParams.rfNotchEnable = _this->rspdx_fmNotch; @@ -492,6 +508,26 @@ private: sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_None, sdrplay_api_Update_RspDx_AntennaControl); } + // General options + _this->openDevParams->devParams->fsFreq.fsHz = _this->sampleRate; + _this->openDevParams->rxChannelA->tunerParams.bwType = _this->bandwidth; + _this->openDevParams->rxChannelA->tunerParams.rfFreq.rfHz = _this->freq; + _this->openDevParams->rxChannelA->tunerParams.gain.gRdB = _this->gain; + _this->openDevParams->rxChannelA->tunerParams.gain.LNAstate = _this->lnaGain; + _this->openDevParams->rxChannelA->ctrlParams.agc.enable = agcModes[_this->agc]; + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Dev_Fs, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_BwType, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_Frf, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_Gr, sdrplay_api_Update_Ext1_None); + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Ctrl_Agc, sdrplay_api_Update_Ext1_None); + + // Bandwidth + _this->bandwidth = (_this->bandwidthId == 8) ? preferedBandwidth[_this->srId] : bandwidths[_this->bandwidthId]; + if (_this->running) { + _this->openDevParams->rxChannelA->tunerParams.bwType = _this->bandwidth; + sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_BwType, sdrplay_api_Update_Ext1_None); + } + _this->running = true; spdlog::info("SDRPlaySourceModule '{0}': Start!", _this->name); } @@ -673,7 +709,6 @@ private: config.conf["devices"][selectedName]["antenna"] = rsp2_antennaPort; config.release(true); } - if (ImGui::Checkbox(CONCAT("MW/FM Notch##sdrplay_rsp2_notch", name), &rsp2_notch)) { openDevParams->rxChannelA->rsp2TunerParams.rfNotchEnable = rsp2_notch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_Rsp2_RfNotchControl, sdrplay_api_Update_Ext1_None); @@ -691,7 +726,44 @@ private: } void RSPduoMenu(float menuWidth) { - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Device currently unsupported"); + ImGui::Text("Antenna"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + + if (ImGui::Combo(CONCAT("##sdrplay_rspduo_ant", name), &rspduo_antennaPort, rspduo_antennaPortsTxt)) { + rspDuoSelectAntennaPort(rspduo_antennaPort); + config.aquire(); + config.conf["devices"][selectedName]["antenna"] = rspduo_antennaPort; + config.release(true); + } + if (ImGui::Checkbox(CONCAT("FM Notch##sdrplay_rspduo_notch", name), &rspduo_fmNotch)) { + openDevParams->rxChannelA->rspDuoTunerParams.rfNotchEnable = rspduo_fmNotch; + sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_RfNotchControl, sdrplay_api_Update_Ext1_None); + config.aquire(); + config.conf["devices"][selectedName]["fmNotch"] = rspduo_fmNotch; + config.release(true); + } + if (ImGui::Checkbox(CONCAT("DAB Notch##sdrplay_rspduo_dabnotch", name), &rspduo_dabNotch)) { + openDevParams->rxChannelA->rspDuoTunerParams.rfDabNotchEnable = rspduo_dabNotch; + sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_RfDabNotchControl, sdrplay_api_Update_Ext1_None); + config.aquire(); + config.conf["devices"][selectedName]["dabNotch"] = rspduo_dabNotch; + config.release(true); + } + if (ImGui::Checkbox(CONCAT("AM Notch##sdrplay_rspduo_dabnotch", name), &rspduo_amNotch)) { + openDevParams->rxChannelA->rspDuoTunerParams.tuner1AmNotchEnable = rspduo_amNotch; + sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_Tuner1AmNotchControl, sdrplay_api_Update_Ext1_None); + config.aquire(); + config.conf["devices"][selectedName]["amNotch"] = rspduo_amNotch; + config.release(true); + } + if (ImGui::Checkbox(CONCAT("Bias-T##sdrplay_rspduo_biast", name), &rspduo_biasT)) { + openDevParams->rxChannelA->rspDuoTunerParams.biasTEnable = rspduo_biasT; + sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_BiasTControl, sdrplay_api_Update_Ext1_None); + config.aquire(); + config.conf["devices"][selectedName]["biast"] = rspduo_biasT; + config.release(true); + } } void RSPdxMenu(float menuWidth) { @@ -796,6 +868,8 @@ private: bool rspduo_fmNotch = false; bool rspduo_dabNotch = false; bool rspduo_biasT = false; + bool rspduo_amNotch = false; + int rspduo_antennaPort = 0; // RSPdx Options bool rspdx_fmNotch = false;