Move DFT/TDR calculation into dedicated thread, limit update rate

This commit is contained in:
Jan Käberich 2021-12-30 15:23:07 +01:00
parent fd786c4176
commit 8e47d14192
5 changed files with 299 additions and 148 deletions

View file

@ -6,6 +6,8 @@
#include "ui_dftdialog.h"
#include "ui_dftexplanationwidget.h"
#include <QDebug>
using namespace std;
Math::DFT::DFT()
@ -13,9 +15,22 @@ Math::DFT::DFT()
automaticDC = true;
DCfreq = 1000000000.0;
destructing = false;
thread = new DFTThread(*this);
thread->start(TDRThread::Priority::LowestPriority);
connect(&window, &WindowFunction::changed, this, &DFT::updateDFT);
}
Math::DFT::~DFT()
{
// tell thread to exit
destructing = true;
semphr.release();
thread->wait();
delete thread;
}
TraceMath::DataType Math::DFT::outputType(TraceMath::DataType inputType)
{
if(inputType == DataType::Time) {
@ -120,66 +135,8 @@ void Math::DFT::inputSamplesChanged(unsigned int begin, unsigned int end)
// not the end, do nothing
return;
}
double DC = DCfreq;
TDR *tdr = nullptr;
if(automaticDC) {
// find the last operation that transformed from the frequency domain to the time domain
auto in = input;
while(in->getInput()->getDataType() != DataType::Frequency) {
in = input->getInput();
}
switch(in->getType()) {
case Type::TDR: {
tdr = static_cast<TDR*>(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;
break;
}
}
auto samples = input->rData().size();
auto timeSpacing = input->rData()[1].x - input->rData()[0].x;
vector<complex<double>> timeDomain(samples);
for(unsigned int i=0;i<samples;i++) {
timeDomain.at(i) = input->rData()[i].y;
}
Fft::shift(timeDomain, false);
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());
data.clear();
int DCbin = timeDomain.size() / 2, startBin = 0;
if(DC > 0) {
data.resize(timeDomain.size());
} else {
startBin = (timeDomain.size()+1) / 2;
data.resize(timeDomain.size()/2);
}
// reverse effect of frequency domain window function from TDR (if available)
if(tdr) {
tdr->getWindow().reverse(timeDomain);
}
for(int i = startBin;(unsigned int) i<timeDomain.size();i++) {
auto freq = (i - DCbin) * binSpacing + DC;
data[i - startBin].x = round(freq);
data[i - startBin].y = timeDomain.at(i);
}
emit outputSamplesChanged(0, data.size());
// trigger calculation in thread
semphr.release();
success();
}
@ -189,3 +146,85 @@ void Math::DFT::updateDFT()
inputSamplesChanged(0, input->rData().size());
}
}
Math::DFTThread::DFTThread(Math::DFT &dft)
: dft(dft)
{
}
void Math::DFTThread::run()
{
qDebug() << "DFT thread starting";
while(1) {
dft.semphr.acquire();
// clear possible additional semaphores
dft.semphr.tryAcquire(dft.semphr.available());
if(dft.destructing) {
// TDR object about to be deleted, exit thread
qDebug() << "DFT thread exiting";
return;
}
qDebug() << "DFT thread calculating";
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<TDR*>(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;
break;
}
}
auto samples = dft.input->rData().size();
auto timeSpacing = dft.input->rData()[1].x - dft.input->rData()[0].x;
vector<complex<double>> timeDomain(samples);
for(unsigned int i=0;i<samples;i++) {
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());
dft.data.clear();
int DCbin = timeDomain.size() / 2, startBin = 0;
if(DC > 0) {
dft.data.resize(timeDomain.size());
} else {
startBin = (timeDomain.size()+1) / 2;
dft.data.resize(timeDomain.size()/2);
}
// reverse effect of frequency domain window function from TDR (if available)
if(tdr) {
tdr->getWindow().reverse(timeDomain);
}
for(int i = startBin;(unsigned int) i<timeDomain.size();i++) {
auto freq = (i - DCbin) * binSpacing + DC;
dft.data[i - startBin].x = round(freq);
dft.data[i - startBin].y = timeDomain.at(i);
}
emit dft.outputSamplesChanged(0, dft.data.size());
}
}