Merge branch 'master' into patch-1

This commit is contained in:
Dr. Rubisco 2021-07-29 12:38:23 -04:00 committed by GitHub
commit d84273a0db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 454 additions and 406 deletions

View File

@ -47,15 +47,14 @@ void ConfigManager::save(bool lock) {
} }
void ConfigManager::enableAutoSave() { void ConfigManager::enableAutoSave() {
if (!autoSaveEnabled) { if (autoSaveEnabled) { return; }
autoSaveEnabled = true; autoSaveEnabled = true;
termFlag = false; termFlag = false;
autoSaveThread = std::thread(autoSaveWorker, this); autoSaveThread = std::thread(&ConfigManager::autoSaveWorker, this);
}
} }
void ConfigManager::disableAutoSave() { void ConfigManager::disableAutoSave() {
if (autoSaveEnabled) { if (!autoSaveEnabled) { return; }
{ {
std::lock_guard<std::mutex> lock(termMtx); std::lock_guard<std::mutex> lock(termMtx);
autoSaveEnabled = false; autoSaveEnabled = false;
@ -63,35 +62,34 @@ void ConfigManager::disableAutoSave() {
} }
termCond.notify_one(); termCond.notify_one();
if (autoSaveThread.joinable()) { autoSaveThread.join(); } if (autoSaveThread.joinable()) { autoSaveThread.join(); }
}
} }
void ConfigManager::acquire() { void ConfigManager::acquire() {
mtx.lock(); mtx.lock();
} }
void ConfigManager::release(bool changed) { void ConfigManager::release(bool modified) {
this->changed |= changed; changed |= modified;
mtx.unlock(); mtx.unlock();
} }
void ConfigManager::autoSaveWorker(ConfigManager* _this) { void ConfigManager::autoSaveWorker() {
while (_this->autoSaveEnabled) { while (autoSaveEnabled) {
if (!_this->mtx.try_lock()) { if (!mtx.try_lock()) {
spdlog::warn("ConfigManager locked, waiting..."); spdlog::warn("ConfigManager locked, waiting...");
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
continue; continue;
} }
if (_this->changed) { if (changed) {
_this->changed = false; changed = false;
_this->save(false); save(false);
} }
_this->mtx.unlock(); mtx.unlock();
// Sleep but listen for wakeup call // Sleep but listen for wakeup call
{ {
std::unique_lock<std::mutex> lock(_this->termMtx); std::unique_lock<std::mutex> lock(termMtx);
_this->termCond.wait_for(lock, std::chrono::milliseconds(1000), [_this]() { return _this->termFlag; } ); termCond.wait_for(lock, std::chrono::milliseconds(1000), [this]() { return termFlag; } );
} }
} }
} }

View File

@ -17,21 +17,21 @@ public:
void enableAutoSave(); void enableAutoSave();
void disableAutoSave(); void disableAutoSave();
void acquire(); void acquire();
void release(bool changed = false); void release(bool modified = false);
json conf; json conf;
private: private:
static void autoSaveWorker(ConfigManager* _this); void autoSaveWorker();
std::string path = ""; std::string path = "";
bool changed = false; volatile bool changed = false;
bool autoSaveEnabled = false; volatile bool autoSaveEnabled = false;
std::thread autoSaveThread; std::thread autoSaveThread;
std::mutex mtx; std::mutex mtx;
std::mutex termMtx; std::mutex termMtx;
std::condition_variable termCond; std::condition_variable termCond;
bool termFlag = false; volatile bool termFlag = false;
}; };

View File

@ -206,6 +206,7 @@ int sdrpp_main(int argc, char *argv[]) {
defConfig["showWaterfall"] = true; defConfig["showWaterfall"] = true;
defConfig["source"] = ""; defConfig["source"] = "";
defConfig["decimationPower"] = 0; defConfig["decimationPower"] = 0;
defConfig["iqCorrection"] = false;
defConfig["streams"]["Radio"]["muted"] = false; defConfig["streams"]["Radio"]["muted"] = false;
defConfig["streams"]["Radio"]["sink"] = "Audio"; defConfig["streams"]["Radio"]["sink"] = "Audio";
@ -462,6 +463,11 @@ int sdrpp_main(int argc, char *argv[]) {
glfwSwapBuffers(core::window); glfwSwapBuffers(core::window);
} }
// Shut down all modules
for (auto& [name, mod] : core::moduleManager.modules) {
mod.end();
}
// Cleanup // Cleanup
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown(); ImGui_ImplGlfw_Shutdown();
@ -472,5 +478,8 @@ int sdrpp_main(int argc, char *argv[]) {
sigpath::signalPath.stop(); sigpath::signalPath.stop();
core::configManager.disableAutoSave();
core::configManager.save();
return 0; return 0;
} }

View File

@ -24,9 +24,10 @@ namespace sdrpp_credits {
const char* libraries[] = { const char* libraries[] = {
"Dear ImGui (ocornut)", "Dear ImGui (ocornut)",
"fftw3 (fftw.org)",
"glew (Nigel Stewart)",
"glfw (Camilla Löwy)",
"json (nlohmann)", "json (nlohmann)",
"RtAudio",
"SoapySDR (PothosWare)",
"spdlog (gabime)", "spdlog (gabime)",
"Portable File Dialogs" "Portable File Dialogs"
}; };

76
core/src/dsp/correction.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include <dsp/block.h>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <dsp/window.h>
namespace dsp {
class IQCorrector : public generic_block<IQCorrector> {
public:
IQCorrector() {}
IQCorrector(stream<complex_t>* in, float rate) { init(in, rate); }
void init(stream<complex_t>* in, float rate) {
_in = in;
correctionRate = rate;
offset.re = 0;
offset.im = 0;
generic_block<IQCorrector>::registerInput(_in);
generic_block<IQCorrector>::registerOutput(&out);
generic_block<IQCorrector>::_block_init = true;
}
void setInput(stream<complex_t>* in) {
assert(generic_block<IQCorrector>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<IQCorrector>::ctrlMtx);
generic_block<IQCorrector>::tempStop();
generic_block<IQCorrector>::unregisterInput(_in);
_in = in;
generic_block<IQCorrector>::registerInput(_in);
generic_block<IQCorrector>::tempStart();
}
void setCorrectionRate(float rate) {
correctionRate = rate;
}
int run() {
int count = _in->read();
if (count < 0) { return -1; }
if (bypass) {
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
for (int i = 0; i < count; i++) {
out.writeBuf[i] = _in->readBuf[i] - offset;
offset = offset + (out.writeBuf[i] * correctionRate);
}
_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
stream<complex_t> out;
// TEMPORARY FOR DEBUG PURPOSES
bool bypass = false;
complex_t offset;
private:
stream<complex_t>* _in;
float correctionRate = 0.00001;
};
}

View File

@ -15,6 +15,10 @@ namespace credits {
void show() { void show() {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0,0,0,0));
ImVec2 dispSize = ImGui::GetIO().DisplaySize;
ImVec2 center = ImVec2(dispSize.x/2.0f, dispSize.y/2.0f);
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup("Credits"); ImGui::OpenPopup("Credits");
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove); ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
@ -55,11 +59,8 @@ namespace credits {
ImGui::Spacing(); ImGui::Spacing();
ImGui::Text("SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")"); ImGui::Text("SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")");
ImVec2 dispSize = ImGui::GetIO().DisplaySize;
ImVec2 winSize = ImGui::GetWindowSize();
ImGui::SetWindowPos(ImVec2(std::round((dispSize.x/2) - (winSize.x/2)), std::round((dispSize.y/2) - (winSize.y/2))));
ImGui::EndPopup(); ImGui::EndPopup();
ImGui::PopStyleVar(1); ImGui::PopStyleColor();
ImGui::PopStyleVar();
} }
} }

View File

@ -404,9 +404,11 @@ void MainWindow::draw() {
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 387); int snrWidth = std::min<int>(300, ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - 87);
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth+87));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5);
ImGui::SetNextItemWidth(300); ImGui::SetNextItemWidth(snrWidth);
ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0); ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0);
ImGui::SameLine(); ImGui::SameLine();

View File

@ -108,7 +108,7 @@ namespace displaymenu {
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) { if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) {
std::clamp<int>(fftRate, 1, 200); fftRate = std::max<int>(1, fftRate);
sigpath::signalPath.setFFTRate(fftRate); sigpath::signalPath.setFFTRate(fftRate);
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["fftRate"] = fftRate; core::configManager.conf["fftRate"] = fftRate;

View File

@ -12,6 +12,7 @@ namespace sourecmenu {
double customOffset = 0.0; double customOffset = 0.0;
double effectiveOffset = 0.0; double effectiveOffset = 0.0;
int decimationPower = 0; int decimationPower = 0;
bool iqCorrection = false;
EventHandler<std::string> sourceRegisteredHandler; EventHandler<std::string> sourceRegisteredHandler;
EventHandler<std::string> sourceUnregisterHandler; EventHandler<std::string> sourceUnregisterHandler;
@ -124,6 +125,8 @@ namespace sourecmenu {
customOffset = core::configManager.conf["offset"]; customOffset = core::configManager.conf["offset"];
offsetMode = core::configManager.conf["offsetMode"]; offsetMode = core::configManager.conf["offsetMode"];
decimationPower = core::configManager.conf["decimationPower"]; decimationPower = core::configManager.conf["decimationPower"];
iqCorrection = core::configManager.conf["iqCorrection"];
sigpath::signalPath.setIQCorrection(iqCorrection);
updateOffset(); updateOffset();
refreshSources(); refreshSources();
@ -158,6 +161,13 @@ namespace sourecmenu {
sigpath::sourceManager.showSelectedMenu(); sigpath::sourceManager.showSelectedMenu();
if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) {
sigpath::signalPath.setIQCorrection(iqCorrection);
core::configManager.acquire();
core::configManager.conf["iqCorrection"] = iqCorrection;
core::configManager.release(true);
}
ImGui::Text("Offset mode"); ImGui::Text("Offset mode");
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());

View File

@ -520,7 +520,7 @@ namespace ImGui {
float pixel; float pixel;
float dataRange = waterfallMax - waterfallMin; float dataRange = waterfallMax - waterfallMin;
int count = std::min<float>(waterfallHeight, fftLines); int count = std::min<float>(waterfallHeight, fftLines);
if (rawFFTs != NULL) { if (rawFFTs != NULL && fftLines >= 0) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize; drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize;
drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2); drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2);
@ -530,6 +530,12 @@ namespace ImGui {
waterfallFb[(i * dataWidth) + j] = waterfallPallet[(int)(pixel * (WATERFALL_RESOLUTION - 1))]; waterfallFb[(i * dataWidth) + j] = waterfallPallet[(int)(pixel * (WATERFALL_RESOLUTION - 1))];
} }
} }
for (int i = count; i < waterfallHeight; i++) {
for (int j = 0; j < dataWidth; j++) {
waterfallFb[(i * dataWidth) + j] = (uint32_t)255 << 24;
}
}
} }
delete[] tempData; delete[] tempData;
waterfallUpdate = true; waterfallUpdate = true;
@ -1028,15 +1034,16 @@ namespace ImGui {
void WaterFall::setRawFFTSize(int size, bool lock) { void WaterFall::setRawFFTSize(int size, bool lock) {
std::lock_guard<std::mutex> lck(buf_mtx); std::lock_guard<std::mutex> lck(buf_mtx);
rawFFTSize = size; rawFFTSize = size;
if (rawFFTs != NULL) {
int wfSize = std::max<int>(1, waterfallHeight); int wfSize = std::max<int>(1, waterfallHeight);
if (rawFFTs != NULL) {
rawFFTs = (float*)realloc(rawFFTs, rawFFTSize * wfSize * sizeof(float)); rawFFTs = (float*)realloc(rawFFTs, rawFFTSize * wfSize * sizeof(float));
} }
else { else {
int wfSize = std::max<int>(1, waterfallHeight);
rawFFTs = (float*)malloc(rawFFTSize * wfSize * sizeof(float)); rawFFTs = (float*)malloc(rawFFTSize * wfSize * sizeof(float));
} }
fftLines = 0;
memset(rawFFTs, 0, rawFFTSize * waterfallHeight * sizeof(float)); memset(rawFFTs, 0, rawFFTSize * waterfallHeight * sizeof(float));
updateWaterfallFb();
} }
void WaterFall::setBandPlanPos(int pos) { void WaterFall::setBandPlanPos(int pos) {

View File

@ -16,6 +16,7 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream
// split.init(input); // split.init(input);
inputBuffer.init(input); inputBuffer.init(input);
corrector.init(&inputBuffer.out, 50.0f / sampleRate);
split.init(&inputBuffer.out); split.init(&inputBuffer.out);
reshape.init(&fftStream, fftSize, (sampleRate / fftRate) - fftSize); reshape.init(&fftStream, fftSize, (sampleRate / fftRate) - fftSize);
@ -43,6 +44,8 @@ void SignalPath::setSampleRate(double sampleRate) {
vfo.vfo->start(); vfo.vfo->start();
} }
corrector.setCorrectionRate(50.0f / sampleRate);
split.start(); split.start();
} }
@ -55,6 +58,7 @@ void SignalPath::start() {
decimator->start(); decimator->start();
} }
inputBuffer.start(); inputBuffer.start();
if (iqCorrection) { corrector.start(); }
split.start(); split.start();
reshape.start(); reshape.start();
fftHandlerSink.start(); fftHandlerSink.start();
@ -66,6 +70,7 @@ void SignalPath::stop() {
decimator->stop(); decimator->stop();
} }
inputBuffer.stop(); inputBuffer.stop();
if (iqCorrection) { corrector.stop(); }
split.stop(); split.stop();
reshape.stop(); reshape.stop();
fftHandlerSink.stop(); fftHandlerSink.stop();
@ -159,7 +164,13 @@ void SignalPath::setDecimation(int dec) {
// If no decimation, reconnect // If no decimation, reconnect
if (!dec) { if (!dec) {
if (iqCorrection) {
split.setInput(&corrector.out);
}
else {
split.setInput(&inputBuffer.out); split.setInput(&inputBuffer.out);
}
if (running) { split.start(); } if (running) { split.start(); }
core::setInputSampleRate(sourceSampleRate); core::setInputSampleRate(sourceSampleRate);
return; return;
@ -167,7 +178,17 @@ void SignalPath::setDecimation(int dec) {
// Create new decimators // Create new decimators
for (int i = 0; i < dec; i++) { for (int i = 0; i < dec; i++) {
dsp::HalfDecimator<dsp::complex_t>* decimator = new dsp::HalfDecimator<dsp::complex_t>((i == 0) ? &inputBuffer.out : &decimators[i-1]->out, &halfBandWindow); dsp::HalfDecimator<dsp::complex_t>* decimator;
if (iqCorrection && i == 0) {
decimator = new dsp::HalfDecimator<dsp::complex_t>(&corrector.out, &halfBandWindow);
}
else if (i == 0) {
decimator = new dsp::HalfDecimator<dsp::complex_t>(&inputBuffer.out, &halfBandWindow);
}
else {
decimator = new dsp::HalfDecimator<dsp::complex_t>(&decimators[i-1]->out, &halfBandWindow);
}
if (running) { decimator->start(); } if (running) { decimator->start(); }
decimators.push_back(decimator); decimators.push_back(decimator);
} }
@ -177,3 +198,32 @@ void SignalPath::setDecimation(int dec) {
// Update the DSP sample rate // Update the DSP sample rate
core::setInputSampleRate(sourceSampleRate); core::setInputSampleRate(sourceSampleRate);
} }
void SignalPath::setIQCorrection(bool enabled) {
if (iqCorrection == enabled) { return; }
if (!iqCorrection && enabled) {
if (decimation) {
decimators[0]->setInput(&corrector.out);
}
else {
split.setInput(&corrector.out);
}
if (running) { corrector.start(); }
}
else if (iqCorrection && !enabled) {
if (running) { corrector.stop(); }
if (decimation) {
decimators[0]->setInput(&inputBuffer.out);
}
else {
split.setInput(&inputBuffer.out);
}
}
iqCorrection = enabled;
if (!enabled) {
corrector.offset.re = 0;
corrector.offset.im = 0;
}
}

View File

@ -4,6 +4,7 @@
#include <map> #include <map>
#include <dsp/sink.h> #include <dsp/sink.h>
#include <dsp/decimation.h> #include <dsp/decimation.h>
#include <dsp/correction.h>
class SignalPath { class SignalPath {
public: public:
@ -24,6 +25,7 @@ public:
void stopFFT(); void stopFFT();
void setBuffering(bool enabled); void setBuffering(bool enabled);
void setDecimation(int dec); void setDecimation(int dec);
void setIQCorrection(bool enabled);
dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer; dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer;
double sourceSampleRate = 0; double sourceSampleRate = 0;
@ -36,6 +38,7 @@ private:
}; };
dsp::Splitter<dsp::complex_t> split; dsp::Splitter<dsp::complex_t> split;
dsp::IQCorrector corrector;
// FFT // FFT
dsp::stream<dsp::complex_t> fftStream; dsp::stream<dsp::complex_t> fftStream;
@ -53,4 +56,5 @@ private:
int inputBlockSize; int inputBlockSize;
bool bufferingEnabled = false; bool bufferingEnabled = false;
bool running = false; bool running = false;
bool iqCorrection = false;
}; };

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
#define VERSION_STR "1.0.0_rc1" #define VERSION_STR "1.0.0_rc2"

View File

@ -198,19 +198,20 @@ private:
ImGui::EndTable(); ImGui::EndTable();
if (strlen(nameBuf) == 0) { style::beginDisabled(); } bool applyDisabled = (strlen(nameBuf) == 0) || (bookmarks.find(editedBookmarkName) != bookmarks.end() && editedBookmarkName != firstEditedBookmarkName);
if (applyDisabled) { style::beginDisabled(); }
if (ImGui::Button("Apply")) { if (ImGui::Button("Apply")) {
open = false; open = false;
// If editing, delete the original one // If editing, delete the original one
if (editOpen) { if (editOpen) {
bookmarks.erase(firstEeditedBookmarkName); bookmarks.erase(firstEditedBookmarkName);
} }
bookmarks[nameBuf] = editedBookmark; bookmarks[editedBookmarkName] = editedBookmark;
saveByName(selectedListName); saveByName(selectedListName);
} }
if (strlen(nameBuf) == 0) { style::endDisabled(); } if (applyDisabled) { style::endDisabled(); }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Cancel")) { if (ImGui::Button("Cancel")) {
open = false; open = false;
@ -463,7 +464,7 @@ private:
_this->editedBookmark.selected = false; _this->editedBookmark.selected = false;
_this->editOpen = true; _this->createOpen = true;
// Find new unique default name // Find new unique default name
if (_this->bookmarks.find("New Bookmark") == _this->bookmarks.end()) { if (_this->bookmarks.find("New Bookmark") == _this->bookmarks.end()) {
@ -492,7 +493,7 @@ private:
_this->editOpen = true; _this->editOpen = true;
_this->editedBookmark = _this->bookmarks[selectedNames[0]]; _this->editedBookmark = _this->bookmarks[selectedNames[0]];
_this->editedBookmarkName = selectedNames[0]; _this->editedBookmarkName = selectedNames[0];
_this->firstEeditedBookmarkName = selectedNames[0]; _this->firstEditedBookmarkName = selectedNames[0];
} }
if (selectedNames.size() != 1 && _this->selectedListName != "") { style::endDisabled(); } if (selectedNames.size() != 1 && _this->selectedListName != "") { style::endDisabled(); }
@ -679,7 +680,9 @@ private:
std::string hoveredBookmarkName; std::string hoveredBookmarkName;
if (_this->bookmarkDisplayMode == BOOKMARK_DISP_MODE_TOP) { if (_this->bookmarkDisplayMode == BOOKMARK_DISP_MODE_TOP) {
for (auto const bm : _this->waterfallBookmarks) { int count = _this->waterfallBookmarks.size();
for (int i = count-1; i >= 0; i--) {
auto& bm = _this->waterfallBookmarks[i];
double centerXpos = args.fftRectMin.x + std::round((bm.bookmark.frequency - args.lowFreq) * args.freqToPixelRatio); double centerXpos = args.fftRectMin.x + std::round((bm.bookmark.frequency - args.lowFreq) * args.freqToPixelRatio);
ImVec2 nameSize = ImGui::CalcTextSize(bm.bookmarkName.c_str()); ImVec2 nameSize = ImGui::CalcTextSize(bm.bookmarkName.c_str());
ImVec2 rectMin = ImVec2(centerXpos-(nameSize.x/2)-5, args.fftRectMin.y); ImVec2 rectMin = ImVec2(centerXpos-(nameSize.x/2)-5, args.fftRectMin.y);
@ -696,7 +699,9 @@ private:
} }
} }
else if (_this->bookmarkDisplayMode == BOOKMARK_DISP_MODE_BOTTOM) { else if (_this->bookmarkDisplayMode == BOOKMARK_DISP_MODE_BOTTOM) {
for (auto const bm : _this->waterfallBookmarks) { int count = _this->waterfallBookmarks.size();
for (int i = count-1; i >= 0; i--) {
auto& bm = _this->waterfallBookmarks[i];
double centerXpos = args.fftRectMin.x + std::round((bm.bookmark.frequency - args.lowFreq) * args.freqToPixelRatio); double centerXpos = args.fftRectMin.x + std::round((bm.bookmark.frequency - args.lowFreq) * args.freqToPixelRatio);
ImVec2 nameSize = ImGui::CalcTextSize(bm.bookmarkName.c_str()); ImVec2 nameSize = ImGui::CalcTextSize(bm.bookmarkName.c_str());
ImVec2 rectMin = ImVec2(centerXpos-(nameSize.x/2)-5, args.fftRectMax.y-nameSize.y); ImVec2 rectMin = ImVec2(centerXpos-(nameSize.x/2)-5, args.fftRectMax.y-nameSize.y);
@ -808,7 +813,7 @@ private:
std::map<std::string, FrequencyBookmark> bookmarks; std::map<std::string, FrequencyBookmark> bookmarks;
std::string editedBookmarkName = ""; std::string editedBookmarkName = "";
std::string firstEeditedBookmarkName = ""; std::string firstEditedBookmarkName = "";
FrequencyBookmark editedBookmark; FrequencyBookmark editedBookmark;
std::vector<std::string> listNames; std::vector<std::string> listNames;
@ -861,5 +866,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
} }
MOD_EXPORT void _END_() { MOD_EXPORT void _END_() {
// Nothing here config.disableAutoSave();
config.save();
} }

View File

@ -10,6 +10,8 @@
#include <meteor_demodulator_interface.h> #include <meteor_demodulator_interface.h>
#include <config.h> #include <config.h>
#include <options.h> #include <options.h>
#include <cctype>
#include <radio_interface.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
#define MAX_COMMAND_LENGTH 8192 #define MAX_COMMAND_LENGTH 8192
@ -360,16 +362,27 @@ private:
// NOTE: THIS STUFF ISN'T THREADSAFE AND WILL LIKELY BREAK. // NOTE: THIS STUFF ISN'T THREADSAFE AND WILL LIKELY BREAK.
// Execute commands // If the command is empty, do nothing
if (parts.size() == 0) { return; } if (parts.size() == 0) { return; }
else if (parts[0].at(0) == '\\') { // Check to see if command is longform
parts[0].replace(0,1,""); // Remove leading backslash // If the command is a compound command, execute each one separately
if (parts[0] == "set_freq") { if (parts[0].size() > 1 && parts[0][0] != '\\') {
std::string arguments;
if (parts.size() > 1) { arguments = cmd.substr(parts[0].size()); }
for (char c : parts[0]) {
commandHandler(c + arguments);
}
return;
}
spdlog::info("Rigctl command: '{0}'", cmd);
// Otherwise, execute the command
if (parts[0] == "F" || parts[0] == "\\set_freq") {
std::lock_guard lck(vfoMtx); std::lock_guard lck(vfoMtx);
// if number of arguments isn't correct, return error // if number of arguments isn't correct, return error
if (parts.size() != 2) { if (parts.size() != 2) {
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n"; resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
return; return;
@ -379,7 +392,6 @@ private:
if (!tuningEnabled) { if (!tuningEnabled) {
resp = "RPRT 0\n"; resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
return;
} }
// Parse frequency and assign it to the VFO // Parse frequency and assign it to the VFO
@ -388,7 +400,7 @@ private:
resp = "RPRT 0\n"; resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
else if (parts[0] == "get_freq") { else if (parts[0] == "f" || parts[0] == "\\get_freq") {
std::lock_guard lck(vfoMtx); std::lock_guard lck(vfoMtx);
// Get center frequency of the SDR // Get center frequency of the SDR
@ -404,115 +416,142 @@ private:
sprintf(buf, "%" PRIu64 "\n", (uint64_t)freq); sprintf(buf, "%" PRIu64 "\n", (uint64_t)freq);
client->write(strlen(buf), (uint8_t*)buf); client->write(strlen(buf), (uint8_t*)buf);
} }
else if (parts[0] == "set_mode") { else if (parts[0] == "M" || parts[0] == "\\set_mode") {
std::lock_guard lck(vfoMtx); std::lock_guard lck(vfoMtx);
// if number of arguments isn't correct, return error
if (parts.size() != 3) {
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// If not controlling the VFO, return
if (!tuningEnabled) {
resp = "RPRT 0\n"; resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// If client is querying, respond accordingly // If client is querying, respond accordingly
if(parts[1] == "?") { if (parts.size() >= 2 && parts[1] == "?") {
resp = "FM WFM AM DSB USB CW LSB RAW"; resp = "FM WFM AM DSB USB CW LSB RAW\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
return; return;
} }
// Parse mode and bandwidth // if number of arguments isn't correct or the VFO is not "VFO", return error
int mode; if (parts.size() != 3) {
if(parts[1] == "FM"){
mode = RADIO_IFACE_MODE_NFM;
}else if(parts[1] == "WFM"){
mode = RADIO_IFACE_MODE_WFM;
}else if(parts[1] == "AM"){
mode = RADIO_IFACE_MODE_AM;
}else if(parts[1] == "DSB"){
mode = RADIO_IFACE_MODE_DSB;
}else if(parts[1] == "USB"){
mode = RADIO_IFACE_MODE_USB;
}else if(parts[1] == "CW"){
mode = RADIO_IFACE_MODE_CW;
}else if(parts[1] == "LSB"){
mode = RADIO_IFACE_MODE_LSB;
}else if(parts[1] == "RAW"){
mode = RADIO_IFACE_MODE_RAW;
}else{
// If mode is not supported, return error
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n"; resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
return; return;
} }
int bandwidth = std::stoi(parts[2]);
// Set mode and bandwidth and respond // Check that the bandwidth is an integer
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_SET_MODE, &mode, 0); for (char c : parts[2]) {
sigpath::vfoManager.setBandwidth(selectedVfo, bandwidth, true); if (!std::isdigit(c)) {
resp = "RPRT 0\n"; resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
}
float newBandwidth = std::atoi(parts[2].c_str());
int newMode;
if (parts[1] == "FM") {
newMode = RADIO_IFACE_MODE_NFM;
}
else if (parts[1] == "WFM") {
newMode = RADIO_IFACE_MODE_WFM;
}
else if (parts[1] == "AM") {
newMode = RADIO_IFACE_MODE_AM;
}
else if (parts[1] == "DSB") {
newMode = RADIO_IFACE_MODE_DSB;
}
else if (parts[1] == "USB") {
newMode = RADIO_IFACE_MODE_USB;
}
else if (parts[1] == "CW") {
newMode = RADIO_IFACE_MODE_CW;
}
else if (parts[1] == "LSB") {
newMode = RADIO_IFACE_MODE_LSB;
}
else if (parts[1] == "RAW") {
newMode = RADIO_IFACE_MODE_RAW;
}
else {
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// If tuning is enabled, set the mode and optionally the bandwidth
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio" && tuningEnabled) {
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_SET_MODE, &newMode, NULL);
if (newBandwidth) {
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_SET_BANDWIDTH, &newBandwidth, NULL);
}
}
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
else if (parts[0] == "get_mode") { else if (parts[0] == "m" || parts[0] == "\\get_mode") {
std::lock_guard lck(vfoMtx); std::lock_guard lck(vfoMtx);
resp = "RAW ";
// Initialize output stream if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio") {
std::stringstream buf;
// Get mode enum and parse to the output stream
int mode; int mode;
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, 0, &mode); core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, NULL, &mode);
switch(mode) {
case RADIO_IFACE_MODE_NFM : buf << "FM\n"; if (mode == RADIO_IFACE_MODE_NFM) {
break; resp = "FM ";
case RADIO_IFACE_MODE_WFM : buf << "WFM\n";
break;
case RADIO_IFACE_MODE_AM : buf << "AM\n";
break;
case RADIO_IFACE_MODE_DSB : buf << "DSB\n";
break;
case RADIO_IFACE_MODE_USB : buf << "USB\n";
break;
case RADIO_IFACE_MODE_CW : buf << "CW\n";
break;
case RADIO_IFACE_MODE_LSB : buf << "LSB\n";
break;
case RADIO_IFACE_MODE_RAW : buf << "RAW\n";
break;
} }
// Send bandwidth to output stream and respond else if (mode == RADIO_IFACE_MODE_WFM) {
buf << sigpath::vfoManager.getBandwidth(selectedVfo) << "\n"; resp = "WFM ";
resp = buf.str(); }
else if (mode == RADIO_IFACE_MODE_AM) {
resp = "AM ";
}
else if (mode == RADIO_IFACE_MODE_DSB) {
resp = "DSB ";
}
else if (mode == RADIO_IFACE_MODE_USB) {
resp = "USB ";
}
else if (mode == RADIO_IFACE_MODE_CW) {
resp = "CW ";
}
else if (mode == RADIO_IFACE_MODE_LSB) {
resp = "LSB ";
}
}
if (!selectedVfo.empty()) {
resp += std::to_string((int)sigpath::vfoManager.getBandwidth(selectedVfo)) + "\n";
}
else {
resp += "0\n";
}
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
else if (parts[0] == "set_vfo") { else if (parts[0] == "V" || parts[0] == "\\set_vfo") {
// if number of arguments isn't correct, return error std::lock_guard lck(vfoMtx);
resp = "RPRT 0\n";
// if number of arguments isn't correct or the VFO is not "VFO", return error
if (parts.size() != 2) { if (parts.size() != 2) {
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n"; resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
return; return;
} }
// Respond if (parts[1] == "?") {
resp = "RPRT 0\n"; resp = "VFO\n";
}
else if (parts[1] != "VFO") {
resp = "RPRT 1\n";
}
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
else if (parts[0] == "get_vfo") { else if (parts[0] == "v" || parts[0] == "\\get_vfo") {
// Respond with VFO std::lock_guard lck(vfoMtx);
resp = "VFO\n"; resp = "VFO\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
else if (parts[0] == "recorder_start") { else if (parts[0] == "\\recorder_start") {
std::lock_guard lck(recorderMtx); std::lock_guard lck(recorderMtx);
// If not controlling the recorder, return // If not controlling the recorder, return
@ -534,7 +573,7 @@ private:
resp = "RPRT 0\n"; resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
else if (parts[0] == "recorder_stop") { else if (parts[0] == "\\recorder_stop") {
std::lock_guard lck(recorderMtx); std::lock_guard lck(recorderMtx);
// If not controlling the recorder, return // If not controlling the recorder, return
@ -567,169 +606,6 @@ private:
return; return;
} }
} }
else {
for(int i = 0; i < parts[0].length(); i++){ // Loop adds support for compound commands
if (parts[0].at(i) == 'F') {
std::lock_guard lck(vfoMtx);
// if number of arguments isn't correct, return error
if (parts.size() != 2) {
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// If not controlling the VFO, return
if (!tuningEnabled) {
resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// Parse frequency and assign it to the VFO
long long freq = std::stoll(parts[1]);
tuner::tune(tuner::TUNER_MODE_NORMAL, selectedVfo, freq);
resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
}
else if (parts[0].at(i) == 'f') {
std::lock_guard lck(vfoMtx);
// Get center frequency of the SDR
double freq = gui::waterfall.getCenterFrequency();
// Add the offset of the VFO if it exists
if (sigpath::vfoManager.vfoExists(selectedVfo)) {
freq += sigpath::vfoManager.getOffset(selectedVfo);
}
// Respond with the frequency
char buf[128];
sprintf(buf, "%" PRIu64 "\n", (uint64_t)freq);
client->write(strlen(buf), (uint8_t*)buf);
}
else if (parts[0].at(i) == 'M') {
std::lock_guard lck(vfoMtx);
// if number of arguments isn't correct, return error
if (parts.size() != 3) {
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// If not controlling the VFO, return
if (!tuningEnabled) {
resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// If client is querying, respond accordingly
if(parts[1] == "?") {
resp = "FM WFM AM DSB USB CW LSB RAW";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// Parse mode and bandwidth
int mode;
if(parts[1] == "FM"){
mode = RADIO_IFACE_MODE_NFM;
}else if(parts[1] == "WFM"){
mode = RADIO_IFACE_MODE_WFM;
}else if(parts[1] == "AM"){
mode = RADIO_IFACE_MODE_AM;
}else if(parts[1] == "DSB"){
mode = RADIO_IFACE_MODE_DSB;
}else if(parts[1] == "USB"){
mode = RADIO_IFACE_MODE_USB;
}else if(parts[1] == "CW"){
mode = RADIO_IFACE_MODE_CW;
}else if(parts[1] == "LSB"){
mode = RADIO_IFACE_MODE_LSB;
}else if(parts[1] == "RAW"){
mode = RADIO_IFACE_MODE_RAW;
}else{
// If mode is not supported, return error
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
int bandwidth = std::stoi(parts[2]);
// Set mode and bandwidth and respond
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_SET_MODE, &mode, 0);
sigpath::vfoManager.setBandwidth(selectedVfo, bandwidth, true);
resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
}
else if (parts[0].at(i) == 'm') {
std::lock_guard lck(vfoMtx);
// Initialize output stream
std::stringstream buf;
// Get mode enum and parse to the output stream
int mode;
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, 0, &mode);
switch(mode) {
case RADIO_IFACE_MODE_NFM : buf << "FM\n";
break;
case RADIO_IFACE_MODE_WFM : buf << "WFM\n";
break;
case RADIO_IFACE_MODE_AM : buf << "AM\n";
break;
case RADIO_IFACE_MODE_DSB : buf << "DSB\n";
break;
case RADIO_IFACE_MODE_USB : buf << "USB\n";
break;
case RADIO_IFACE_MODE_CW : buf << "CW\n";
break;
case RADIO_IFACE_MODE_LSB : buf << "LSB\n";
break;
case RADIO_IFACE_MODE_RAW : buf << "RAW\n";
break;
}
// Send bandwidth to output stream and respond
buf << sigpath::vfoManager.getBandwidth(selectedVfo) << "\n";
resp = buf.str();
client->write(resp.size(), (uint8_t*)resp.c_str());
}
else if (parts[0].at(i) == 'V') {
// if number of arguments isn't correct, return error
if (parts.size() != 3) {
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
// Respond
resp = "RPRT 0\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
}
else if (parts[0].at(i) == 'v') {
// Respond with VFO
resp = "VFO\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
}
else if (parts[0].at(i) == 'q') {
// Will close automatically
}
else {
// If command is not recognized, return error
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
return;
}
}
}
}
std::string name; std::string name;
bool enabled = true; bool enabled = true;

View File

@ -68,7 +68,7 @@ public:
directSamplingMode = config.conf["directSamplingMode"]; directSamplingMode = config.conf["directSamplingMode"];
rtlAGC = config.conf["rtlAGC"]; rtlAGC = config.conf["rtlAGC"];
tunerAGC = config.conf["tunerAGC"]; tunerAGC = config.conf["tunerAGC"];
gain = config.conf["gainIndex"]; gain = std::clamp<int>(config.conf["gainIndex"], 0, 28);
biasTee = config.conf["biasTee"]; biasTee = config.conf["biasTee"];
offsetTuning = config.conf["offsetTuning"]; offsetTuning = config.conf["offsetTuning"];
hostStr = hostStr.substr(0, 1023); hostStr = hostStr.substr(0, 1023);
@ -127,12 +127,20 @@ private:
spdlog::warn("Setting sample rate to {0}", _this->sampleRate); spdlog::warn("Setting sample rate to {0}", _this->sampleRate);
_this->client.setFrequency(_this->freq); _this->client.setFrequency(_this->freq);
_this->client.setSampleRate(_this->sampleRate); _this->client.setSampleRate(_this->sampleRate);
_this->client.setGainMode(!_this->tunerAGC);
_this->client.setDirectSampling(_this->directSamplingMode); _this->client.setDirectSampling(_this->directSamplingMode);
_this->client.setAGCMode(_this->rtlAGC); _this->client.setAGCMode(_this->rtlAGC);
_this->client.setGainIndex(_this->gain);
_this->client.setBiasTee(_this->biasTee); _this->client.setBiasTee(_this->biasTee);
_this->client.setOffsetTuning(_this->offsetTuning); _this->client.setOffsetTuning(_this->offsetTuning);
if (_this->tunerAGC) {
_this->client.setGainMode(0);
}
else {
_this->client.setGainMode(1);
// Setting it twice because for some reason it refuses to do it on the first time
_this->client.setGainIndex(_this->gain);
}
_this->running = true; _this->running = true;
_this->workerThread = std::thread(worker, _this); _this->workerThread = std::thread(worker, _this);
spdlog::info("RTLTCPSourceModule '{0}': Start!", _this->name); spdlog::info("RTLTCPSourceModule '{0}': Start!", _this->name);
@ -229,7 +237,7 @@ private:
if (_this->tunerAGC) { style::beginDisabled(); } if (_this->tunerAGC) { style::beginDisabled(); }
ImGui::SetNextItemWidth(menuWidth); ImGui::SetNextItemWidth(menuWidth);
if (ImGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 29, "")) { if (ImGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 28, "")) {
if (_this->running) { if (_this->running) {
_this->client.setGainIndex(_this->gain); _this->client.setGainIndex(_this->gain);
} }