10 Commits

Author SHA1 Message Date
711ed7711f more work on the rigctl client update
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 3s
Build Binaries / build_ubuntu_jammy (push) Failing after 3s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
2024-12-19 04:18:19 +01:00
a8e6f24b29 start of work on rewrite 2024-12-18 19:45:51 +01:00
b914587228 Revert "add support for new models (in a bad way) and start the rewrite process"
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 17s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
This reverts commit 3c1d0c7422.
2024-11-27 21:42:59 +01:00
3c1d0c7422 add support for new models (in a bad way) and start the rewrite process 2024-11-27 21:41:46 +01:00
11f87e0fe2 fix menu order bug
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 5s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-11-09 19:28:34 +01:00
e192cb963b fix MacOS ARM CI
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 5s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-11-08 00:29:46 +01:00
fe407a2f27 Merge pull request #1521 from AlexandreRouma/new_source_menu
New source menu
2024-11-08 00:20:26 +01:00
6891d0bb0f final bugfixes to the new source menu
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 3s
Build Binaries / build_debian_bookworm (push) Failing after 6s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 5s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-11-07 23:49:15 +01:00
b835d07573 finish custom offset definitions and fix bug in source selection 2024-11-07 17:39:52 +01:00
69161253e8 source menu upgrade 2024-11-07 14:03:32 +01:00
8 changed files with 561 additions and 164 deletions

View File

@ -138,7 +138,7 @@ jobs:
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build
@ -195,7 +195,7 @@ jobs:
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build

View File

@ -147,12 +147,12 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["menuElements"][3]["name"] = "Sinks";
defConfig["menuElements"][3]["open"] = true;
defConfig["menuElements"][3]["name"] = "Frequency Manager";
defConfig["menuElements"][3]["open"] = true;
defConfig["menuElements"][4]["name"] = "VFO Color";
defConfig["menuElements"][4]["name"] = "Frequency Manager";
defConfig["menuElements"][4]["open"] = true;
defConfig["menuElements"][5]["name"] = "VFO Color";
defConfig["menuElements"][5]["open"] = true;
defConfig["menuElements"][6]["name"] = "Band Plan";
defConfig["menuElements"][6]["open"] = true;
@ -232,12 +232,19 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["modules"] = json::array();
defConfig["offsetMode"] = (int)0; // Off
defConfig["offset"] = 0.0;
defConfig["offsets"]["SpyVerter"] = 120000000.0;
defConfig["offsets"]["Ham-It-Up"] = 125000000.0;
defConfig["offsets"]["MMDS S-band (1998MHz)"] = -1998000000.0;
defConfig["offsets"]["DK5AV X-Band"] = -6800000000.0;
defConfig["offsets"]["Ku LNB (9750MHz)"] = -9750000000.0;
defConfig["offsets"]["Ku LNB (10700MHz)"] = -10700000000.0;
defConfig["selectedOffset"] = "None";
defConfig["manualOffset"] = 0.0;
defConfig["showMenu"] = true;
defConfig["showWaterfall"] = true;
defConfig["source"] = "";
defConfig["decimationPower"] = 0;
defConfig["decimation"] = 1;
defConfig["iqCorrection"] = false;
defConfig["invertIQ"] = false;
@ -318,12 +325,18 @@ int sdrpp_main(int argc, char* argv[]) {
// Remove unused elements
auto items = core::configManager.conf.items();
auto newConf = core::configManager.conf;
bool configCorrected = false;
for (auto const& item : items) {
if (!defConfig.contains(item.key())) {
flog::info("Unused key in config {0}, repairing", item.key());
core::configManager.conf.erase(item.key());
newConf.erase(item.key());
configCorrected = true;
}
}
if (configCorrected) {
core::configManager.conf = newConf;
}
// Update to new module representation in config if needed
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {

View File

@ -5,113 +5,120 @@
#include <gui/main_window.h>
#include <gui/style.h>
#include <signal_path/signal_path.h>
#include <utils/optionlist.h>
#include <gui/dialogs/dialog_box.h>
namespace sourcemenu {
int offsetMode = 0;
int sourceId = 0;
double customOffset = 0.0;
double effectiveOffset = 0.0;
int decimationPower = 0;
EventHandler<std::string> sourcesChangedHandler;
EventHandler<std::string> sourceUnregisterHandler;
OptionList<std::string, std::string> sources;
std::string selectedSource;
int decimId = 0;
OptionList<int, int> decimations;
bool iqCorrection = false;
bool invertIQ = false;
EventHandler<std::string> sourceRegisteredHandler;
EventHandler<std::string> sourceUnregisterHandler;
EventHandler<std::string> sourceUnregisteredHandler;
int offsetId = 0;
double manualOffset = 0.0;
std::string selectedOffset;
double effectiveOffset = 0.0;
OptionList<std::string, double> offsets;
std::map<std::string, double> namedOffsets;
std::vector<std::string> sourceNames;
std::string sourceNamesTxt;
std::string selectedSource;
bool showAddOffsetDialog = false;
char newOffsetName[1024];
double newOffset = 0.0;
bool showDelOffsetDialog = false;
std::string delOffsetName = "";
// Offset IDs
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_ID_NONE,
OFFSET_ID_MANUAL,
OFFSET_ID_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 (offsetId) {
case OFFSET_ID_NONE:
effectiveOffset = 0;
break;
case OFFSET_ID_MANUAL:
effectiveOffset = manualOffset;
break;
default:
effectiveOffset = namedOffsets[offsets.name(offsetId)];
break;
}
// Apply it
sigpath::sourceManager.setTuningOffset(effectiveOffset);
}
void selectOffsetById(int id) {
// Update the offset mode
offsetId = id;
selectedOffset = offsets.name(id);
// Update the offset
updateOffset();
}
void selectOffsetByName(const std::string& name) {
// If the name doesn't exist, select 'None'
if (!offsets.nameExists(name)) {
selectOffsetById(OFFSET_ID_NONE);
return;
}
// Select using the ID associated with the name
selectOffsetById(offsets.nameId(name));
}
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.valueId(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 +127,173 @@ namespace sourcemenu {
// TODO: Stop everything
}
void onSourceUnregistered(std::string name, void* ctx) {
refreshSources();
void reloadOffsets() {
// Clear list
offsets.clear();
namedOffsets.clear();
if (sourceNames.empty()) {
selectedSource = "";
return;
// Define special offset modes
offsets.define("None", OFFSET_ID_NONE);
offsets.define("Manual", OFFSET_ID_MANUAL);
// Acquire the config file
core::configManager.acquire();
// Load custom offsets
auto ofs = core::configManager.conf["offsets"].items();
for (auto& o : ofs) {
namedOffsets[o.key()] = (double)o.value();
}
if (name == selectedSource) {
sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
selectSource(sourceNames[sourceId]);
return;
// Define custom offsets
for (auto& [name, offset] : namedOffsets) {
offsets.define(name, offsets.size());
}
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
// Release the config file
core::configManager.release();
}
void init() {
// Load offset modes
reloadOffsets();
// 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();
std::string selected = core::configManager.conf["source"];
customOffset = core::configManager.conf["offset"];
offsetMode = core::configManager.conf["offsetMode"];
decimationPower = core::configManager.conf["decimationPower"];
// Load other settings
std::string selectedSource = core::configManager.conf["source"];
manualOffset = core::configManager.conf["manualOffset"];
std::string selectedOffset = core::configManager.conf["selectedOffset"];
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(selectedSource);
// Update frontend settings
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
sigpath::iqFrontEnd.setInvertIQ(invertIQ);
updateOffset();
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
selectOffsetByName(selectedOffset);
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
core::configManager.conf["offsets"][name] = offset;
// Acquire the config file
core::configManager.release(true);
// Reload the offsets
reloadOffsets();
// Attempt to re-select the same one
selectOffsetByName(selectedOffset);
}
void delOffset(const std::string& name) {
// Acquire the config file
core::configManager.acquire();
// Define a new offset
core::configManager.conf["offsets"].erase(name);
// Acquire the config file
core::configManager.release(true);
// Reload the offsets
reloadOffsets();
// Attempt to re-select the same one
selectOffsetByName(selectedOffset);
}
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 nameExists = offsets.nameExists(newOffsetName);
bool reservedName = !strcmp(newOffsetName, "None") || !strcmp(newOffsetName, "Manual");
bool denyApply = !newOffsetName[0] || nameExists || reservedName;
if (nameExists) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "An offset with the given name already exists.");
}
else if (reservedName) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "The given name is reserved.");
}
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,21 +316,45 @@ namespace sourcemenu {
}
ImGui::LeftLabel("Offset mode");
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
updateOffset();
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing));
if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) {
selectOffsetById(offsetId);
core::configManager.acquire();
core::configManager.conf["offsetMode"] = offsetMode;
core::configManager.conf["selectedOffset"] = offsets.key(offsetId);
core::configManager.release(true);
}
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::BeginDisabled(); }
if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
delOffsetName = selectedOffset;
showDelOffsetDialog = true;
}
if (offsetId < OFFSET_ID_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.c_str());
}) == 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) {
if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
ImGui::FillWidth();
if (offsetId == OFFSET_ID_MANUAL) {
if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) {
updateOffset();
core::configManager.acquire();
core::configManager.conf["offset"] = customOffset;
core::configManager.conf["manualOffset"] = manualOffset;
core::configManager.release(true);
}
}
@ -222,11 +366,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(); }

View File

@ -1,3 +1,3 @@
#pragma once
#define VERSION_STR "1.2.0"
#define VERSION_STR "1.2.1"

View File

@ -10,23 +10,54 @@
#include <config.h>
#include <cctype>
#include <radio_interface.h>
#include <utils/optionlist.h>
#include <atomic>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "rigctl_client",
/* Description: */ "Client for the RigCTL protocol",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Version: */ 0, 2, 0,
/* Max instances */ 1
};
ConfigManager config;
enum Mode {
MODE_PANADAPTER,
MODE_MIRROR
};
enum Priority {
PRIOR_SDR,
PRIOR_RIGCTL
};
const std::map<int, net::rigctl::Mode> RADIO_TO_RIGCTL = {
{ RADIO_IFACE_MODE_NFM, net::rigctl::MODE_FM },
{ RADIO_IFACE_MODE_WFM, net::rigctl::MODE_WFM },
{ RADIO_IFACE_MODE_AM , net::rigctl::MODE_AM },
{ RADIO_IFACE_MODE_DSB, net::rigctl::MODE_DSB },
{ RADIO_IFACE_MODE_USB, net::rigctl::MODE_USB },
{ RADIO_IFACE_MODE_CW , net::rigctl::MODE_CW },
{ RADIO_IFACE_MODE_LSB, net::rigctl::MODE_LSB }
};
class RigctlClientModule : public ModuleManager::Instance {
public:
RigctlClientModule(std::string name) {
this->name = name;
// Define the operation modes
modes.define("panadapter", "Pandapter", MODE_PANADAPTER);
modes.define("mirror", "Mirror", MODE_MIRROR);
// Define the priority modes
priorities.define("sdr", "SDR", PRIOR_SDR);
priorities.define("rigctl", "RigCTL", PRIOR_RIGCTL);
// Load default
strcpy(host, "127.0.0.1");
@ -40,13 +71,28 @@ public:
port = config.conf[name]["port"];
port = std::clamp<int>(port, 1, 65535);
}
if (config.conf[name].contains("mode")) {
std::string modeStr = config.conf[name]["mode"];
if (modes.keyExists(modeStr)) { modeId = modes.keyId(modeStr); }
}
if (config.conf[name].contains("priority")) {
std::string priorityStr = config.conf[name]["priority"];
if (priorities.keyExists(priorityStr)) { priorityId = modes.keyId(priorityStr); }
}
if (config.conf[name].contains("interval")) {
interval = config.conf[name]["interval"];
interval = std::clamp<int>(interval, 100, 1000);
}
if (config.conf[name].contains("ifFreq")) {
ifFreq = config.conf[name]["ifFreq"];
}
if (config.conf[name].contains("vfo")) {
selectedVFO = config.conf[name]["vfo"];
}
config.release();
_retuneHandler.ctx = this;
_retuneHandler.handler = retuneHandler;
// Refresh VFOs
refreshVFOs();
gui::menu.registerEntry(name, menuHandler, this, NULL);
}
@ -85,10 +131,20 @@ public:
return;
}
// Switch source to panadapter mode
sigpath::sourceManager.setPanadapterIF(ifFreq);
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler);
if (mode == MODE_PANADAPTER) {
// Switch source to panadapter mode
sigpath::sourceManager.setPanadapterIF(ifFreq);
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
}
// Start the worker thread
run = true;
if (mode == MODE_PANADAPTER) {
workerThread = std::thread(&RigctlClientModule::panadapterWorker, this);
}
else if (mode == MODE_MIRROR) {
workerThread = std::thread(&RigctlClientModule::mirrorWorker, this);
}
running = true;
}
@ -97,9 +153,15 @@ public:
std::lock_guard<std::recursive_mutex> lck(mtx);
if (!running) { return; }
// Switch source back to normal mode
sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
// Stop the worker thread
run = false;
if (workerThread.joinable()) { workerThread.join(); }
if (mode == MODE_PANADAPTER) {
// Switch source back to normal mode
sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
}
// Disconnect from rigctl server
client->close();
@ -125,19 +187,70 @@ private:
config.conf[_this->name]["port"] = _this->port;
config.release(true);
}
if (_this->running) { style::endDisabled(); }
ImGui::LeftLabel("IF Frequency");
ImGui::LeftLabel("Mode");
ImGui::FillWidth();
if (ImGui::InputDouble(CONCAT("##_rigctl_if_freq_", _this->name), &_this->ifFreq, 100.0, 100000.0, "%.0f")) {
if (_this->running) {
sigpath::sourceManager.setPanadapterIF(_this->ifFreq);
}
if (ImGui::Combo(CONCAT("##_rigctl_cli_mode_", _this->name), &_this->modeId, _this->modes.txt)) {
_this->mode = _this->modes[_this->modeId];
config.acquire();
config.conf[_this->name]["ifFreq"] = _this->ifFreq;
config.conf[_this->name]["mode"] = _this->modes.key(_this->modeId);
config.release(true);
}
ImGui::LeftLabel("Priority");
ImGui::FillWidth();
if (ImGui::Combo(CONCAT("##_rigctl_cli_priority_", _this->name), &_this->priorityId, _this->priorities.txt)) {
_this->priority = _this->priorities[_this->priorityId];
config.acquire();
config.conf[_this->name]["priority"] = _this->priorities.key(_this->priorityId);
config.release(true);
}
ImGui::LeftLabel("Interval");
ImGui::FillWidth();
if (ImGui::InputInt(CONCAT("##_rigctl_cli_interval_", _this->name), &_this->interval, 10, 100)) {
_this->interval = std::clamp<int>(_this->interval, 100, 1000);
config.acquire();
config.conf[_this->name]["interval"] = _this->interval;
config.release(true);
}
if (_this->mode == MODE_PANADAPTER) {
ImGui::LeftLabel("IF Frequency");
ImGui::FillWidth();
if (ImGui::InputDouble(CONCAT("##_rigctl_if_freq_", _this->name), &_this->ifFreq, 100.0, 100000.0, "%.0f")) {
if (_this->running) {
sigpath::sourceManager.setPanadapterIF(_this->ifFreq);
}
config.acquire();
config.conf[_this->name]["ifFreq"] = _this->ifFreq;
config.release(true);
}
}
else if (_this->mode == MODE_MIRROR) {
ImGui::LeftLabel("Controlled VFO");
ImGui::FillWidth();
if (ImGui::Combo(CONCAT("##_rigctl_cli_vfo_", _this->name), &_this->vfoId, _this->vfos.txt)) {
_this->selectedVFO = _this->vfos[_this->vfoId];
config.acquire();
config.conf[_this->name]["vfo"] = _this->vfos.key(_this->vfoId);
config.release(true);
}
ImGui::Checkbox(CONCAT("Sync Frequency##_rigctl_sync_freq_", _this->name), &_this->syncFrequency);
if (_this->vfoIsRadio) {
ImGui::Checkbox(CONCAT("Sync Mode##_rigctl_sync_freq_", _this->name), &_this->syncMode);
}
else {
bool dummy = false;
if (!_this->running) { style::beginDisabled(); }
ImGui::Checkbox(CONCAT("Sync Mode##_rigctl_sync_freq_", _this->name), &dummy);
if (!_this->running) { style::endDisabled(); }
}
}
if (_this->running) { style::endDisabled(); }
ImGui::FillWidth();
if (_this->running && ImGui::Button(CONCAT("Stop##_rigctl_cli_stop_", _this->name), ImVec2(menuWidth, 0))) {
_this->stop();
@ -159,11 +272,131 @@ private:
}
}
static void retuneHandler(double freq, void* ctx) {
RigctlClientModule* _this = (RigctlClientModule*)ctx;
if (!_this->client || !_this->client->isOpen()) { return; }
if (_this->client->setFreq(freq)) {
flog::error("Could not set frequency");
void selectVFO(const std::string& vfoName) {
// If no vfo is available, deselect
if (vfos.empty()) {
selectedVFO.clear();
vfoIsRadio = false;
return;
}
// If a vfo with that name isn't found, select the first VFO in the list
if (!vfos.keyExists(vfoName)) {
selectVFO(vfos.key(0));
return;
}
// Check if the VFO is from a radio module
vfoIsRadio = (core::moduleManager.getInstanceModuleName(vfoName) == "radio");
// Update the selected VFO
selectedVFO = vfoName;
vfoId = vfos.keyId(vfoName);
}
void refreshVFOs() {
// Clear the list
vfos.clear();
// Define using the VFO list from the waterfall
for (auto const& [_name, vfo] : gui::waterfall.vfos) {
vfos.define(_name, _name, _name);
}
// Reselect the current VFO
selectVFO(selectedVFO);
}
void panadapterWorker() {
int64_t lastRigctlFreq = -1;
int64_t lastCenterFreq = -1;
int64_t rigctlFreq;
int64_t centerFreq;
while (run) {
// Query the current modes
try {
// Get the current rigctl frequency
rigctlFreq = (int64_t)client->getFreq();
// Get the current center frequency
centerFreq = (int64_t)gui::waterfall.getCenterFrequency();
}
catch (const std::exception& e) {
flog::error("Error while getting frequencies: {}", e.what());
}
// Update frequencies depending on the priority
if (priority == PRIOR_SDR) {
}
else if (priority == PRIOR_RIGCTL) {
}
// Save frequencies
lastRigctlFreq = rigctlFreq;
lastCenterFreq = centerFreq;
// Wait for the time interval
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
}
}
void mirrorWorker() {
int64_t lastRigctlFreq = -1;
int64_t lastFreq = -1;
int64_t rigctlFreq;
int64_t freq;
int lastRigctlMode = -1;
int lastVFOMode = -1;
int rigctlMode;
int vfoMode;
while (run) {
// Query the current modes
try {
// Get the current rigctl frequency
rigctlFreq = (int64_t)client->getFreq();
// Get the rigctl and VFO modes
if (selectedVFO.empty()) {
// Get the VFO frequency
// TODO
// Get the mode if needed
if (syncMode && vfoIsRadio) {
// Get the current rigctl mode
// rigctlMode = client->getMode();
// Get the current VFO mode
core::modComManager.callInterface(selectedVFO, RADIO_IFACE_CMD_GET_MODE, NULL, &vfoMode);
}
}
else {
freq = (int64_t)gui::waterfall.getCenterFrequency();
}
}
catch (const std::exception& e) {
flog::error("Error while getting frequencies: {}", e.what());
}
// Update frequencies depending on the priority
if (priority == PRIOR_SDR) {
}
else if (priority == PRIOR_RIGCTL) {
}
// Save modes and frequencies
lastRigctlFreq = rigctlFreq;
lastFreq = freq;
lastRigctlMode = rigctlMode;
lastVFOMode = vfoMode;
// Wait for the time interval
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
}
}
@ -176,9 +409,31 @@ private:
int port = 4532;
std::shared_ptr<net::rigctl::Client> client;
Mode mode = MODE_PANADAPTER;
int modeId = 0;
Priority priority = PRIOR_SDR;
int priorityId = 0;
int interval = 100;
double ifFreq = 8830000.0;
bool syncFrequency = true;
bool syncMode = true;
bool vfoIsRadio = false;
EventHandler<double> _retuneHandler;
OptionList<std::string, Mode> modes;
OptionList<std::string, Priority> priorities;
std::string selectedVFO = "";
int vfoId = 0;
OptionList<std::string, std::string> vfos;
std::atomic_bool run;
std::thread workerThread;
};
MOD_EXPORT void _INIT_() {

View File

@ -1,10 +0,0 @@
#pragma once
#include <utils/networking.h>
class RigCTLClient {
public:
private:
};

View File

@ -18,7 +18,7 @@ SDRPP_MOD_INFO{
/* Name: */ "rigctl_server",
/* Description: */ "My fancy new module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Version: */ 0, 1, 1,
/* Max instances */ -1
};
@ -153,21 +153,16 @@ private:
}
}
ImGui::BeginTable(CONCAT("Stop##_rigctl_srv_tbl_", _this->name), 2);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Checkbox(CONCAT("Tuning##_rigctl_srv_tune_ena_", _this->name), &_this->tuningEnabled)) {
config.acquire();
config.conf[_this->name]["tuning"] = _this->tuningEnabled;
config.release(true);
}
ImGui::TableSetColumnIndex(1);
if (ImGui::Checkbox(CONCAT("Recording##_rigctl_srv_tune_ena_", _this->name), &_this->recordingEnabled)) {
config.acquire();
config.conf[_this->name]["recording"] = _this->recordingEnabled;
config.release(true);
}
ImGui::EndTable();
if (ImGui::Checkbox(CONCAT("Listen on startup##_rigctl_srv_auto_lst_", _this->name), &_this->autoStart)) {
config.acquire();

View File

@ -377,7 +377,7 @@ Modules in beta are still included in releases for the most part but not enabled
| frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ |
| iq_exporter | Working | - | OPT_BUILD_IQ_EXPORTER | ✅ | ✅ | ⛔ |
| recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ |
| rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | ✅ | ✅ | ⛔ |
| rigctl_client | Beta | - | OPT_BUILD_RIGCTL_CLIENT | ✅ | ✅ | ⛔ |
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ |
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | ⛔ |
| scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ |