From acb79fa3bc8400a9b538b4354448fc28133b635f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Mon, 16 Dec 2024 22:21:33 +0100 Subject: [PATCH] WIP: TDR/DFT improvements --- .../LibreVNA-GUI/Traces/Math/dft.cpp | 104 ++++++++++++------ .../LibreVNA-GUI/Traces/Math/dft.h | 2 + .../LibreVNA-GUI/Traces/Math/dftdialog.ui | 20 +++- .../LibreVNA-GUI/Traces/Math/tdr.cpp | 47 ++++++-- .../LibreVNA-GUI/Traces/Math/tdr.h | 4 + .../LibreVNA-GUI/Traces/Math/tdrdialog.ui | 21 +++- 6 files changed, 147 insertions(+), 51 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp index 2e74f853..c9771d9a 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp @@ -17,6 +17,8 @@ using namespace std; Math::DFT::DFT() { automaticDC = true; + removePaddingFromTDR = true; + revertWindowFromTDR = true; DCfreq = 1000000000.0; destructing = false; @@ -67,12 +69,23 @@ void Math::DFT::edit() ui->windowBox->setLayout(new QVBoxLayout); ui->windowBox->layout()->addWidget(window.createEditor()); - connect(ui->DCautomatic, &QRadioButton::toggled, [=](bool automatic){ + connect(ui->removePadding, &QCheckBox::toggled, this, [=](bool remove){ + removePaddingFromTDR = remove; + }); + + connect(ui->revertWindow, &QCheckBox::toggled, this, [=](bool revert){ + revertWindowFromTDR = revert; + }); + + connect(ui->DCautomatic, &QRadioButton::toggled, this, [=](bool automatic){ automaticDC = automatic; ui->freq->setEnabled(!automatic); updateDFT(); }); + ui->removePadding->setChecked(removePaddingFromTDR); + ui->revertWindow->setChecked(revertWindowFromTDR); + if(automaticDC) { ui->DCautomatic->setChecked(true); } else { @@ -84,7 +97,7 @@ void Math::DFT::edit() ui->freq->setPrefixes(" kMG"); ui->freq->setValue(DCfreq); - connect(ui->freq, &SIUnitEdit::valueChanged, [=](double newval){ + connect(ui->freq, &SIUnitEdit::valueChanged, this, [=](double newval){ DCfreq = newval; updateDFT(); }); @@ -111,6 +124,8 @@ nlohmann::json Math::DFT::toJSON() nlohmann::json j; j["automatic_DC"] = automaticDC; j["window"] = window.toJSON(); + j["removePadding"] = removePaddingFromTDR; + j["revertWindow"] = revertWindowFromTDR; if(!automaticDC) { j["DC"] = DCfreq; } @@ -124,6 +139,8 @@ void Math::DFT::fromJSON(nlohmann::json j) if(j.contains("window")) { window.fromJSON(j["window"]); } + removePaddingFromTDR = j.value("removePadding", true); + revertWindowFromTDR = j.value("revertWindow", true); } void Math::DFT::inputSamplesChanged(unsigned int begin, unsigned int end) @@ -169,38 +186,33 @@ void Math::DFTThread::run() qDebug() << "DFT thread exiting"; return; } - // limit update rate if configured in preferences - auto &p = Preferences::getInstance(); - if(p.Acquisition.limitDFT) { - std::this_thread::sleep_until(lastCalc + duration(1.0 / p.Acquisition.maxDFTrate)); - lastCalc = system_clock::now(); - } // qDebug() << "DFT thread calculating"; + if(!dft.input) { + // not connected, skip calculation + continue; + } double DC = dft.DCfreq; TDR *tdr = nullptr; - if(dft.automaticDC) { - // find the last operation that transformed from the frequency domain to the time domain - auto in = dft.input; - while(in->getInput()->getDataType() != DFT::DataType::Frequency) { - in = dft.input->getInput(); - } - switch(in->getType()) { - case DFT::Type::TDR: { - tdr = static_cast(in); - if(tdr->getMode() == TDR::Mode::Lowpass) { - DC = 0; - } else { - // bandpass mode, assume DC is in the middle of the frequency data - DC = tdr->getInput()->getSample(tdr->getInput()->numSamples()/2).x; - } - } - break; - default: - // unknown, assume DC is in the middle of the frequency data - DC = in->getInput()->getSample(in->getInput()->numSamples()/2).x; + // find the last TDR operation + auto in = dft.input; + while(in->getType() != DFT::Type::TDR) { + in = dft.input->getInput(); + if(!in) { break; } } + if(in) { + tdr = static_cast(in); + } + + if(tdr && dft.automaticDC) { + if(tdr->getMode() == TDR::Mode::Lowpass) { + DC = 0; + } else { + // bandpass mode, assume DC is in the middle of the frequency data + DC = tdr->getInput()->getSample(tdr->getInput()->numSamples()/2).x; + } + } auto samples = dft.input->rData().size(); auto timeSpacing = dft.input->rData()[1].x - dft.input->rData()[0].x; vector> timeDomain(samples); @@ -208,14 +220,34 @@ void Math::DFTThread::run() timeDomain.at(i) = dft.input->rData()[i].y; } - Fft::shift(timeDomain, false); dft.window.apply(timeDomain); - Fft::shift(timeDomain, true); Fft::transform(timeDomain, false); // shift DC bin into the middle Fft::shift(timeDomain, false); double binSpacing = 1.0 / (timeSpacing * timeDomain.size()); + + if(tdr) { + // split in padding and actual data sections + unsigned int padding = timeDomain.size() - tdr->getUnpaddedInputSize(); + std::vector> pad_front(timeDomain.begin(), timeDomain.begin()+padding/2); + std::vector> data(timeDomain.begin()+padding/2, timeDomain.end()-padding/2); + std::vector> pad_back(timeDomain.end()-padding/2, timeDomain.end()); + + if(dft.revertWindowFromTDR) { + tdr->getWindow().reverse(data); + } + + if(dft.removePaddingFromTDR) { + timeDomain = data; + } else { + // include padding + timeDomain = pad_front; + copy(data.begin(), data.end(), back_inserter(timeDomain)); + copy(pad_back.begin(), pad_back.end(), back_inserter(timeDomain)); + } + } + dft.data.clear(); int DCbin = timeDomain.size() / 2, startBin = 0; if(DC > 0) { @@ -225,16 +257,18 @@ void Math::DFTThread::run() dft.data.resize(timeDomain.size()/2, TraceMath::Data()); } - // reverse effect of frequency domain window function from TDR (if available) - if(tdr) { - tdr->getWindow().reverse(timeDomain); - } - for(int i = startBin;(unsigned int) i(1.0 / p.Acquisition.maxDFTrate)); + lastCalc = system_clock::now(); + } } } diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h index 6bfbd9d0..3aa5af4c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h @@ -48,6 +48,8 @@ public slots: bool automaticDC; double DCfreq; WindowFunction window; + bool removePaddingFromTDR; + bool revertWindowFromTDR; DFTThread *thread; bool destructing; QSemaphore semphr; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dftdialog.ui b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dftdialog.ui index 61531f13..ffb7e20f 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dftdialog.ui +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dftdialog.ui @@ -3,7 +3,7 @@ DFTDialog - Qt::ApplicationModal + Qt::WindowModality::ApplicationModal @@ -19,7 +19,21 @@ true - + + + + + Remove possible padding from last TDR + + + + + + + Revert window function from last TDR + + + @@ -83,7 +97,7 @@ - QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Ok diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp index 20bdc3a3..82ff7d00 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp @@ -22,6 +22,7 @@ TDR::TDR() manualDC = 1.0; stepResponse = true; mode = Mode::Lowpass; + padding = 0; destructing = false; thread = new TDRThread(*this); @@ -86,19 +87,23 @@ void TDR::edit() ui->manualMag->setEnabled(enable); }; - connect(ui->mode, qOverload(&QComboBox::currentIndexChanged), [=](int index){ + connect(ui->mode, qOverload(&QComboBox::currentIndexChanged), this, [=](int index){ mode = (Mode) index; updateEnabledWidgets(); updateTDR(); }); - connect(ui->computeStepResponse, &QCheckBox::toggled, [=](bool computeStep) { + connect(ui->padding, &QSpinBox::valueChanged, this, [=](int value) { + padding = value; + }); + + connect(ui->computeStepResponse, &QCheckBox::toggled, this, [=](bool computeStep) { stepResponse = computeStep; updateEnabledWidgets(); updateTDR(); }); - connect(ui->DCmanual, &QRadioButton::toggled, [=](bool manual) { + connect(ui->DCmanual, &QRadioButton::toggled, this, [=](bool manual) { automaticDC = !manual; updateEnabledWidgets(); updateTDR(); @@ -114,6 +119,8 @@ void TDR::edit() ui->mode->setCurrentIndex(1); } + ui->padding->setValue(padding); + ui->manualMag->setUnit("dBm"); ui->manualMag->setPrecision(3); ui->manualMag->setValue(Util::SparamTodB(manualDC)); @@ -121,11 +128,11 @@ void TDR::edit() ui->manualPhase->setPrecision(4); ui->manualPhase->setValue(180.0/M_PI * arg(manualDC)); - connect(ui->manualMag, &SIUnitEdit::valueChanged, [=](double newval){ + connect(ui->manualMag, &SIUnitEdit::valueChanged, this, [=](double newval){ manualDC = polar(pow(10, newval / 20.0), arg(manualDC)); updateTDR(); }); - connect(ui->manualPhase, &SIUnitEdit::valueChanged, [=](double newval){ + connect(ui->manualPhase, &SIUnitEdit::valueChanged, this, [=](double newval){ manualDC = polar(abs(manualDC), newval * M_PI / 180.0); updateTDR(); }); @@ -152,6 +159,7 @@ nlohmann::json TDR::toJSON() nlohmann::json j; j["bandpass_mode"] = mode == Mode::Bandpass; j["window"] = window.toJSON(); + j["padding"] = padding; if(mode == Mode::Lowpass) { j["step_response"] = stepResponse; if(stepResponse) { @@ -170,6 +178,7 @@ void TDR::fromJSON(nlohmann::json j) if(j.contains("window")) { window.fromJSON(j["window"]); } + padding = j.value("padding", 0); if(j.value("bandpass_mode", true)) { mode = Mode::Bandpass; } else { @@ -224,6 +233,11 @@ void TDR::updateTDR() } } +unsigned int TDR::getUnpaddedInputSize() const +{ + return unpaddedInputSize; +} + const WindowFunction& TDR::getWindow() const { return window; @@ -254,14 +268,12 @@ void TDRThread::run() qDebug() << "TDR thread exiting"; return; } - // limit update rate if configured in preferences - auto &p = Preferences::getInstance(); - if(p.Acquisition.limitDFT) { - std::this_thread::sleep_until(lastCalc + duration(1.0 / p.Acquisition.maxDFTrate)); - lastCalc = system_clock::now(); - } // qDebug() << "TDR thread calculating"; // perform calculation + if(!tdr.input) { + // not connected, skip calculation + continue; + } vector> frequencyDomain; auto stepSize = (tdr.input->rData().back().x - tdr.input->rData().front().x) / (tdr.input->rData().size() - 1); if(tdr.mode == TDR::Mode::Lowpass) { @@ -321,6 +333,12 @@ void TDRThread::run() } tdr.window.apply(frequencyDomain); + tdr.unpaddedInputSize = frequencyDomain.size(); + if(frequencyDomain.size() < tdr.padding) { + auto missing = tdr.padding - frequencyDomain.size(); + frequencyDomain.insert(frequencyDomain.begin(), missing/2, 0); + frequencyDomain.insert(frequencyDomain.end(), missing/2, 0); + } Fft::shift(frequencyDomain, true); int fft_bins = frequencyDomain.size(); @@ -341,5 +359,12 @@ void TDRThread::run() tdr.updateStepResponse(false); } emit tdr.outputSamplesChanged(0, tdr.data.size()); + + // limit update rate if configured in preferences + auto &p = Preferences::getInstance(); + if(p.Acquisition.limitDFT) { + std::this_thread::sleep_until(lastCalc + duration(1.0 / p.Acquisition.maxDFTrate)); + lastCalc = system_clock::now(); + } } } diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.h b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.h index 0ef1bbe3..703b8895 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.h @@ -48,6 +48,8 @@ class TDR : public TraceMath Mode getMode() const; const WindowFunction& getWindow() const; + unsigned int getUnpaddedInputSize() const; + public slots: void inputSamplesChanged(unsigned int begin, unsigned int end) override; @@ -55,6 +57,8 @@ public slots: void updateTDR(); Mode mode; WindowFunction window; + unsigned int padding; + unsigned int unpaddedInputSize; bool stepResponse; bool automaticDC; std::complex manualDC; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdrdialog.ui b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdrdialog.ui index 3752d020..968d4e6f 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdrdialog.ui +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdrdialog.ui @@ -3,7 +3,7 @@ TDRDialog - Qt::ApplicationModal + Qt::WindowModality::ApplicationModal @@ -43,6 +43,23 @@ + + + + Padding: + + + + + + + 100000 + + + 100 + + + @@ -129,7 +146,7 @@ - QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Ok