diff --git a/core/src/core.cpp b/core/src/core.cpp index 4ebe3280..5478f0f2 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -232,12 +232,26 @@ int sdrpp_main(int argc, char* argv[]) { defConfig["modules"] = json::array(); + defConfig["offsets"] = json::array(); + defConfig["offsets"][0]["name"] = "SpyVerter"; + defConfig["offsets"][0]["offset"] = 120000000; + defConfig["offsets"][1]["name"] = "Ham-It-Up"; + defConfig["offsets"][1]["offset"] = 125000000; + defConfig["offsets"][2]["name"] = "MMDS S-band (1998MHz)"; + defConfig["offsets"][2]["offset"] = -1998000000; + defConfig["offsets"][3]["name"] = "DK5AV X-Band"; + defConfig["offsets"][3]["offset"] = -6800000000; + defConfig["offsets"][4]["name"] = "Ku LNB (9750MHz)"; + defConfig["offsets"][4]["offset"] = -9750000000; + defConfig["offsets"][5]["name"] = "Ku LNB (10700MHz)"; + defConfig["offsets"][5]["offset"] = -10700000000; + defConfig["offsetMode"] = (int)0; // Off defConfig["offset"] = 0.0; defConfig["showMenu"] = true; defConfig["showWaterfall"] = true; defConfig["source"] = ""; - defConfig["decimationPower"] = 0; + defConfig["decimation"] = 1; defConfig["iqCorrection"] = false; defConfig["invertIQ"] = false; diff --git a/core/src/gui/menus/source.cpp b/core/src/gui/menus/source.cpp index da8285ac..77cd5c03 100644 --- a/core/src/gui/menus/source.cpp +++ b/core/src/gui/menus/source.cpp @@ -5,113 +5,97 @@ #include #include #include +#include +#include namespace sourcemenu { int offsetMode = 0; int sourceId = 0; double customOffset = 0.0; double effectiveOffset = 0.0; - int decimationPower = 0; + int decimId = 0; bool iqCorrection = false; bool invertIQ = false; - EventHandler sourceRegisteredHandler; + EventHandler sourcesChangedHandler; EventHandler sourceUnregisterHandler; - EventHandler sourceUnregisteredHandler; - std::vector sourceNames; - std::string sourceNamesTxt; + OptionList sources; std::string selectedSource; + OptionList offsets; + std::vector customOffsets; + OptionList decimations; + + bool showAddOffsetDialog = false; + char newOffsetName[1024]; + double newOffset = 0.0; + + bool showDelOffsetDialog = false; + std::string delOffsetName = ""; enum { OFFSET_MODE_NONE, - OFFSET_MODE_CUSTOM, - OFFSET_MODE_SPYVERTER, - OFFSET_MODE_HAM_IT_UP, - OFFSET_MODE_MMDS_SB_1998, - OFFSET_MODE_DK5AV_XB, - OFFSET_MODE_KU_LNB_9750, - OFFSET_MODE_KU_LNB_10700, - _OFFSET_MODE_COUNT + OFFSET_MODE_MANUAL, + OFFSET_MODE_CUSTOM_BASE }; - const char* offsetModesTxt = "None\0" - "Custom\0" - "SpyVerter\0" - "Ham-It-Up\0" - "MMDS S-band (1998MHz)\0" - "DK5AV X-Band\0" - "Ku LNB (9750MHz)\0" - "Ku LNB (10700MHz)\0"; - - const char* decimationStages = "None\0" - "2\0" - "4\0" - "8\0" - "16\0" - "32\0" - "64\0"; - void updateOffset() { - if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; } - else if (offsetMode == OFFSET_MODE_SPYVERTER) { - effectiveOffset = 120000000; - } // 120MHz Up-conversion - else if (offsetMode == OFFSET_MODE_HAM_IT_UP) { - effectiveOffset = 125000000; - } // 125MHz Up-conversion - else if (offsetMode == OFFSET_MODE_MMDS_SB_1998) { - effectiveOffset = -1998000000; - } // 1.998GHz Down-conversion - else if (offsetMode == OFFSET_MODE_DK5AV_XB) { - effectiveOffset = -6800000000; - } // 6.8GHz Down-conversion - else if (offsetMode == OFFSET_MODE_KU_LNB_9750) { - effectiveOffset = -9750000000; - } // 9.750GHz Down-conversion - else if (offsetMode == OFFSET_MODE_KU_LNB_10700) { - effectiveOffset = -10700000000; - } // 10.7GHz Down-conversion - else { + // Compute the effective offset + switch (offsetMode) { + case OFFSET_MODE_NONE: effectiveOffset = 0; + break; + case OFFSET_MODE_MANUAL: + effectiveOffset = customOffset; + break; + default: + effectiveOffset = customOffsets[offsetMode - OFFSET_MODE_CUSTOM_BASE]; + break; } + + // Apply it sigpath::sourceManager.setTuningOffset(effectiveOffset); } void refreshSources() { - sourceNames = sigpath::sourceManager.getSourceNames(); - sourceNamesTxt.clear(); + // Get sources + auto sourceNames = sigpath::sourceManager.getSourceNames(); + + // Define source options + sources.clear(); for (auto name : sourceNames) { - sourceNamesTxt += name; - sourceNamesTxt += '\0'; + sources.define(name, name, name); } } void selectSource(std::string name) { - if (sourceNames.empty()) { + // If there is no source, give up + if (sources.empty()) { + sourceId = 0; selectedSource.clear(); return; } - auto it = std::find(sourceNames.begin(), sourceNames.end(), name); - if (it == sourceNames.end()) { - selectSource(sourceNames[0]); + + // If a source with the given name doesn't exist, select the first source instead + if (!sources.valueExists(name)) { + selectSource(sources.value(0)); return; } - sourceId = std::distance(sourceNames.begin(), it); - selectedSource = sourceNames[sourceId]; - sigpath::sourceManager.selectSource(sourceNames[sourceId]); + + // Update the GUI variables + sourceId = sources.valueExists(name); + selectedSource = name; + + // Select the source module + sigpath::sourceManager.selectSource(name); } - void onSourceRegistered(std::string name, void* ctx) { + void onSourcesChanged(std::string name, void* ctx) { + // Update the source list refreshSources(); - if (selectedSource.empty()) { - sourceId = 0; - selectSource(sourceNames[0]); - return; - } - - sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); + // Reselect the current source + selectSource(selectedSource); } void onSourceUnregister(std::string name, void* ctx) { @@ -120,60 +104,150 @@ namespace sourcemenu { // TODO: Stop everything } - void onSourceUnregistered(std::string name, void* ctx) { - refreshSources(); + void loadOffsets() { + // Clear list + offsets.clear(); + customOffsets.clear(); - if (sourceNames.empty()) { - selectedSource = ""; - return; + // Define special offset modes + offsets.define("None", 0); + offsets.define("Manual", 1); + + // Acquire the config file + core::configManager.acquire(); + + // Load custom offsets + std::vector offsetList = core::configManager.conf["offsets"]; + for (auto& o : offsetList) { + customOffsets.push_back(o["offset"]); + offsets.define(o["name"], offsets.size()); } - if (name == selectedSource) { - sourceId = std::clamp(sourceId, 0, sourceNames.size() - 1); - selectSource(sourceNames[sourceId]); - return; - } - - sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); + // Release the config file + core::configManager.release(); } void init() { + // Load offset modes + loadOffsets(); + + // Define decimation values + decimations.define(1, "None", 1); + decimations.define(2, "2x", 2); + decimations.define(4, "4x", 4); + decimations.define(8, "8x", 8); + decimations.define(16, "16x", 16); + decimations.define(32, "32x", 32); + decimations.define(64, "64x", 64); + + // Acquire the config file core::configManager.acquire(); + + // Load other settings std::string selected = core::configManager.conf["source"]; customOffset = core::configManager.conf["offset"]; offsetMode = core::configManager.conf["offsetMode"]; - decimationPower = core::configManager.conf["decimationPower"]; iqCorrection = core::configManager.conf["iqCorrection"]; invertIQ = core::configManager.conf["invertIQ"]; + int decimation = core::configManager.conf["decimation"]; + if (decimations.keyExists(decimation)) { + decimId = decimations.keyId(decimation); + } + + // Release the config file + core::configManager.release(); + + // Select the source module + refreshSources(); + selectSource(selected); + + // Update frontend settings sigpath::iqFrontEnd.setDCBlocking(iqCorrection); sigpath::iqFrontEnd.setInvertIQ(invertIQ); updateOffset(); + sigpath::iqFrontEnd.setDecimation(decimations.value(decimId)); - refreshSources(); - selectSource(selected); - sigpath::iqFrontEnd.setDecimation(1 << decimationPower); - - sourceRegisteredHandler.handler = onSourceRegistered; + // Register handlers + sourcesChangedHandler.handler = onSourcesChanged; sourceUnregisterHandler.handler = onSourceUnregister; - sourceUnregisteredHandler.handler = onSourceUnregistered; - sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler); + sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler); sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler); - sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler); + sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler); + } - core::configManager.release(); + void addOffset(const std::string& name, double offset) { + // Acquire the config file + core::configManager.acquire(); + + // Define a new offset + auto newOffsetObj = json::object(); + newOffsetObj["name"] = newOffsetName; + newOffsetObj["offset"] = newOffset; + core::configManager.conf["offsets"].push_back(newOffsetObj); + + // Acquire the config file + core::configManager.release(true); + + // Reload the offsets + loadOffsets(); + + // Re-select the same one + // TODO: Switch from an array to a map, because two can't have the same name anyway and it'll just make things easier...+ + } + + void delOffset(const std::string& name) { + + } + + bool addOffsetDialog() { + bool open = true; + gui::mainWindow.lockWaterfallControls = true; + + float menuWidth = ImGui::GetContentRegionAvail().x; + + const char* id = "Add offset##sdrpp_add_offset_dialog_"; + ImGui::OpenPopup(id); + + if (ImGui::BeginPopup(id, ImGuiWindowFlags_NoResize)) { + ImGui::LeftLabel("Name"); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + ImGui::InputText("##sdrpp_add_offset_name", newOffsetName, 1023); + + ImGui::LeftLabel("Offset"); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + ImGui::InputDouble("##sdrpp_add_offset_offset", &newOffset); + + bool denyApply = !newOffsetName[0] || offsets.nameExists(newOffsetName); + + if (denyApply) { style::beginDisabled(); } + if (ImGui::Button("Apply")) { + addOffset(newOffsetName, newOffset); + open = false; + } + if (denyApply) { style::endDisabled(); } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + open = false; + } + ImGui::EndPopup(); + } + return open; } void draw(void* ctx) { float itemWidth = ImGui::GetContentRegionAvail().x; + float lineHeight = ImGui::GetTextLineHeightWithSpacing(); + float spacing = lineHeight - ImGui::GetTextLineHeight(); bool running = gui::mainWindow.sdrIsRunning(); if (running) { style::beginDisabled(); } ImGui::SetNextItemWidth(itemWidth); - if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) { - selectSource(sourceNames[sourceId]); + if (ImGui::Combo("##source", &sourceId, sources.txt)) { + std::string newSource = sources.value(sourceId); + selectSource(newSource); core::configManager.acquire(); - core::configManager.conf["source"] = sourceNames[sourceId]; + core::configManager.conf["source"] = newSource; core::configManager.release(true); } @@ -196,17 +270,41 @@ namespace sourcemenu { } ImGui::LeftLabel("Offset mode"); - ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) { + ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing)); + if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsets.txt)) { updateOffset(); core::configManager.acquire(); core::configManager.conf["offsetMode"] = offsetMode; core::configManager.release(true); } + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing); + if (offsetMode < OFFSET_MODE_CUSTOM_BASE) { ImGui::BeginDisabled(); } + if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) { + delOffsetName = "TEST"; + showDelOffsetDialog = true; + } + if (offsetMode < OFFSET_MODE_CUSTOM_BASE) { ImGui::EndDisabled(); } + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing); + if (ImGui::Button("+##_sdrpp_offset_add_", ImVec2(lineHeight + 0.5f*spacing, 0))) { + strcpy(newOffsetName, "New Offset"); + showAddOffsetDialog = true; + } + + // Offset delete confirmation + if (ImGui::GenericDialog("sdrpp_del_offset_confirm", showDelOffsetDialog, GENERIC_DIALOG_BUTTONS_YES_NO, []() { + ImGui::Text("Deleting offset named \"%s\". Are you sure?", delOffsetName); + }) == GENERIC_DIALOG_BUTTON_YES) { + delOffset(delOffsetName); + } + + // Offset add diaglog + if (showAddOffsetDialog) { showAddOffsetDialog = addOffsetDialog(); } ImGui::LeftLabel("Offset"); - ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX()); - if (offsetMode == OFFSET_MODE_CUSTOM) { + ImGui::FillWidth(); + if (offsetMode == OFFSET_MODE_MANUAL) { if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) { updateOffset(); core::configManager.acquire(); @@ -222,11 +320,11 @@ namespace sourcemenu { if (running) { style::beginDisabled(); } ImGui::LeftLabel("Decimation"); - ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) { - sigpath::iqFrontEnd.setDecimation(1 << decimationPower); + ImGui::FillWidth(); + if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) { + sigpath::iqFrontEnd.setDecimation(decimations.value(decimId)); core::configManager.acquire(); - core::configManager.conf["decimationPower"] = decimationPower; + core::configManager.conf["decimation"] = decimations.key(decimId); core::configManager.release(true); } if (running) { style::endDisabled(); } diff --git a/core/src/version.h b/core/src/version.h index 3e0b37f5..2e14cb6d 100644 --- a/core/src/version.h +++ b/core/src/version.h @@ -1,3 +1,3 @@ #pragma once -#define VERSION_STR "1.2.0" \ No newline at end of file +#define VERSION_STR "1.2.1" \ No newline at end of file