diff --git a/core/src/gui/widgets/stepped_slider.cpp b/core/src/gui/widgets/stepped_slider.cpp new file mode 100644 index 00000000..be2f7c23 --- /dev/null +++ b/core/src/gui/widgets/stepped_slider.cpp @@ -0,0 +1,23 @@ +#include +#include +#include + +namespace ImGui { + bool 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; + } +} diff --git a/core/src/gui/widgets/stepped_slider.h b/core/src/gui/widgets/stepped_slider.h new file mode 100644 index 00000000..06aefe6e --- /dev/null +++ b/core/src/gui/widgets/stepped_slider.h @@ -0,0 +1,5 @@ +#pragma once + +namespace ImGui { + bool SliderFloatWithSteps(const char* label, float* v, float v_min, float v_max, float v_step, const char* display_format = "%.3f"); +} diff --git a/soapy_source/src/main.cpp b/soapy_source/src/main.cpp index d88d0433..5a206e4d 100644 --- a/soapy_source/src/main.cpp +++ b/soapy_source/src/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,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 +88,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 +155,42 @@ private: gainList = dev->listGains(SOAPY_SDR_RX, channelId); delete[] uiGains; uiGains = new float[gainList.size()]; + gainRanges.clear(); + 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.clear(); + 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 +211,11 @@ private: } i++; } + if(config.conf["devices"][name].contains("bandwidth")) { + uiBandwidthId = config.conf["devices"][name]["bandwidth"]; + } else if(bandwidthList.size() > 2) { + uiBandwidthId = 0; + } if (hasAgc && config.conf["devices"][name].contains("agc")) { agc = config.conf["devices"][name]["agc"]; } @@ -183,6 +235,8 @@ private: uiGains[i] = gainRanges[i].minimum(); i++; } + if(bandwidthList.size() > 2) + uiBandwidthId = 0; if (hasAgc) { agc = false; } @@ -200,6 +254,8 @@ private: conf["gains"][gain] = uiGains[i]; i++; } + if(bandwidthList.size() > 2) + conf["bandwidth"] = uiBandwidthId; if (hasAgc) { conf["agc"] = agc; } @@ -233,6 +289,12 @@ private: _this->dev->setGain(SOAPY_SDR_RX, _this->channelId, gain, _this->uiGains[i]); i++; } + if(_this->bandwidthList.size() > 2) { + 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 +353,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() > 2 && _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 +398,14 @@ 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 +413,23 @@ private: } i++; } + if(_this->bandwidthList.size() > 2) { + 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 +470,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 +495,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { MOD_EXPORT void _END_() { config.disableAutoSave(); config.save(); -} \ No newline at end of file +}