mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-26 19:08:30 +01:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
d84273a0db
@ -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;
|
||||||
@ -64,34 +63,33 @@ 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; } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
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() {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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());
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
};
|
};
|
@ -1,3 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION_STR "1.0.0_rc1"
|
#define VERSION_STR "1.0.0_rc2"
|
@ -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();
|
||||||
}
|
}
|
@ -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) {
|
||||||
|
resp = "RPRT 1\n";
|
||||||
|
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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") {
|
if (parts[1] == "FM") {
|
||||||
mode = RADIO_IFACE_MODE_NFM;
|
newMode = RADIO_IFACE_MODE_NFM;
|
||||||
}else if(parts[1] == "WFM"){
|
}
|
||||||
mode = RADIO_IFACE_MODE_WFM;
|
else if (parts[1] == "WFM") {
|
||||||
}else if(parts[1] == "AM"){
|
newMode = RADIO_IFACE_MODE_WFM;
|
||||||
mode = RADIO_IFACE_MODE_AM;
|
}
|
||||||
}else if(parts[1] == "DSB"){
|
else if (parts[1] == "AM") {
|
||||||
mode = RADIO_IFACE_MODE_DSB;
|
newMode = RADIO_IFACE_MODE_AM;
|
||||||
}else if(parts[1] == "USB"){
|
}
|
||||||
mode = RADIO_IFACE_MODE_USB;
|
else if (parts[1] == "DSB") {
|
||||||
}else if(parts[1] == "CW"){
|
newMode = RADIO_IFACE_MODE_DSB;
|
||||||
mode = RADIO_IFACE_MODE_CW;
|
}
|
||||||
}else if(parts[1] == "LSB"){
|
else if (parts[1] == "USB") {
|
||||||
mode = RADIO_IFACE_MODE_LSB;
|
newMode = RADIO_IFACE_MODE_USB;
|
||||||
}else if(parts[1] == "RAW"){
|
}
|
||||||
mode = RADIO_IFACE_MODE_RAW;
|
else if (parts[1] == "CW") {
|
||||||
}else{
|
newMode = RADIO_IFACE_MODE_CW;
|
||||||
// If mode is not supported, return error
|
}
|
||||||
spdlog::error("Rigctl client sent invalid command: '{0}'", cmd);
|
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";
|
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
|
// If tuning is enabled, set the mode and optionally the bandwidth
|
||||||
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_SET_MODE, &mode, 0);
|
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio" && tuningEnabled) {
|
||||||
sigpath::vfoManager.setBandwidth(selectedVfo, bandwidth, true);
|
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_SET_MODE, &newMode, NULL);
|
||||||
resp = "RPRT 0\n";
|
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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user