diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp index c9771d9a..0cab2fb2 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.cpp @@ -147,10 +147,9 @@ void Math::DFT::inputSamplesChanged(unsigned int begin, unsigned int end) { Q_UNUSED(begin); Q_UNUSED(end); - if(input->rData().size() < 2) { + if(input->getData().size() < 2) { // not enough input data - data.clear(); - emit outputSamplesChanged(0, 0); + clearOutput(); warning("Not enough input samples"); return; } @@ -162,10 +161,18 @@ void Math::DFT::inputSamplesChanged(unsigned int begin, unsigned int end) void Math::DFT::updateDFT() { if(dataType != DataType::Invalid) { - inputSamplesChanged(0, input->rData().size()); + inputSamplesChanged(0, input->getData().size()); } } +void Math::DFT::clearOutput() +{ + dataMutex.lock(); + data.clear(); + dataMutex.unlock(); + emit outputSamplesChanged(0, 0); +} + Math::DFTThread::DFTThread(Math::DFT &dft) : dft(dft) { @@ -186,11 +193,18 @@ void Math::DFTThread::run() qDebug() << "DFT thread exiting"; return; } -// qDebug() << "DFT thread calculating"; + // qDebug() << "DFT thread calculating"; if(!dft.input) { // not connected, skip calculation continue; } + auto inputData = dft.input->getData(); + if(!inputData.size()) { + dft.clearOutput(); + dft.warning("Not enough input samples"); + continue; + } + double DC = dft.DCfreq; TDR *tdr = nullptr; // find the last TDR operation @@ -213,14 +227,15 @@ void Math::DFTThread::run() 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; + auto samples = inputData.size(); + auto timeSpacing = inputData[1].x - inputData[0].x; vector> timeDomain(samples); for(unsigned int i=0;irData()[i].y; + timeDomain.at(i) = inputData[i].y; } dft.window.apply(timeDomain); + Fft::shift(timeDomain, true); Fft::transform(timeDomain, false); // shift DC bin into the middle Fft::shift(timeDomain, false); @@ -229,7 +244,10 @@ void Math::DFTThread::run() if(tdr) { // split in padding and actual data sections - unsigned int padding = timeDomain.size() - tdr->getUnpaddedInputSize(); + unsigned int padding = 0; + if(timeDomain.size() > tdr->getUnpaddedInputSize()) { + 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()); @@ -248,8 +266,8 @@ void Math::DFTThread::run() } } - dft.data.clear(); int DCbin = timeDomain.size() / 2, startBin = 0; + dft.dataMutex.lock(); if(DC > 0) { dft.data.resize(timeDomain.size(), TraceMath::Data()); } else { @@ -262,7 +280,9 @@ void Math::DFTThread::run() dft.data[i - startBin].x = round(freq); dft.data[i - startBin].y = timeDomain.at(i); } - emit dft.outputSamplesChanged(0, dft.data.size()); + auto size = dft.data.size(); + dft.dataMutex.unlock(); + emit dft.outputSamplesChanged(0, size); // limit update rate if configured in preferences auto &p = Preferences::getInstance(); diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h index 3aa5af4c..7bbe26ad 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/dft.h @@ -45,6 +45,7 @@ public slots: private: void updateDFT(); + void clearOutput(); bool automaticDC; double DCfreq; WindowFunction window; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/expression.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/expression.cpp index b9d74180..f7878cea 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/expression.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/expression.cpp @@ -85,7 +85,11 @@ void Math::Expression::fromJSON(nlohmann::json j) void Math::Expression::inputSamplesChanged(unsigned int begin, unsigned int end) { - auto in = input->rData(); + std::vector in; + if(input) { + in = input->getData(); + } + dataMutex.lock(); data.resize(in.size()); try { for(unsigned int i=begin;irData().size()); + inputSamplesChanged(0, input->getData().size()); } } diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/medianfilter.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/medianfilter.cpp index 5b6b6417..b2df82d6 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/medianfilter.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/medianfilter.cpp @@ -79,8 +79,14 @@ void MedianFilter::fromJSON(nlohmann::json j) } void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) { - if(data.size() != input->rData().size()) { - data.resize(input->rData().size()); + std::vector inputData; + if(input) { + inputData = input->getData(); + } + if(data.size() != inputData.size()) { + dataMutex.lock(); + data.resize(inputData.size()); + dataMutex.unlock(); } if(data.size() > 0) { auto kernelOffset = (kernelSize-1)/2; @@ -89,8 +95,8 @@ void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) { if(start < 0) { start = 0; } - if(stop > input->rData().size()) { - stop = input->rData().size(); + if(stop > inputData.size()) { + stop = inputData.size(); } auto comp = [=](const complex&a, const complex&b){ @@ -104,6 +110,7 @@ void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) { }; vector> kernel(kernelSize); + dataMutex.lock(); for(unsigned int out=start;out in + out) { inputSample = 0; - } else if(in + out >= input->rData().size() + kernelOffset) { - inputSample = input->rData().size() - 1; + } else if(in + out >= inputData.size() + kernelOffset) { + inputSample = inputData.size() - 1; } else { inputSample = in + out - kernelOffset; } - auto sample = input->rData().at(inputSample).y; + auto sample = inputData.at(inputSample).y; kernel[in] = sample; } // sort initial kernel @@ -129,20 +136,21 @@ void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) { if(toRemove < 0) { toRemove = 0; } - if(toAdd >= input->rData().size()) { - toAdd = input->rData().size() - 1; + if(toAdd >= inputData.size()) { + toAdd = inputData.size() - 1; } - auto sampleToRemove = input->rData().at(toRemove).y; + auto sampleToRemove = inputData.at(toRemove).y; auto remove_iterator = lower_bound(kernel.begin(), kernel.end(), sampleToRemove, comp); kernel.erase(remove_iterator); - auto sampleToAdd = input->rData().at(toAdd).y; + auto sampleToAdd = inputData.at(toAdd).y; // insert sample at correct position in vector kernel.insert(upper_bound(kernel.begin(), kernel.end(), sampleToAdd, comp), sampleToAdd); } data.at(out).y = kernel[kernelOffset]; - data.at(out).x = input->rData().at(out).x; + data.at(out).x = inputData.at(out).x; } + dataMutex.unlock(); emit outputSamplesChanged(start, stop); success(); } else { diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp index 82ff7d00..3260bb92 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tdr.cpp @@ -205,7 +205,7 @@ void TDR::setMode(Mode m) } mode = m; if(input) { - inputSamplesChanged(0, input->rData().size()); + inputSamplesChanged(0, input->getData().size()); } } @@ -213,15 +213,13 @@ void TDR::inputSamplesChanged(unsigned int begin, unsigned int end) { Q_UNUSED(begin); Q_UNUSED(end); - if(input->rData().size() >= 2) { + if(input->getData().size() >= 2) { // trigger calculation in thread semphr.release(); success(); } else { // not enough input data - data.clear(); - updateStepResponse(false); - emit outputSamplesChanged(0, 0); + clearOutput(); warning("Not enough input samples"); } } @@ -229,10 +227,19 @@ void TDR::inputSamplesChanged(unsigned int begin, unsigned int end) void TDR::updateTDR() { if(dataType != DataType::Invalid) { - inputSamplesChanged(0, input->rData().size()); + inputSamplesChanged(0, input->getData().size()); } } +void TDR::clearOutput() +{ + dataMutex.lock(); + data.clear(); + dataMutex.unlock(); + updateStepResponse(false); + emit outputSamplesChanged(0, 0); +} + unsigned int TDR::getUnpaddedInputSize() const { return unpaddedInputSize; @@ -268,67 +275,56 @@ void TDRThread::run() qDebug() << "TDR thread exiting"; return; } -// qDebug() << "TDR thread calculating"; + // qDebug() << "TDR thread calculating"; // perform calculation if(!tdr.input) { // not connected, skip calculation continue; } + auto inputData = tdr.input->getData(); + if(!inputData.size()) { + // empty input data, clear output data + tdr.clearOutput(); + tdr.warning("Not enough input samples"); + continue; + } vector> frequencyDomain; - auto stepSize = (tdr.input->rData().back().x - tdr.input->rData().front().x) / (tdr.input->rData().size() - 1); + auto stepSize = (inputData.back().x - inputData.front().x) / (inputData.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; - } + auto steps = inputData.size(); + auto firstStep = inputData.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 = inputData[1].x; + steps--; + } + if(firstStep * steps != inputData.back().x) { + // data is not available with correct frequency spacing, calculate required steps + steps = inputData.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 { - 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; - } + frequencyDomain[steps] = tdr.manualDC; } } 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; + frequencyDomain.resize(inputData.size()); + for(unsigned int i=0;i - 100000 + 10000000 100 diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/timegate.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Math/timegate.cpp index 881aa751..532a6d9e 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/timegate.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/timegate.cpp @@ -148,8 +148,12 @@ void Math::TimeGate::fromJSON(nlohmann::json j) void Math::TimeGate::setStart(double start) { - if(input && input->rData().size() > 0 && start < input->rData().front().x) { - start = input->rData().back().x; + if(!input) { + return; + } + auto inputData = input->getData(); + if(inputData.size() > 0 && start < inputData.front().x) { + start = inputData.back().x; } double stop = center + span / 2; @@ -165,8 +169,12 @@ void Math::TimeGate::setStart(double start) void Math::TimeGate::setStop(double stop) { - if(input && input->rData().size() > 0 && stop > input->rData().back().x) { - stop = input->rData().back().x; + if(!input) { + return; + } + auto inputData = input->getData(); + if(inputData.size() > 0 && stop > inputData.back().x) { + stop = inputData.back().x; } double start = center - span / 2; @@ -210,16 +218,24 @@ double Math::TimeGate::getStop() void Math::TimeGate::inputSamplesChanged(unsigned int begin, unsigned int end) { - if(data.size() != input->rData().size()) { - data.resize(input->rData().size()); + std::vector inputData; + if(input) { + inputData = input->getData(); + } + if(data.size() != inputData.size()) { + dataMutex.lock(); + data.resize(inputData.size()); + dataMutex.unlock(); updateFilter(); } + dataMutex.lock(); for(auto i = begin;irData()[i]; + data[i] = inputData[i]; data[i].y *= filter[i]; } + dataMutex.unlock(); emit outputSamplesChanged(begin, end); - if(input->rData().size() > 0) { + if(inputData.size() > 0) { success(); } else { warning("No input data"); @@ -231,14 +247,15 @@ void Math::TimeGate::updateFilter() if(!input) { return; } + auto inputData = input->getData(); std::vector> buf; filter.clear(); - buf.resize(input->rData().size() * 2); + buf.resize(inputData.size() * 2); if(!buf.size()) { return; } - auto maxX = input->rData().back().x; - auto minX = input->rData().front().x; + auto maxX = inputData.back().x; + auto minX = inputData.front().x; auto wc1 = Util::Scale(center - span / 2, minX, maxX, 0, 1); auto wc2 = Util::Scale(center + span / 2, minX, maxX, 0, 1); @@ -271,7 +288,7 @@ void Math::TimeGate::updateFilter() emit filterUpdated(); // needs to update output samples, pretend that input samples have changed - inputSamplesChanged(0, input->rData().size()); + inputSamplesChanged(0, inputData.size()); } Math::TimeGateGraph::TimeGateGraph(QWidget *parent) @@ -285,13 +302,18 @@ Math::TimeGateGraph::TimeGateGraph(QWidget *parent) QPoint Math::TimeGateGraph::plotValueToPixel(double x, double y) { - if(!gate->getInput() || !gate->getInput()->rData().size()) { + if(!gate->getInput()) { + return QPoint(0, 0); + } + + auto inputData = gate->getInput()->getData(); + + if(!inputData.size()) { return QPoint(0, 0); } - auto input = gate->getInput()->rData(); - auto minX = input.front().x; - auto maxX = input.back().x; + auto minX = inputData.front().x; + auto maxX = inputData.back().x; int plotLeft = 0; int plotRight = size().width(); @@ -306,13 +328,18 @@ QPoint Math::TimeGateGraph::plotValueToPixel(double x, double y) QPointF Math::TimeGateGraph::pixelToPlotValue(QPoint p) { - if(!gate->getInput() || !gate->getInput()->rData().size()) { + if(!gate->getInput()) { + return QPointF(0.0, 0.0); + } + + auto inputData = gate->getInput()->getData(); + + if(!inputData.size()) { return QPointF(0.0, 0.0); } - auto input = gate->getInput()->rData(); - auto minX = input.front().x; - auto maxX = input.back().x; + auto minX = inputData.front().x; + auto maxX = inputData.back().x; int plotLeft = 0; int plotRight = size().width(); @@ -327,11 +354,11 @@ QPointF Math::TimeGateGraph::pixelToPlotValue(QPoint p) void Math::TimeGateGraph::paintEvent(QPaintEvent *event) { - if(!gate) { + if(!gate || !gate->getInput()) { return; } // grab input data - auto input = gate->getInput()->rData(); + auto inputData = gate->getInput()->getData(); Q_UNUSED(event) auto& pref = Preferences::getInstance(); @@ -340,7 +367,7 @@ 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()) { + if(!inputData.size()) { // no data yet, nothing to plot return; } @@ -351,8 +378,8 @@ void Math::TimeGateGraph::paintEvent(QPaintEvent *event) pen.setStyle(Qt::SolidLine); p.setPen(pen); - auto minX = input.front().x; - auto maxX = input.back().x; + auto minX = inputData.front().x; + auto maxX = inputData.back().x; int plotLeft = 0; int plotRight = size().width(); @@ -362,14 +389,14 @@ void Math::TimeGateGraph::paintEvent(QPaintEvent *event) QPoint p1, p2; // limit amount of displayed points to keep GUI snappy - auto increment = input.size() / 500; + auto increment = inputData.size() / 500; if(!increment) { increment = 1; } - for(unsigned int i=increment;irFilter(); pen = QPen(Qt::red, 1); p.setPen(pen); - for(unsigned int i=increment;i + TraceMath::TraceMath() { input = nullptr; @@ -87,14 +89,13 @@ TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type) TraceMath::Data TraceMath::getSample(unsigned int index) { + TraceMath::Data d; + dataMutex.lock(); if(index < data.size()) { - return data[index]; - } else { - TraceMath::Data d; - d.x = 0; - d.y = 0; - return d; + d = data[index]; } + dataMutex.unlock(); + return d; } double TraceMath::getStepResponse(unsigned int index) @@ -108,6 +109,7 @@ double TraceMath::getStepResponse(unsigned int index) double TraceMath::getInterpolatedStepResponse(double x) { + QMutexLocker locker(&dataMutex); if(stepResponse.size() != data.size()) { // make sure all the step response data is available return std::numeric_limits::quiet_NaN(); @@ -140,7 +142,7 @@ double TraceMath::getInterpolatedStepResponse(double x) TraceMath::Data TraceMath::getInterpolatedSample(double x) { Data ret; - + QMutexLocker locker(&dataMutex); if(data.size() == 0 || x < data.front().x || x > data.back().x) { ret.y = std::numeric_limits>::quiet_NaN(); ret.x = std::numeric_limits::quiet_NaN(); @@ -165,7 +167,10 @@ TraceMath::Data TraceMath::getInterpolatedSample(double x) unsigned int TraceMath::numSamples() { - return data.size(); + dataMutex.lock(); + auto size = data.size(); + dataMutex.unlock(); + return size; } QString TraceMath::dataTypeToString(TraceMath::DataType type) @@ -201,7 +206,9 @@ void TraceMath::removeInput() // disconnect everything from the input disconnect(input, nullptr, this, nullptr); input = nullptr; + dataMutex.lock(); data.clear(); + dataMutex.unlock(); dataType = DataType::Invalid; emit outputTypeChanged(dataType); } @@ -222,14 +229,19 @@ void TraceMath::inputTypeChanged(TraceMath::DataType type) { auto newType = outputType(type); dataType = newType; + dataMutex.lock(); data.clear(); + dataMutex.unlock(); if(dataType == DataType::Invalid) { error("Invalid input data"); disconnect(input, &TraceMath::outputSamplesChanged, this, &TraceMath::inputSamplesChanged); updateStepResponse(false); } else { connect(input, &TraceMath::outputSamplesChanged, this, &TraceMath::inputSamplesChanged, Qt::UniqueConnection); - inputSamplesChanged(0, input->data.size()); + input->dataMutex.lock(); + auto inputSize = input->data.size(); + input->dataMutex.unlock(); + inputSamplesChanged(0, inputSize); } emit outputTypeChanged(dataType); } @@ -256,8 +268,14 @@ void TraceMath::success() } } +QMutex& TraceMath::mutex() +{ + return dataMutex; +} + void TraceMath::updateStepResponse(bool valid) { + QMutexLocker locker(&dataMutex); if(valid) { stepResponse.resize(data.size()); double accumulate = 0.0; @@ -298,3 +316,11 @@ TraceMath::DataType TraceMath::getDataType() const { return dataType; } + +std::vector TraceMath::getData() +{ + dataMutex.lock(); + auto ret = data; + dataMutex.unlock(); + return ret; +} diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tracemath.h b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tracemath.h index 08d06dce..1583b2a8 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Math/tracemath.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Math/tracemath.h @@ -4,6 +4,7 @@ #include "savable.h" #include +#include #include #include /* @@ -110,7 +111,7 @@ class TraceMath : public QObject, public Savable { void assignInput(TraceMath *input); DataType getDataType() const; - virtual std::vector& rData() { return data;} + virtual std::vector getData(); Status getStatus() const; QString getStatusDescription() const; virtual Type getType() = 0; @@ -120,6 +121,8 @@ class TraceMath : public QObject, public Savable { TraceMath *getInput() const; + QMutex& mutex(); + public slots: // some values of the input data have changed, begin/end determine which sample(s) has changed virtual void inputSamplesChanged(unsigned int begin, unsigned int end){Q_UNUSED(begin) Q_UNUSED(end)} @@ -137,6 +140,7 @@ public slots: void warning(QString warn); void error(QString err); void success(); + QMutex dataMutex; std::vector data; // buffer for time domain step response data. This makes it possible to access an arbitrary sample of the step response without having to // integrate the impulse response every time. Call updateStepResponse in your derived class, if step response data is valid after updating diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/trace.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/trace.cpp index 19874d13..2c3ac110 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/trace.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/trace.cpp @@ -1355,7 +1355,7 @@ void Trace::clearDeembedding() double Trace::minX() { if(lastMath->numSamples() > 0) { - return lastMath->rData().front().x; + return lastMath->getData().front().x; } else { return numeric_limits::max(); } @@ -1364,7 +1364,7 @@ double Trace::minX() double Trace::maxX() { if(lastMath->numSamples() > 0) { - return lastMath->rData().back().x; + return lastMath->getData().back().x; } else { return numeric_limits::lowest(); } @@ -1374,7 +1374,7 @@ double Trace::findExtremum(bool max, double xmin, double xmax) { double compare = max ? numeric_limits::min() : numeric_limits::max(); double freq = 0.0; - for(auto sample : lastMath->rData()) { + for(auto sample : lastMath->getData()) { if(sample.x < xmin || sample.x > xmax) { continue; } @@ -1405,7 +1405,7 @@ std::vector Trace::findPeakFrequencies(unsigned int maxPeaks, double min double frequency = 0.0; double max_dbm = -200.0; double min_dbm = 200.0; - for(auto d : lastMath->rData()) { + for(auto d : lastMath->getData()) { if(d.x < xmin || d.x > xmax) { continue; } @@ -1517,12 +1517,12 @@ unsigned int Trace::numSamples() } } -std::vector &Trace::rData() +std::vector Trace::getData() { if(deembeddingActive && deembeddingAvailable()) { return deembeddingData; } else { - return TraceMath::rData(); + return TraceMath::getData(); } } @@ -1620,12 +1620,12 @@ double Trace::getGroupDelay(double frequency) int Trace::index(double x) { - auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), x, [](const Data &lhs, const double x) -> bool { + auto lower = lower_bound(lastMath->getData().begin(), lastMath->getData().end(), x, [](const Data &lhs, const double x) -> bool { return lhs.x < x; }); - if(lower == lastMath->rData().end()) { + if(lower == lastMath->getData().end()) { // actually beyond the last sample, return the index of the last anyway to avoid access past data - return lastMath->rData().size() - 1; + return lastMath->getData().size() - 1; } - return lower - lastMath->rData().begin(); + return lower - lastMath->getData().begin(); } diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/trace.h b/Software/PC_Application/LibreVNA-GUI/Traces/trace.h index 69fe0ab3..0ece8a0b 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/trace.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/trace.h @@ -95,7 +95,7 @@ class Trace : public TraceMath virtual Data getSample(unsigned int index) override; virtual Data getInterpolatedSample(double x) override; virtual unsigned int numSamples() override; - virtual std::vector& rData() override; + virtual std::vector getData() override; double getUnwrappedPhase(unsigned int index); // returns a (possibly interpolated sample) at a specified frequency/time/power diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceeditdialog.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceeditdialog.cpp index 5d9d90f9..852705a7 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceeditdialog.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceeditdialog.cpp @@ -283,13 +283,14 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) : } else { // composite operation added, check which one and edit the correct suboperation switch(type) { - case TraceMath::Type::TimeDomainGating: + case TraceMath::Type::TimeDomainGating: { + auto inputData = newMath[0]->getInput()->getData(); // Automatically select bandpass/lowpass TDR, depending on selected span - if(newMath[0]->getInput()->rData().size() > 0) { + if(inputData.size() > 0) { // Automatically select bandpass/lowpass TDR, depending on selected span auto tdr = (Math::TDR*) newMath[0]; - auto fstart = tdr->getInput()->rData().front().x; - auto fstop = tdr->getInput()->rData().back().x; + auto fstart = inputData.front().x; + auto fstop = inputData.back().x; if(fstart < fstop / 100.0) { tdr->setMode(Math::TDR::Mode::Lowpass); @@ -301,6 +302,7 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) : // TDR/DFT can be left at default, edit the actual gate newMath[1]->edit(); + } break; default: break; diff --git a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro index fa069b90..88008ec3 100644 --- a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro +++ b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro @@ -151,6 +151,7 @@ SOURCES += \ ../LibreVNA-GUI/streamingserver.cpp \ ../LibreVNA-GUI/touchstone.cpp \ ../LibreVNA-GUI/unit.cpp \ + ffttests.cpp \ main.cpp \ parametertests.cpp \ portextensiontests.cpp \ @@ -344,6 +345,7 @@ HEADERS += \ ../LibreVNA-GUI/streamingserver.h \ ../LibreVNA-GUI/touchstone.h \ ../LibreVNA-GUI/unit.h \ + ffttests.h \ parametertests.h \ portextensiontests.h \ utiltests.h diff --git a/Software/PC_Application/LibreVNA-Test/ffttests.cpp b/Software/PC_Application/LibreVNA-Test/ffttests.cpp new file mode 100644 index 00000000..3ee5b7e6 --- /dev/null +++ b/Software/PC_Application/LibreVNA-Test/ffttests.cpp @@ -0,0 +1,59 @@ +#include "ffttests.h" + +#include "Traces/fftcomplex.h" + +using namespace std; + +fftTests::fftTests() {} + +static bool compareComplexVectors(const vector> &v1, const vector> &v2) { + if(v1.size() != v2.size()) { + return false; + } + for(unsigned int i=0;i 1e-14) { + return false; + } + if(abs(v1[i].imag() - v2[i].imag()) > 1e-14) { + return false; + } + } + return true; +} + +void fftTests::fft() +{ + vector> data{1, 2, 3, 4, 5}; + Fft::transform(data, false); + vector> expectedResult{15, complex(-2.5, 3.440954801177934), complex(-2.5, 0.812299240582265), complex(-2.5, -0.812299240582265), complex(-2.5, -3.440954801177934)}; + QVERIFY(compareComplexVectors(data, expectedResult)); +} + +void fftTests::fftAndIfft() +{ + vector> data{1, 2, 3, 4, 5}; + vector> expectedResult{1, 2, 3, 4, 5}; + Fft::transform(data, false); + Fft::transform(data, true); + for(auto &d : data) { + d /= data.size(); + } + QVERIFY(compareComplexVectors(data, expectedResult)); +} + +void fftTests::ifftAndFft() +{ + vector> data{1, 2, 3, 4, 5}; + vector> expectedResult{1, 2, 3, 4, 5}; + Fft::transform(data, true); + for(auto &d : data) { + d /= data.size(); + } + Fft::transform(data, false); + QVERIFY(compareComplexVectors(data, expectedResult)); +} + +void fftTests::fftAndIfftWithShift() +{ + +} diff --git a/Software/PC_Application/LibreVNA-Test/ffttests.h b/Software/PC_Application/LibreVNA-Test/ffttests.h new file mode 100644 index 00000000..029ba2fb --- /dev/null +++ b/Software/PC_Application/LibreVNA-Test/ffttests.h @@ -0,0 +1,19 @@ +#ifndef FFTTESTS_H +#define FFTTESTS_H + +#include + +class fftTests : public QObject +{ + Q_OBJECT +public: + fftTests(); + +private slots: + void fft(); + void fftAndIfft(); + void ifftAndFft(); + void fftAndIfftWithShift(); +}; + +#endif // FFTTESTS_H diff --git a/Software/PC_Application/LibreVNA-Test/main.cpp b/Software/PC_Application/LibreVNA-Test/main.cpp index 610ad4ca..c3dafaa2 100644 --- a/Software/PC_Application/LibreVNA-Test/main.cpp +++ b/Software/PC_Application/LibreVNA-Test/main.cpp @@ -1,6 +1,7 @@ #include "utiltests.h" #include "portextensiontests.h" #include "parametertests.h" +#include "ffttests.h" #include @@ -12,6 +13,7 @@ int main(int argc, char *argv[]) status |= QTest::qExec(new UtilTests, argc, argv); status |= QTest::qExec(new PortExtensionTests, argc, argv); status |= QTest::qExec(new ParameterTests, argc, argv); + status |= QTest::qExec(new fftTests, argc, argv); return status; }