diff --git a/core/src/imgui/imgui.h b/core/src/imgui/imgui.h index b6c91a03..db6a16a5 100644 --- a/core/src/imgui/imgui.h +++ b/core/src/imgui/imgui.h @@ -481,6 +481,7 @@ namespace ImGui // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders + IMGUI_API bool SliderFloatWithSteps(const char* label, float* v, float v_min, float v_max, float v_step, const char* display_format = "%.3f"); IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); diff --git a/core/src/imgui/imgui_widgets.cpp b/core/src/imgui/imgui_widgets.cpp index 6961d80b..aa1d72b4 100644 --- a/core/src/imgui/imgui_widgets.cpp +++ b/core/src/imgui/imgui_widgets.cpp @@ -2702,6 +2702,25 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } +bool ImGui::SliderFloatWithSteps(const char* label, float* v, float v_min, float v_max, float v_step, const char* display_format) +{ + if (!display_format) + display_format = "%.3f"; + + char text_buf[64] = {}; + ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), display_format, *v); + + // Map from [v_min,v_max] to [0,N] + const int countValues = int((v_max-v_min)/v_step); + int v_i = int((*v - v_min)/v_step); + const bool value_changed = ImGui::SliderInt(label, &v_i, 0, countValues, text_buf); + + // Remap from [0,N] to [v_min,v_max] + *v = v_min + float(v_i) * v_step; + return value_changed; +} + + bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); diff --git a/soapy_source/src/main.cpp b/soapy_source/src/main.cpp index d88d0433..e7949b7f 100644 --- a/soapy_source/src/main.cpp +++ b/soapy_source/src/main.cpp @@ -67,6 +67,14 @@ public: bool isEnabled() { return enabled; } + + template + std::string to_string_with_precision(const T a_value, const int n = 6) { + std::ostringstream out; + out.precision(n); + out << std::fixed << a_value; + return out.str(); + } private: void refresh() { @@ -79,6 +87,21 @@ private: i++; } } + + float selectBwBySr(double samplerate) { + float cur = bandwidthList[1]; + std::vector bwListReversed = bandwidthList; + std::reverse(bwListReversed.begin(), bwListReversed.end()); + for(auto bw : bwListReversed) { + if(bw >= samplerate) { + cur = bw; + } else { + break; + } + } + spdlog::info("Bandwidth for samplerate {0} is {1}", samplerate, cur); + return cur; + } void selectSampleRate(double samplerate) { spdlog::info("Setting sample rate to {0}", samplerate); @@ -131,19 +154,41 @@ private: gainList = dev->listGains(SOAPY_SDR_RX, channelId); delete[] uiGains; uiGains = new float[gainList.size()]; + for (auto gain : gainList) { gainRanges.push_back(dev->getGainRange(SOAPY_SDR_RX, channelId, gain)); } + + SoapySDR::RangeList bandwidthRange = dev->getBandwidthRange(SOAPY_SDR_RX, channelId); + + txtBwList = ""; + + bandwidthList.push_back(-1); + txtBwList += "Auto"; + txtBwList += '\0'; + + for(auto bwr : bandwidthRange) { + float bw = bwr.minimum(); + bandwidthList.push_back(bw); + if (bw > 1.0e3 && bw <= 1.0e6) { + txtBwList += to_string_with_precision((bw / 1.0e3), 2) + " kHz"; + } else if (bw > 1.0e6) { + txtBwList += to_string_with_precision((bw / 1.0e6), 2) + " MHz"; + } else { + txtBwList += to_string_with_precision(bw, 0); + } + txtBwList += '\0'; + } sampleRates = dev->listSampleRates(SOAPY_SDR_RX, channelId); txtSrList = ""; for (double sr : sampleRates) { if (sr > 1.0e3 && sr <= 1.0e6) { - txtSrList += std::to_string((sr / 1.0e3)) + " kHz"; + txtSrList += to_string_with_precision((sr / 1.0e3), 2) + " kHz"; } else if (sr > 1.0e6) { - txtSrList += std::to_string((sr / 1.0e6)) + " MHz"; + txtSrList += to_string_with_precision((sr / 1.0e6), 2) + " MHz"; } else { - txtSrList += std::to_string((int) sr); + txtSrList += to_string_with_precision(sr, 0); } txtSrList += '\0'; } @@ -164,6 +209,11 @@ private: } i++; } + if(config.conf["devices"][name].contains("bandwidth")) { + uiBandwidthId = config.conf["devices"][name]["bandwidth"]; + } else if(bandwidthList.size() > 1) { + uiBandwidthId = bandwidthList[0]; + } if (hasAgc && config.conf["devices"][name].contains("agc")) { agc = config.conf["devices"][name]["agc"]; } @@ -183,6 +233,8 @@ private: uiGains[i] = gainRanges[i].minimum(); i++; } + if(bandwidthList.size() > 1) + uiBandwidthId = bandwidthList[0]; if (hasAgc) { agc = false; } @@ -200,6 +252,8 @@ private: conf["gains"][gain] = uiGains[i]; i++; } + if(bandwidthList.size() > 1) + conf["bandwidth"] = uiBandwidthId; if (hasAgc) { conf["agc"] = agc; } @@ -233,6 +287,12 @@ private: _this->dev->setGain(SOAPY_SDR_RX, _this->channelId, gain, _this->uiGains[i]); i++; } + if(_this->bandwidthList.size() > 1) { + if(_this->bandwidthList[_this->uiBandwidthId] == -1) + _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId])); + else + _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->bandwidthList[_this->uiBandwidthId]); + } if (_this->hasAgc) { _this->dev->setGainMode(SOAPY_SDR_RX, _this->channelId, _this->agc); @@ -291,6 +351,8 @@ private: if (ImGui::Combo(CONCAT("##_sr_select_", _this->name), &_this->srId, _this->txtSrList.c_str())) { _this->selectSampleRate(_this->sampleRates[_this->srId]); + if(_this->bandwidthList.size() > 1 && _this->running && _this->bandwidthList[_this->uiBandwidthId] == -1) + _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId])); _this->saveCurrent(); } @@ -334,8 +396,13 @@ private: ImGui::SameLine(); ImGui::SetCursorPosX(gainNameLen); ImGui::SetNextItemWidth(menuWidth - gainNameLen); - if (ImGui::SliderFloat((gain + std::string("##_gain_sel_") + _this->name).c_str(), &_this->uiGains[i], - _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum())) { + float step = _this->gainRanges[i].step(); + bool res; + if(step == 0.0f) + res = ImGui::SliderFloat((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum()); + else + res = ImGui::SliderFloatWithSteps((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum(), step); + if(res) { if (_this->running) { _this->dev->setGain(SOAPY_SDR_RX, _this->channelId, gain, _this->uiGains[i]); } @@ -343,6 +410,23 @@ private: } i++; } + if(_this->bandwidthList.size() > 1) { + float bwLen = ImGui::CalcTextSize("Bandwidth").x + 5.0f; + ImGui::Text("Bandwidth"); + ImGui::SameLine(); + ImGui::SetCursorPosX(bwLen); + ImGui::SetNextItemWidth(menuWidth - bwLen); + + if (ImGui::Combo(CONCAT("##_bw_select_", _this->name), &_this->uiBandwidthId, _this->txtBwList.c_str())) { + if(_this->running) { + if(_this->bandwidthList[_this->uiBandwidthId] == -1) + _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId])); + else + _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->bandwidthList[_this->uiBandwidthId]); + } + _this->saveCurrent(); + } + } } static void _worker(SoapyModule* _this) { @@ -383,6 +467,9 @@ private: int channelId = 0; std::vector gainList; std::vector gainRanges; + int uiBandwidthId = 0; + std::vector bandwidthList; + std::string txtBwList; }; MOD_EXPORT void _INIT_() { @@ -405,4 +492,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { MOD_EXPORT void _END_() { config.disableAutoSave(); config.save(); -} \ No newline at end of file +}