diff --git a/Software/PC_Application/Traces/Math/dft.cpp b/Software/PC_Application/Traces/Math/dft.cpp index 6e4d3c6..29b5a1a 100644 --- a/Software/PC_Application/Traces/Math/dft.cpp +++ b/Software/PC_Application/Traces/Math/dft.cpp @@ -6,6 +6,8 @@ #include "ui_dftdialog.h" #include "ui_dftexplanationwidget.h" +#include + 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(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> timeDomain(samples); - for(unsigned int i=0;irData()[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) irData().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(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> timeDomain(samples); + for(unsigned int i=0;irData()[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 +#include + namespace Math { +class DFT; + +class DFTThread : public QThread +{ + Q_OBJECT +public: + DFTThread(DFT &dft); + ~DFTThread(){}; +private: + void run() override; + DFT &dft; +}; + class DFT : public TraceMath { + friend class DFTThread; + Q_OBJECT public: DFT(); + ~DFT(); DataType outputType(DataType inputType) override; QString description() override; @@ -29,6 +48,9 @@ private: bool automaticDC; double DCfreq; WindowFunction window; + DFTThread *thread; + bool destructing; + QSemaphore semphr; }; } diff --git a/Software/PC_Application/Traces/Math/tdr.cpp b/Software/PC_Application/Traces/Math/tdr.cpp index 48111e4..9033020 100644 --- a/Software/PC_Application/Traces/Math/tdr.cpp +++ b/Software/PC_Application/Traces/Math/tdr.cpp @@ -19,9 +19,22 @@ TDR::TDR() stepResponse = true; mode = Mode::Lowpass; + destructing = false; + thread = new TDRThread(*this); + thread->start(TDRThread::Priority::LowestPriority); + connect(&window, &WindowFunction::changed, this, &TDR::updateTDR); } +TDR::~TDR() +{ + // tell thread to exit + destructing = true; + semphr.release(); + thread->wait(); + delete thread; +} + TraceMath::DataType TDR::outputType(TraceMath::DataType inputType) { if(inputType == DataType::Frequency) { @@ -190,85 +203,8 @@ void TDR::inputSamplesChanged(unsigned int begin, unsigned int end) // not the end, do nothing return; } - vector> frequencyDomain; - auto stepSize = (input->rData().back().x - input->rData().front().x) / (input->rData().size() - 1); - if(mode == Mode::Lowpass) { - if(stepResponse) { - auto steps = input->rData().size(); - auto firstStep = input->rData().front().x; - // frequency points need to be evenly spaced all the way to DC - if(firstStep == 0) { - // zero as first step would result in infinite number of points, skip and start with second - firstStep = input->rData()[1].x; - steps--; - } - if(firstStep * steps != input->rData().back().x) { - // data is not available with correct frequency spacing, calculate required steps - steps = input->rData().back().x / firstStep; - stepSize = firstStep; - } - frequencyDomain.resize(2 * steps + 1); - // copy frequencies, use the flipped conjugate for negative part - for(unsigned int i = 1;i<=steps;i++) { - auto S = input->getInterpolatedSample(stepSize * i).y; - frequencyDomain[steps - i] = conj(S); - frequencyDomain[steps + i] = S; - } - if(automaticDC) { - // use simple extrapolation from lowest two points to extract DC value - auto abs_DC = 2.0 * abs(frequencyDomain[steps + 1]) - abs(frequencyDomain[steps + 2]); - auto phase_DC = 2.0 * arg(frequencyDomain[steps + 1]) - arg(frequencyDomain[steps + 2]); - frequencyDomain[steps] = polar(abs_DC, phase_DC); - } else { - frequencyDomain[steps] = manualDC; - } - } else { - auto steps = input->rData().size(); - unsigned int offset = 0; - if(input->rData().front().x == 0) { - // DC measurement is inaccurate, skip - steps--; - offset++; - } - // no step response required, can use frequency values as they are. No extra extrapolated DC value here -> 2 values less than with step response - frequencyDomain.resize(2 * steps - 1); - frequencyDomain[steps - 1] = input->rData()[offset].y; - for(unsigned int i = 1;irData()[i + offset].y; - frequencyDomain[steps - i - 1] = conj(S); - frequencyDomain[steps + i - 1] = S; - } - } - } else { - // bandpass mode - // Can use input data directly, no need to extend with complex conjugate - frequencyDomain.resize(input->rData().size()); - for(unsigned int i=0;irData().size();i++) { - frequencyDomain[i] = input->rData()[i].y; - } - } - - window.apply(frequencyDomain); - Fft::shift(frequencyDomain, true); - - auto fft_bins = frequencyDomain.size(); - const double fs = 1.0 / (stepSize * fft_bins); - - Fft::transform(frequencyDomain, true); - - data.clear(); - data.resize(fft_bins); - - for(unsigned int i = 0;i> frequencyDomain; + auto stepSize = (tdr.input->rData().back().x - tdr.input->rData().front().x) / (tdr.input->rData().size() - 1); + if(tdr.mode == TDR::Mode::Lowpass) { + if(tdr.stepResponse) { + auto steps = tdr.input->rData().size(); + auto firstStep = tdr.input->rData().front().x; + // frequency points need to be evenly spaced all the way to DC + if(firstStep == 0) { + // zero as first step would result in infinite number of points, skip and start with second + firstStep = tdr.input->rData()[1].x; + steps--; + } + if(firstStep * steps != tdr.input->rData().back().x) { + // data is not available with correct frequency spacing, calculate required steps + steps = tdr.input->rData().back().x / firstStep; + stepSize = firstStep; + } + frequencyDomain.resize(2 * steps + 1); + // copy frequencies, use the flipped conjugate for negative part + for(unsigned int i = 1;i<=steps;i++) { + auto S = tdr.input->getInterpolatedSample(stepSize * i).y; + frequencyDomain[steps - i] = conj(S); + frequencyDomain[steps + i] = S; + } + if(tdr.automaticDC) { + // use simple extrapolation from lowest two points to extract DC value + auto abs_DC = 2.0 * abs(frequencyDomain[steps + 1]) - abs(frequencyDomain[steps + 2]); + auto phase_DC = 2.0 * arg(frequencyDomain[steps + 1]) - arg(frequencyDomain[steps + 2]); + frequencyDomain[steps] = polar(abs_DC, phase_DC); + } else { + frequencyDomain[steps] = tdr.manualDC; + } + } else { + auto steps = tdr.input->rData().size(); + unsigned int offset = 0; + if(tdr.input->rData().front().x == 0) { + // DC measurement is inaccurate, skip + steps--; + offset++; + } + // no step response required, can use frequency values as they are. No extra extrapolated DC value here -> 2 values less than with step response + frequencyDomain.resize(2 * steps - 1); + frequencyDomain[steps - 1] = tdr.input->rData()[offset].y; + for(unsigned int i = 1;irData()[i + offset].y; + frequencyDomain[steps - i - 1] = conj(S); + frequencyDomain[steps + i - 1] = S; + } + } + } else { + // bandpass mode + // Can use input data directly, no need to extend with complex conjugate + frequencyDomain.resize(tdr.input->rData().size()); + for(unsigned int i=0;irData().size();i++) { + frequencyDomain[i] = tdr.input->rData()[i].y; + } + } + + tdr.window.apply(frequencyDomain); + Fft::shift(frequencyDomain, true); + + auto fft_bins = frequencyDomain.size(); + const double fs = 1.0 / (stepSize * fft_bins); + + Fft::transform(frequencyDomain, true); + + tdr.data.clear(); + tdr.data.resize(fft_bins); + + for(unsigned int i = 0;i +#include + namespace Math { +class TDR; + +class TDRThread : public QThread +{ + Q_OBJECT +public: + TDRThread(TDR &tdr); + ~TDRThread(){}; +private: + void run() override; + TDR &tdr; +}; + class TDR : public TraceMath { + friend class TDRThread; + Q_OBJECT public: TDR(); + ~TDR(); DataType outputType(DataType inputType) override; QString description() override; @@ -39,6 +58,9 @@ private: bool stepResponse; bool automaticDC; std::complex manualDC; + TDRThread *thread; + bool destructing; + QSemaphore semphr; }; } diff --git a/Software/PC_Application/Traces/Math/timegate.cpp b/Software/PC_Application/Traces/Math/timegate.cpp index c2f3af8..5e89b79 100644 --- a/Software/PC_Application/Traces/Math/timegate.cpp +++ b/Software/PC_Application/Traces/Math/timegate.cpp @@ -346,13 +346,35 @@ void Math::TimeGateGraph::paintEvent(QPaintEvent *event) p.setBackground(QBrush(pref.Graphs.Color.background)); p.fillRect(0, 0, width(), height(), QBrush(pref.Graphs.Color.background)); + if(!gate->getInput() || !gate->getInput()->rData().size()) { + // no data yet, nothing to plot + return; + } + // plot trace auto pen = QPen(Qt::green, 1); pen.setCosmetic(true); pen.setStyle(Qt::SolidLine); p.setPen(pen); - for(unsigned int i=1;i(last.x, minX, maxX, plotLeft, plotRight)); + p1.setY(Util::Scale(y_last, -120, 20, plotBottom, plotTop)); + p2.setX(Util::Scale(now.x, minX, maxX, plotLeft, plotRight)); + p2.setY(Util::Scale(y_now, -120, 20, plotBottom, plotTop)); +// auto p1 = plotValueToPixel(last.x, y_last); +// auto p2 = plotValueToPixel(now.x, y_now); // draw line p.drawLine(p1, p2); } @@ -372,11 +398,11 @@ void Math::TimeGateGraph::paintEvent(QPaintEvent *event) auto filter = gate->rFilter(); pen = QPen(Qt::red, 1); p.setPen(pen); - for(unsigned int i=1;i(x_last, minX, maxX, plotLeft, plotRight)); + p1.setY(Util::Scale(f_last, -120, 20, plotBottom, plotTop)); + p2.setX(Util::Scale(x_now, minX, maxX, plotLeft, plotRight)); + p2.setY(Util::Scale(f_now, -120, 20, plotBottom, plotTop)); +// auto p1 = plotValueToPixel(x_last, f_last); +// auto p2 = plotValueToPixel(x_now, f_now); // draw line p.drawLine(p1, p2);