mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-25 02:18:30 +01:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
d84273a0db
@ -47,51 +47,49 @@ void ConfigManager::save(bool lock) {
|
||||
}
|
||||
|
||||
void ConfigManager::enableAutoSave() {
|
||||
if (!autoSaveEnabled) {
|
||||
autoSaveEnabled = true;
|
||||
termFlag = false;
|
||||
autoSaveThread = std::thread(autoSaveWorker, this);
|
||||
}
|
||||
if (autoSaveEnabled) { return; }
|
||||
autoSaveEnabled = true;
|
||||
termFlag = false;
|
||||
autoSaveThread = std::thread(&ConfigManager::autoSaveWorker, this);
|
||||
}
|
||||
|
||||
void ConfigManager::disableAutoSave() {
|
||||
if (autoSaveEnabled) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(termMtx);
|
||||
autoSaveEnabled = false;
|
||||
termFlag = true;
|
||||
}
|
||||
termCond.notify_one();
|
||||
if (autoSaveThread.joinable()) { autoSaveThread.join(); }
|
||||
if (!autoSaveEnabled) { return; }
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(termMtx);
|
||||
autoSaveEnabled = false;
|
||||
termFlag = true;
|
||||
}
|
||||
termCond.notify_one();
|
||||
if (autoSaveThread.joinable()) { autoSaveThread.join(); }
|
||||
}
|
||||
|
||||
void ConfigManager::acquire() {
|
||||
mtx.lock();
|
||||
}
|
||||
|
||||
void ConfigManager::release(bool changed) {
|
||||
this->changed |= changed;
|
||||
void ConfigManager::release(bool modified) {
|
||||
changed |= modified;
|
||||
mtx.unlock();
|
||||
}
|
||||
|
||||
void ConfigManager::autoSaveWorker(ConfigManager* _this) {
|
||||
while (_this->autoSaveEnabled) {
|
||||
if (!_this->mtx.try_lock()) {
|
||||
void ConfigManager::autoSaveWorker() {
|
||||
while (autoSaveEnabled) {
|
||||
if (!mtx.try_lock()) {
|
||||
spdlog::warn("ConfigManager locked, waiting...");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
continue;
|
||||
}
|
||||
if (_this->changed) {
|
||||
_this->changed = false;
|
||||
_this->save(false);
|
||||
if (changed) {
|
||||
changed = false;
|
||||
save(false);
|
||||
}
|
||||
_this->mtx.unlock();
|
||||
mtx.unlock();
|
||||
|
||||
// Sleep but listen for wakeup call
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_this->termMtx);
|
||||
_this->termCond.wait_for(lock, std::chrono::milliseconds(1000), [_this]() { return _this->termFlag; } );
|
||||
std::unique_lock<std::mutex> lock(termMtx);
|
||||
termCond.wait_for(lock, std::chrono::milliseconds(1000), [this]() { return termFlag; } );
|
||||
}
|
||||
}
|
||||
}
|
@ -17,21 +17,21 @@ public:
|
||||
void enableAutoSave();
|
||||
void disableAutoSave();
|
||||
void acquire();
|
||||
void release(bool changed = false);
|
||||
void release(bool modified = false);
|
||||
|
||||
json conf;
|
||||
|
||||
private:
|
||||
static void autoSaveWorker(ConfigManager* _this);
|
||||
void autoSaveWorker();
|
||||
|
||||
std::string path = "";
|
||||
bool changed = false;
|
||||
bool autoSaveEnabled = false;
|
||||
volatile bool changed = false;
|
||||
volatile bool autoSaveEnabled = false;
|
||||
std::thread autoSaveThread;
|
||||
std::mutex mtx;
|
||||
|
||||
std::mutex termMtx;
|
||||
std::condition_variable termCond;
|
||||
bool termFlag = false;
|
||||
volatile bool termFlag = false;
|
||||
|
||||
};
|
@ -206,6 +206,7 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
defConfig["showWaterfall"] = true;
|
||||
defConfig["source"] = "";
|
||||
defConfig["decimationPower"] = 0;
|
||||
defConfig["iqCorrection"] = false;
|
||||
|
||||
defConfig["streams"]["Radio"]["muted"] = false;
|
||||
defConfig["streams"]["Radio"]["sink"] = "Audio";
|
||||
@ -462,6 +463,11 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
glfwSwapBuffers(core::window);
|
||||
}
|
||||
|
||||
// Shut down all modules
|
||||
for (auto& [name, mod] : core::moduleManager.modules) {
|
||||
mod.end();
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
@ -472,5 +478,8 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
|
||||
sigpath::signalPath.stop();
|
||||
|
||||
core::configManager.disableAutoSave();
|
||||
core::configManager.save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -24,9 +24,10 @@ namespace sdrpp_credits {
|
||||
|
||||
const char* libraries[] = {
|
||||
"Dear ImGui (ocornut)",
|
||||
"fftw3 (fftw.org)",
|
||||
"glew (Nigel Stewart)",
|
||||
"glfw (Camilla Löwy)",
|
||||
"json (nlohmann)",
|
||||
"RtAudio",
|
||||
"SoapySDR (PothosWare)",
|
||||
"spdlog (gabime)",
|
||||
"Portable File Dialogs"
|
||||
};
|
||||
|
76
core/src/dsp/correction.h
Normal file
76
core/src/dsp/correction.h
Normal 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;
|
||||
|
||||
|
||||
};
|
||||
}
|
@ -15,6 +15,10 @@ namespace credits {
|
||||
|
||||
void show() {
|
||||
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::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
|
||||
|
||||
@ -55,11 +59,8 @@ namespace credits {
|
||||
ImGui::Spacing();
|
||||
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::PopStyleVar(1);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
@ -404,9 +404,11 @@ void MainWindow::draw() {
|
||||
|
||||
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::SetNextItemWidth(300);
|
||||
ImGui::SetNextItemWidth(snrWidth);
|
||||
ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
@ -108,7 +108,7 @@ namespace displaymenu {
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
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);
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["fftRate"] = fftRate;
|
||||
|
@ -12,6 +12,7 @@ namespace sourecmenu {
|
||||
double customOffset = 0.0;
|
||||
double effectiveOffset = 0.0;
|
||||
int decimationPower = 0;
|
||||
bool iqCorrection = false;
|
||||
|
||||
EventHandler<std::string> sourceRegisteredHandler;
|
||||
EventHandler<std::string> sourceUnregisterHandler;
|
||||
@ -124,6 +125,8 @@ namespace sourecmenu {
|
||||
customOffset = core::configManager.conf["offset"];
|
||||
offsetMode = core::configManager.conf["offsetMode"];
|
||||
decimationPower = core::configManager.conf["decimationPower"];
|
||||
iqCorrection = core::configManager.conf["iqCorrection"];
|
||||
sigpath::signalPath.setIQCorrection(iqCorrection);
|
||||
updateOffset();
|
||||
|
||||
refreshSources();
|
||||
@ -158,6 +161,13 @@ namespace sourecmenu {
|
||||
|
||||
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::SameLine();
|
||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||
|
@ -362,7 +362,7 @@ namespace ImGui {
|
||||
|
||||
// If the mouse wheel is moved on the frequency scale
|
||||
if (mouseWheel != 0 && mouseInFreq) {
|
||||
viewOffset -= (double)mouseWheel * viewBandwidth / 20.0;
|
||||
viewOffset -= (double)mouseWheel * viewBandwidth / 20.0;
|
||||
|
||||
if (viewOffset + (viewBandwidth / 2.0) > wholeBandwidth / 2.0) {
|
||||
double freqOffset = (viewOffset + (viewBandwidth / 2.0)) - (wholeBandwidth / 2.0);
|
||||
@ -520,7 +520,7 @@ namespace ImGui {
|
||||
float pixel;
|
||||
float dataRange = waterfallMax - waterfallMin;
|
||||
int count = std::min<float>(waterfallHeight, fftLines);
|
||||
if (rawFFTs != NULL) {
|
||||
if (rawFFTs != NULL && fftLines >= 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize;
|
||||
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))];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = count; i < waterfallHeight; i++) {
|
||||
for (int j = 0; j < dataWidth; j++) {
|
||||
waterfallFb[(i * dataWidth) + j] = (uint32_t)255 << 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] tempData;
|
||||
waterfallUpdate = true;
|
||||
@ -1028,15 +1034,16 @@ namespace ImGui {
|
||||
void WaterFall::setRawFFTSize(int size, bool lock) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
rawFFTSize = size;
|
||||
int wfSize = std::max<int>(1, waterfallHeight);
|
||||
if (rawFFTs != NULL) {
|
||||
int wfSize = std::max<int>(1, waterfallHeight);
|
||||
rawFFTs = (float*)realloc(rawFFTs, rawFFTSize * wfSize * sizeof(float));
|
||||
}
|
||||
else {
|
||||
int wfSize = std::max<int>(1, waterfallHeight);
|
||||
rawFFTs = (float*)malloc(rawFFTSize * wfSize * sizeof(float));
|
||||
}
|
||||
fftLines = 0;
|
||||
memset(rawFFTs, 0, rawFFTSize * waterfallHeight * sizeof(float));
|
||||
updateWaterfallFb();
|
||||
}
|
||||
|
||||
void WaterFall::setBandPlanPos(int pos) {
|
||||
|
@ -16,6 +16,7 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream
|
||||
|
||||
// split.init(input);
|
||||
inputBuffer.init(input);
|
||||
corrector.init(&inputBuffer.out, 50.0f / sampleRate);
|
||||
split.init(&inputBuffer.out);
|
||||
|
||||
reshape.init(&fftStream, fftSize, (sampleRate / fftRate) - fftSize);
|
||||
@ -43,6 +44,8 @@ void SignalPath::setSampleRate(double sampleRate) {
|
||||
vfo.vfo->start();
|
||||
}
|
||||
|
||||
corrector.setCorrectionRate(50.0f / sampleRate);
|
||||
|
||||
split.start();
|
||||
}
|
||||
|
||||
@ -55,6 +58,7 @@ void SignalPath::start() {
|
||||
decimator->start();
|
||||
}
|
||||
inputBuffer.start();
|
||||
if (iqCorrection) { corrector.start(); }
|
||||
split.start();
|
||||
reshape.start();
|
||||
fftHandlerSink.start();
|
||||
@ -66,6 +70,7 @@ void SignalPath::stop() {
|
||||
decimator->stop();
|
||||
}
|
||||
inputBuffer.stop();
|
||||
if (iqCorrection) { corrector.stop(); }
|
||||
split.stop();
|
||||
reshape.stop();
|
||||
fftHandlerSink.stop();
|
||||
@ -159,7 +164,13 @@ void SignalPath::setDecimation(int dec) {
|
||||
|
||||
// If no decimation, reconnect
|
||||
if (!dec) {
|
||||
split.setInput(&inputBuffer.out);
|
||||
if (iqCorrection) {
|
||||
split.setInput(&corrector.out);
|
||||
}
|
||||
else {
|
||||
split.setInput(&inputBuffer.out);
|
||||
}
|
||||
|
||||
if (running) { split.start(); }
|
||||
core::setInputSampleRate(sourceSampleRate);
|
||||
return;
|
||||
@ -167,7 +178,17 @@ void SignalPath::setDecimation(int dec) {
|
||||
|
||||
// Create new decimators
|
||||
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(); }
|
||||
decimators.push_back(decimator);
|
||||
}
|
||||
@ -176,4 +197,33 @@ void SignalPath::setDecimation(int dec) {
|
||||
|
||||
// Update the DSP sample rate
|
||||
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;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
#include <map>
|
||||
#include <dsp/sink.h>
|
||||
#include <dsp/decimation.h>
|
||||
#include <dsp/correction.h>
|
||||
|
||||
class SignalPath {
|
||||
public:
|
||||
@ -24,6 +25,7 @@ public:
|
||||
void stopFFT();
|
||||
void setBuffering(bool enabled);
|
||||
void setDecimation(int dec);
|
||||
void setIQCorrection(bool enabled);
|
||||
|
||||
dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer;
|
||||
double sourceSampleRate = 0;
|
||||
@ -36,6 +38,7 @@ private:
|
||||
};
|
||||
|
||||
dsp::Splitter<dsp::complex_t> split;
|
||||
dsp::IQCorrector corrector;
|
||||
|
||||
// FFT
|
||||
dsp::stream<dsp::complex_t> fftStream;
|
||||
@ -53,4 +56,5 @@ private:
|
||||
int inputBlockSize;
|
||||
bool bufferingEnabled = false;
|
||||
bool running = false;
|
||||
bool iqCorrection = false;
|
||||
};
|
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define VERSION_STR "1.0.0_rc1"
|
||||
#define VERSION_STR "1.0.0_rc2"
|
@ -198,19 +198,20 @@ private:
|
||||
|
||||
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")) {
|
||||
open = false;
|
||||
|
||||
// If editing, delete the original one
|
||||
if (editOpen) {
|
||||
bookmarks.erase(firstEeditedBookmarkName);
|
||||
bookmarks.erase(firstEditedBookmarkName);
|
||||
}
|
||||
bookmarks[nameBuf] = editedBookmark;
|
||||
bookmarks[editedBookmarkName] = editedBookmark;
|
||||
|
||||
saveByName(selectedListName);
|
||||
}
|
||||
if (strlen(nameBuf) == 0) { style::endDisabled(); }
|
||||
if (applyDisabled) { style::endDisabled(); }
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
open = false;
|
||||
@ -463,7 +464,7 @@ private:
|
||||
|
||||
_this->editedBookmark.selected = false;
|
||||
|
||||
_this->editOpen = true;
|
||||
_this->createOpen = true;
|
||||
|
||||
// Find new unique default name
|
||||
if (_this->bookmarks.find("New Bookmark") == _this->bookmarks.end()) {
|
||||
@ -492,7 +493,7 @@ private:
|
||||
_this->editOpen = true;
|
||||
_this->editedBookmark = _this->bookmarks[selectedNames[0]];
|
||||
_this->editedBookmarkName = selectedNames[0];
|
||||
_this->firstEeditedBookmarkName = selectedNames[0];
|
||||
_this->firstEditedBookmarkName = selectedNames[0];
|
||||
}
|
||||
if (selectedNames.size() != 1 && _this->selectedListName != "") { style::endDisabled(); }
|
||||
|
||||
@ -679,7 +680,9 @@ private:
|
||||
std::string hoveredBookmarkName;
|
||||
|
||||
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);
|
||||
ImVec2 nameSize = ImGui::CalcTextSize(bm.bookmarkName.c_str());
|
||||
ImVec2 rectMin = ImVec2(centerXpos-(nameSize.x/2)-5, args.fftRectMin.y);
|
||||
@ -696,7 +699,9 @@ private:
|
||||
}
|
||||
}
|
||||
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);
|
||||
ImVec2 nameSize = ImGui::CalcTextSize(bm.bookmarkName.c_str());
|
||||
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::string editedBookmarkName = "";
|
||||
std::string firstEeditedBookmarkName = "";
|
||||
std::string firstEditedBookmarkName = "";
|
||||
FrequencyBookmark editedBookmark;
|
||||
|
||||
std::vector<std::string> listNames;
|
||||
@ -861,5 +866,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
// Nothing here
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -10,6 +10,8 @@
|
||||
#include <meteor_demodulator_interface.h>
|
||||
#include <config.h>
|
||||
#include <options.h>
|
||||
#include <cctype>
|
||||
#include <radio_interface.h>
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
#define MAX_COMMAND_LENGTH 8192
|
||||
@ -360,374 +362,248 @@ private:
|
||||
|
||||
// NOTE: THIS STUFF ISN'T THREADSAFE AND WILL LIKELY BREAK.
|
||||
|
||||
// Execute commands
|
||||
// If the command is empty, do nothing
|
||||
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 (parts[0] == "set_freq") {
|
||||
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());
|
||||
// If the command is a compound command, execute each one separately
|
||||
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);
|
||||
}
|
||||
else if (parts[0] == "get_freq") {
|
||||
std::lock_guard lck(vfoMtx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get center frequency of the SDR
|
||||
double freq = gui::waterfall.getCenterFrequency();
|
||||
spdlog::info("Rigctl command: '{0}'", cmd);
|
||||
|
||||
// Add the offset of the VFO if it exists
|
||||
if (sigpath::vfoManager.vfoExists(selectedVfo)) {
|
||||
freq += sigpath::vfoManager.getOffset(selectedVfo);
|
||||
}
|
||||
// Otherwise, execute the command
|
||||
if (parts[0] == "F" || parts[0] == "\\set_freq") {
|
||||
std::lock_guard lck(vfoMtx);
|
||||
|
||||
// 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] == "set_mode") {
|
||||
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] == "get_mode") {
|
||||
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] == "set_vfo") {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Respond
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "get_vfo") {
|
||||
// Respond with VFO
|
||||
resp = "VFO\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "recorder_start") {
|
||||
std::lock_guard lck(recorderMtx);
|
||||
|
||||
// If not controlling the recorder, return
|
||||
if (!recordingEnabled) {
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the command to the selected recorder
|
||||
if (recorderType == RECORDER_TYPE_METEOR_DEMODULATOR) {
|
||||
core::modComManager.callInterface(selectedRecorder, METEOR_DEMODULATOR_IFACE_CMD_START, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
core::modComManager.callInterface(selectedRecorder, RECORDER_IFACE_CMD_START, NULL, NULL);
|
||||
}
|
||||
|
||||
// Respond with a sucess
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "recorder_stop") {
|
||||
std::lock_guard lck(recorderMtx);
|
||||
|
||||
// If not controlling the recorder, return
|
||||
if (!recordingEnabled) {
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the command to the selected recorder
|
||||
if (recorderType == RECORDER_TYPE_METEOR_DEMODULATOR) {
|
||||
core::modComManager.callInterface(selectedRecorder, METEOR_DEMODULATOR_IFACE_CMD_STOP, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
core::modComManager.callInterface(selectedRecorder, RECORDER_IFACE_CMD_STOP, NULL, NULL);
|
||||
}
|
||||
|
||||
// Respond with a sucess
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "quit") {
|
||||
// Will close automatically
|
||||
}
|
||||
else {
|
||||
// If command is not recognized, return error
|
||||
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
|
||||
// if number of arguments isn't correct, return error
|
||||
if (parts.size() != 2) {
|
||||
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());
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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);
|
||||
else if (parts[0] == "f" || parts[0] == "\\get_freq") {
|
||||
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;
|
||||
}
|
||||
// Get center frequency of the SDR
|
||||
double freq = gui::waterfall.getCenterFrequency();
|
||||
|
||||
// If not controlling the VFO, return
|
||||
if (!tuningEnabled) {
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
// Add the offset of the VFO if it exists
|
||||
if (sigpath::vfoManager.vfoExists(selectedVfo)) {
|
||||
freq += sigpath::vfoManager.getOffset(selectedVfo);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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] == "M" || parts[0] == "\\set_mode") {
|
||||
std::lock_guard lck(vfoMtx);
|
||||
resp = "RPRT 0\n";
|
||||
|
||||
// Get center frequency of the SDR
|
||||
double freq = gui::waterfall.getCenterFrequency();
|
||||
// If client is querying, respond accordingly
|
||||
if (parts.size() >= 2 && parts[1] == "?") {
|
||||
resp = "FM WFM AM DSB USB CW LSB RAW\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the offset of the VFO if it exists
|
||||
if (sigpath::vfoManager.vfoExists(selectedVfo)) {
|
||||
freq += sigpath::vfoManager.getOffset(selectedVfo);
|
||||
}
|
||||
// if number of arguments isn't correct or the VFO is not "VFO", return error
|
||||
if (parts.size() != 3) {
|
||||
resp = "RPRT 1\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Check that the bandwidth is an integer
|
||||
for (char c : parts[2]) {
|
||||
if (!std::isdigit(c)) {
|
||||
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());
|
||||
}
|
||||
else if (parts[0] == "m" || parts[0] == "\\get_mode") {
|
||||
std::lock_guard lck(vfoMtx);
|
||||
resp = "RAW ";
|
||||
|
||||
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio") {
|
||||
int mode;
|
||||
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, NULL, &mode);
|
||||
|
||||
if (mode == RADIO_IFACE_MODE_NFM) {
|
||||
resp = "FM ";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_WFM) {
|
||||
resp = "WFM ";
|
||||
}
|
||||
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());
|
||||
}
|
||||
else if (parts[0] == "V" || parts[0] == "\\set_vfo") {
|
||||
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) {
|
||||
resp = "RPRT 1\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (parts[1] == "?") {
|
||||
resp = "VFO\n";
|
||||
}
|
||||
else if (parts[1] != "VFO") {
|
||||
resp = "RPRT 1\n";
|
||||
}
|
||||
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "v" || parts[0] == "\\get_vfo") {
|
||||
std::lock_guard lck(vfoMtx);
|
||||
resp = "VFO\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "\\recorder_start") {
|
||||
std::lock_guard lck(recorderMtx);
|
||||
|
||||
// If not controlling the recorder, return
|
||||
if (!recordingEnabled) {
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the command to the selected recorder
|
||||
if (recorderType == RECORDER_TYPE_METEOR_DEMODULATOR) {
|
||||
core::modComManager.callInterface(selectedRecorder, METEOR_DEMODULATOR_IFACE_CMD_START, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
core::modComManager.callInterface(selectedRecorder, RECORDER_IFACE_CMD_START, NULL, NULL);
|
||||
}
|
||||
|
||||
// Respond with a sucess
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "\\recorder_stop") {
|
||||
std::lock_guard lck(recorderMtx);
|
||||
|
||||
// If not controlling the recorder, return
|
||||
if (!recordingEnabled) {
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the command to the selected recorder
|
||||
if (recorderType == RECORDER_TYPE_METEOR_DEMODULATOR) {
|
||||
core::modComManager.callInterface(selectedRecorder, METEOR_DEMODULATOR_IFACE_CMD_STOP, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
core::modComManager.callInterface(selectedRecorder, RECORDER_IFACE_CMD_STOP, NULL, NULL);
|
||||
}
|
||||
|
||||
// Respond with a sucess
|
||||
resp = "RPRT 0\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else if (parts[0] == "quit") {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ public:
|
||||
directSamplingMode = config.conf["directSamplingMode"];
|
||||
rtlAGC = config.conf["rtlAGC"];
|
||||
tunerAGC = config.conf["tunerAGC"];
|
||||
gain = config.conf["gainIndex"];
|
||||
gain = std::clamp<int>(config.conf["gainIndex"], 0, 28);
|
||||
biasTee = config.conf["biasTee"];
|
||||
offsetTuning = config.conf["offsetTuning"];
|
||||
hostStr = hostStr.substr(0, 1023);
|
||||
@ -127,12 +127,20 @@ private:
|
||||
spdlog::warn("Setting sample rate to {0}", _this->sampleRate);
|
||||
_this->client.setFrequency(_this->freq);
|
||||
_this->client.setSampleRate(_this->sampleRate);
|
||||
_this->client.setGainMode(!_this->tunerAGC);
|
||||
_this->client.setDirectSampling(_this->directSamplingMode);
|
||||
_this->client.setAGCMode(_this->rtlAGC);
|
||||
_this->client.setGainIndex(_this->gain);
|
||||
_this->client.setBiasTee(_this->biasTee);
|
||||
_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->workerThread = std::thread(worker, _this);
|
||||
spdlog::info("RTLTCPSourceModule '{0}': Start!", _this->name);
|
||||
@ -229,7 +237,7 @@ private:
|
||||
|
||||
if (_this->tunerAGC) { style::beginDisabled(); }
|
||||
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) {
|
||||
_this->client.setGainIndex(_this->gain);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user