diff --git a/bladerf_source/src/main.cpp b/bladerf_source/src/main.cpp index aae03f69..f216d89d 100644 --- a/bladerf_source/src/main.cpp +++ b/bladerf_source/src/main.cpp @@ -195,7 +195,8 @@ private: // Setup device parameters bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(0), _this->sampleRate, NULL); bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(0), _this->freq); - bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(0), (_this->bwId == _this->bandwidths.size()) ? _this->sampleRate : _this->bandwidths[_this->bwId], NULL); + bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(0), (_this->bwId == _this->bandwidths.size()) ? + std::clamp(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL); bladerf_set_gain_mode(_this->openDev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MANUAL); bladerf_set_gain(_this->openDev, BLADERF_CHANNEL_RX(0), _this->testGain); @@ -280,7 +281,8 @@ private: ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); if (ImGui::Combo(CONCAT("##_balderf_bw_sel_", _this->name), &_this->bwId, _this->bandwidthsTxt.c_str())) { if (_this->running) { - bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(0), (_this->bwId == _this->bandwidths.size()) ? _this->sampleRate : _this->bandwidths[_this->bwId], NULL); + bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(0), (_this->bwId == _this->bandwidths.size()) ? + std::clamp(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL); } // Save config } diff --git a/core/src/core.cpp b/core/src/core.cpp index be81eccc..9bd5e18a 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -174,6 +174,8 @@ int sdrpp_main(int argc, char *argv[]) { defConfig["vfoOffsets"] = json::object(); + defConfig["vfoColors"]["Radio"] = "#FFFFFF"; + #ifdef _WIN32 defConfig["modulesDirectory"] = "./modules"; defConfig["resourcesDirectory"] = "./res"; @@ -193,7 +195,7 @@ int sdrpp_main(int argc, char *argv[]) { // Fix missing elements in config for (auto const& item : defConfig.items()) { if (!core::configManager.conf.contains(item.key())) { - spdlog::warn("Missing key in config {0}, repairing", item.key()); + spdlog::info("Missing key in config {0}, repairing", item.key()); core::configManager.conf[item.key()] = defConfig[item.key()]; } } @@ -202,7 +204,7 @@ int sdrpp_main(int argc, char *argv[]) { auto items = core::configManager.conf.items(); for (auto const& item : items) { if (!defConfig.contains(item.key())) { - spdlog::warn("Unused key in config {0}, repairing", item.key()); + spdlog::info("Unused key in config {0}, repairing", item.key()); core::configManager.conf.erase(item.key()); } } diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 6dcb31fb..383d65e6 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -32,6 +32,7 @@ #include #include #include +#include int fftSize = 8192 * 8; @@ -135,6 +136,7 @@ void windowInit() { gui::menu.registerEntry("Scripting", scriptingmenu::draw, NULL); gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL); gui::menu.registerEntry("Display", displaymenu::draw, NULL); + gui::menu.registerEntry("VFO Color", vfo_color_menu::draw, NULL); gui::freqSelect.init(); @@ -214,6 +216,7 @@ void windowInit() { scriptingmenu::init(); bandplanmenu::init(); displaymenu::init(); + vfo_color_menu::init(); // TODO for 0.2.5 // Add "select file" option for the file source diff --git a/core/src/gui/menus/vfo_color.cpp b/core/src/gui/menus/vfo_color.cpp new file mode 100644 index 00000000..ab930315 --- /dev/null +++ b/core/src/gui/menus/vfo_color.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace vfo_color_menu { + std::map vfoColors; + std::string openName = ""; + EventHandler vfoAddHndl; + + void vfoAddHandler(VFOManager::VFO* vfo, void* ctx) { + std::string name = vfo->getName(); + if (vfoColors.find(name) != vfoColors.end()) { + ImVec4 col = vfoColors[name]; + vfo->setColor(IM_COL32((int)roundf(col.x * 255), (int)roundf(col.y * 255), (int)roundf(col.z * 255), 50)); + return; + } + vfo->setColor(IM_COL32(255, 255, 255, 50)); + vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + } + + void init() { + // Load colors from config + bool modified = false; + core::configManager.aquire(); + json conf = core::configManager.conf["vfoColors"]; + for (auto& [name, val] : conf.items()) { + // If not a string, repair with default + if (!val.is_string()) { + core::configManager.conf["vfoColors"][name] = "#FFFFFF"; + vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + modified = true; + if (sigpath::vfoManager.vfoExists(name)) { + sigpath::vfoManager.setColor(name, IM_COL32(255, 255, 255, 50)); + } + continue; + } + + // If not a valid hex color, repair with default + std::string col = val; + if (col[0] != '#' || !std::all_of(col.begin() + 1, col.end(), ::isxdigit)) { + core::configManager.conf["vfoColors"][name] = "#FFFFFF"; + vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + modified = true; + if (sigpath::vfoManager.vfoExists(name)) { + sigpath::vfoManager.setColor(name, IM_COL32(255, 255, 255, 50)); + } + continue; + } + + // Since the color is valid, decode it and set the vfo's color + float r, g, b; + r = std::stoi(col.substr(1, 2), NULL, 16); + g = std::stoi(col.substr(3, 2), NULL, 16); + b = std::stoi(col.substr(5, 2), NULL, 16); + vfoColors[name] = ImVec4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f); + if (sigpath::vfoManager.vfoExists(name)) { + sigpath::vfoManager.setColor(name, IM_COL32((int)roundf(r), (int)roundf(g), (int)roundf(b), 50)); + } + } + + // Iterate existing VFOs and set their color if in the config, if not set to default + for (auto& [name, vfo] : gui::waterfall.vfos) { + if (vfoColors.find(name) == vfoColors.end()) { + vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + vfo->color = IM_COL32(255, 255, 255, 50); + modified = true; + } + } + + vfoAddHndl.handler = vfoAddHandler; + sigpath::vfoManager.vfoCreatedEvent.bindHandler(vfoAddHndl); + core::configManager.release(modified); + } + + void draw(void* ctx) { + ImGui::BeginTable("VFO Color Buttons Table", 2); + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + if (ImGui::Button("Auto Color##vfo_color", ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) { + float delta = 1.0f / (float)gui::waterfall.vfos.size(); + float hue = 0; + for (auto& [name, vfo] : gui::waterfall.vfos) { + float r, g, b; + ImGui::ColorConvertHSVtoRGB(hue, 0.5f, 1.0f, r, g, b); + vfoColors[name] = ImVec4(r, g, b, 1.0f); + vfo->color = IM_COL32((int)roundf(r * 255), (int)roundf(g * 255), (int)roundf(b * 255), 50); + hue += delta; + core::configManager.aquire(); + char buf[16]; + sprintf(buf, "#%02X%02X%02X", (int)roundf(r * 255), (int)roundf(g * 255), (int)roundf(b * 255)); + core::configManager.conf["vfoColors"][name] = buf; + core::configManager.release(true); + } + } + + ImGui::TableSetColumnIndex(1); + if (ImGui::Button("Clear All##vfo_color", ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) { + for (auto& [name, vfo] : gui::waterfall.vfos) { + vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + vfo->color = IM_COL32(255, 255, 255, 50); + core::configManager.aquire(); + char buf[16]; + core::configManager.conf["vfoColors"][name] = "#FFFFFF"; + core::configManager.release(true); + } + } + + ImGui::EndTable(); + + ImGui::BeginTable("VFO Color table", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders); + for (auto& [name, vfo] : gui::waterfall.vfos) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImVec4 col(1.0f, 1.0f, 1.0f, 1.0f); + if (vfoColors.find(name) != vfoColors.end()) { + col = vfoColors[name]; + } + if (ImGui::ColorEdit3(("##vfo_color_"+name).c_str(), (float*)&col, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) { + vfoColors[name] = col; + vfo->color = IM_COL32((int)roundf(col.x * 255), (int)roundf(col.y * 255), (int)roundf(col.z * 255), 50); + core::configManager.aquire(); + char buf[16]; + sprintf(buf, "#%02X%02X%02X", (int)roundf(col.x * 255), (int)roundf(col.y * 255), (int)roundf(col.z * 255)); + core::configManager.conf["vfoColors"][name] = buf; + core::configManager.release(true); + } + ImGui::SameLine(); + ImGui::Text(name.c_str()); + } + ImGui::EndTable(); + } +} \ No newline at end of file diff --git a/core/src/gui/menus/vfo_color.h b/core/src/gui/menus/vfo_color.h new file mode 100644 index 00000000..2792986b --- /dev/null +++ b/core/src/gui/menus/vfo_color.h @@ -0,0 +1,6 @@ +#pragma once + +namespace vfo_color_menu { + void init(); + void draw(void* ctx); +} \ No newline at end of file diff --git a/core/src/gui/widgets/waterfall.cpp b/core/src/gui/widgets/waterfall.cpp index 28f5008f..6ba19287 100644 --- a/core/src/gui/widgets/waterfall.cpp +++ b/core/src/gui/widgets/waterfall.cpp @@ -194,7 +194,7 @@ namespace ImGui { if (IS_IN_AREA(mPos, wfMin, wfMax)) { for (auto const& [name, vfo] : vfos) { - window->DrawList->AddRectFilled(vfo->wfRectMin, vfo->wfRectMax, IM_COL32(255, 255, 255, 50)); + window->DrawList->AddRectFilled(vfo->wfRectMin, vfo->wfRectMax, vfo->color); window->DrawList->AddLine(vfo->wfLineMin, vfo->wfLineMax, (name == selectedVFO) ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255)); } } @@ -1115,7 +1115,7 @@ namespace ImGui { } void WaterfallVFO::draw(ImGuiWindow* window, bool selected) { - window->DrawList->AddRectFilled(rectMin, rectMax, IM_COL32(255, 255, 255, 50)); + window->DrawList->AddRectFilled(rectMin, rectMax, color); if (lineVisible) { window->DrawList->AddLine(lineMin, lineMax, selected ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255)); } @@ -1128,7 +1128,7 @@ namespace ImGui { if (reference != REF_UPPER && !bandwidthLocked) { if (IS_IN_AREA(mousePos, rbwSelMin, rbwSelMax)) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } else if (IS_IN_AREA(mousePos, wfRbwSelMin, wfRbwSelMax)) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } - } + } }; void WaterFall::showWaterfall() { diff --git a/core/src/gui/widgets/waterfall.h b/core/src/gui/widgets/waterfall.h index c4ddc0e7..c1e35a4f 100644 --- a/core/src/gui/widgets/waterfall.h +++ b/core/src/gui/widgets/waterfall.h @@ -61,6 +61,8 @@ namespace ImGui { double minBandwidth; double maxBandwidth; bool bandwidthLocked; + + ImU32 color = IM_COL32(255, 255, 255, 50); }; class WaterFall { diff --git a/core/src/signal_path/vfo_manager.cpp b/core/src/signal_path/vfo_manager.cpp index ef7a6477..3a65f5d8 100644 --- a/core/src/signal_path/vfo_manager.cpp +++ b/core/src/signal_path/vfo_manager.cpp @@ -73,6 +73,14 @@ double VFOManager::VFO::getBandwidth() { return wtfVFO->bandwidth; } +void VFOManager::VFO::setColor(ImU32 color) { + wtfVFO->color = color; +} + +std::string VFOManager::VFO::getName() { + return name; +} + VFOManager::VFOManager() { } @@ -83,6 +91,7 @@ VFOManager::VFO* VFOManager::createVFO(std::string name, int reference, double o } VFOManager::VFO* vfo = new VFO(name, reference, offset, bandwidth, sampleRate, minBandwidth, maxBandwidth, bandwidthLocked); vfos[name] = vfo; + vfoCreatedEvent.emit(vfo); return vfo; } @@ -97,6 +106,7 @@ void VFOManager::deleteVFO(VFOManager::VFO* vfo) { if (name == "") { return; } + vfoDeletedEvent.emit(vfo); vfos.erase(name); delete vfo; } @@ -164,6 +174,17 @@ double VFOManager::getBandwidth(std::string name) { return vfos[name]->getBandwidth(); } +void VFOManager::setColor(std::string name, ImU32 color) { + if (vfos.find(name) == vfos.end()) { + return; + } + return vfos[name]->setColor(color); +} + +bool VFOManager::vfoExists(std::string name) { + return (vfos.find(name) != vfos.end()); +} + void VFOManager::updateFromWaterfall(ImGui::WaterFall* wtf) { for (auto const& [name, vfo] : vfos) { if (vfo->wtfVFO->centerOffsetChanged) { @@ -171,4 +192,4 @@ void VFOManager::updateFromWaterfall(ImGui::WaterFall* wtf) { vfo->dspVFO->setOffset(vfo->wtfVFO->centerOffset); } } -} +} \ No newline at end of file diff --git a/core/src/signal_path/vfo_manager.h b/core/src/signal_path/vfo_manager.h index 8bc2fd90..ae09434d 100644 --- a/core/src/signal_path/vfo_manager.h +++ b/core/src/signal_path/vfo_manager.h @@ -2,6 +2,7 @@ #include #include #include +#include class VFOManager { public: @@ -22,6 +23,8 @@ public: void setBandwidthLimits(double minBandwidth, double maxBandwidth, bool bandwidthLocked); bool getBandwidthChanged(bool erase = true); double getBandwidth(); + void setColor(ImU32 color); + std::string getName(); dsp::stream* output; @@ -46,9 +49,15 @@ public: void setBandwidthLimits(std::string name, double minBandwidth, double maxBandwidth, bool bandwidthLocked); bool getBandwidthChanged(std::string name, bool erase = true); double getBandwidth(std::string name); + void setColor(std::string name, ImU32 color); + std::string getName(); + bool vfoExists(std::string name); void updateFromWaterfall(ImGui::WaterFall* wtf); + Event vfoCreatedEvent; + Event vfoDeletedEvent; + private: std::map vfos; }; \ No newline at end of file diff --git a/rtl_tcp_source/src/main.cpp b/rtl_tcp_source/src/main.cpp index 327adf1f..ce6cf96f 100644 --- a/rtl_tcp_source/src/main.cpp +++ b/rtl_tcp_source/src/main.cpp @@ -161,10 +161,18 @@ private: if (_this->running) { style::beginDisabled(); } ImGui::SetNextItemWidth(menuWidth - portWidth); - ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024); + if (ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024)) { + config.aquire(); + config.conf["host"] = std::string(_this->ip); + config.release(true); + } ImGui::SameLine(); ImGui::SetNextItemWidth(portWidth); - ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0); + if (ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0)) { + config.aquire(); + config.conf["port"] = _this->port; + config.release(true); + } ImGui::SetNextItemWidth(menuWidth); if (ImGui::Combo(CONCAT("##_rtltcp_sr_", _this->name), &_this->srId, _this->srTxt.c_str())) {