From 49f9b5442dfb2dd8d27d65ac28b4846a3aedd94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sat, 28 Nov 2020 22:34:40 +0100 Subject: [PATCH] Updated graphs to use new math system --- .../PC_Application/Traces/Math/tdrlowpass.cpp | 4 +- .../PC_Application/Traces/Math/tracemath.cpp | 24 ++ .../PC_Application/Traces/Math/tracemath.h | 6 + Software/PC_Application/Traces/trace.cpp | 214 +++-------------- Software/PC_Application/Traces/trace.h | 45 +--- .../Traces/traceexportdialog.cpp | 6 +- .../PC_Application/Traces/tracemarker.cpp | 207 +++++++--------- Software/PC_Application/Traces/tracemarker.h | 7 - .../Traces/tracemarkermodel.cpp | 9 - .../PC_Application/Traces/tracemarkermodel.h | 4 +- Software/PC_Application/Traces/tracemodel.cpp | 3 - Software/PC_Application/Traces/traceplot.cpp | 10 + Software/PC_Application/Traces/traceplot.h | 1 + .../PC_Application/Traces/tracesmithchart.cpp | 12 +- .../PC_Application/Traces/tracexyplot.cpp | 224 +++++++----------- Software/PC_Application/Traces/tracexyplot.h | 5 +- 16 files changed, 263 insertions(+), 518 deletions(-) diff --git a/Software/PC_Application/Traces/Math/tdrlowpass.cpp b/Software/PC_Application/Traces/Math/tdrlowpass.cpp index 55f1c26..aa0ddb0 100644 --- a/Software/PC_Application/Traces/Math/tdrlowpass.cpp +++ b/Software/PC_Application/Traces/Math/tdrlowpass.cpp @@ -23,7 +23,7 @@ TraceMath::DataType TDRLowpass::outputType(TraceMath::DataType inputType) QString TDRLowpass::description() { - return "TDR (bandpass mode)"; + return "TDR (lowpass mode)"; } void TDRLowpass::edit() @@ -85,11 +85,13 @@ void TDRLowpass::inputSamplesChanged(unsigned int begin, unsigned int end) data[i].x = fs * i; data[i].y = frequencyDomain[i] / (double) fft_bins; } + updateStepResponse(true); emit outputSamplesChanged(0, data.size()); success(); } else { // not enough input data data.clear(); + updateStepResponse(false); emit outputSamplesChanged(0, 0); warning("Not enough input samples"); } diff --git a/Software/PC_Application/Traces/Math/tracemath.cpp b/Software/PC_Application/Traces/Math/tracemath.cpp index c3e951c..56794a3 100644 --- a/Software/PC_Application/Traces/Math/tracemath.cpp +++ b/Software/PC_Application/Traces/Math/tracemath.cpp @@ -52,6 +52,15 @@ TraceMath::Data TraceMath::getSample(unsigned int index) return data.at(index); } +double TraceMath::getStepResponse(unsigned int index) +{ + if(stepResponse.size() > index) { + return stepResponse[index]; + } else { + return std::numeric_limits::quiet_NaN(); + } +} + TraceMath::Data TraceMath::getInterpolatedSample(double x) { Data ret; @@ -120,6 +129,7 @@ void TraceMath::inputTypeChanged(TraceMath::DataType type) emit outputTypeChanged(dataType); if(dataType == DataType::Invalid) { error("Invalid input data"); + updateStepResponse(false); } } } @@ -146,6 +156,20 @@ void TraceMath::success() } } +void TraceMath::updateStepResponse(bool valid) +{ + if(valid) { + stepResponse.resize(data.size()); + double accumulate = 0.0; + for(unsigned int i=0;i 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 + // data. + std::vector stepResponse; + void updateStepResponse(bool valid); TraceMath *input; DataType dataType; diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp index 6bd3a9e..214bcac 100644 --- a/Software/PC_Application/Traces/trace.cpp +++ b/Software/PC_Application/Traces/trace.cpp @@ -5,8 +5,7 @@ using namespace std; Trace::Trace(QString name, QColor color, LiveParameter live) - : tdr_users(0), - _name(name), + : _name(name), _color(color), _liveType(LivedataType::Overwrite), _liveParam(live), @@ -75,12 +74,6 @@ void Trace::addData(const Trace::Data& d) { } success(); emit outputSamplesChanged(lower - data.begin(), lower - data.begin() + 1); - if(lower == data.begin()) { - // received the first point, which means the last sweep just finished - if(tdr_users) { - updateTimeDomainData(); - } - } } void Trace::addData(const Trace::Data &d, const Protocol::SweepSettings &s) @@ -168,78 +161,6 @@ void Trace::removeMarker(TraceMarker *m) markers.erase(m); emit markerRemoved(m); } -//#include -//#include - -void Trace::updateTimeDomainData() -{ - if(data.size() < 2) { - // can't compute anything - timeDomain.clear(); - return; - } -// using namespace std::chrono; -// auto starttime = duration_cast< milliseconds >( -// system_clock::now().time_since_epoch() -// ).count(); - auto steps = size(); - auto firstStep = minFreq(); - if(firstStep == 0) { - // zero as first step would result in infinite number of points, skip and start with second - firstStep = lastMath->rData()[1].x; - steps--; - } - if(firstStep * steps != maxFreq()) { - // data is not available with correct frequency spacing, calculate required steps - steps = maxFreq() / firstStep; - } - const double PI = 3.141592653589793238463; - // reserve vector for negative frequenies and DC as well - vector> frequencyDomain(2*steps + 1); - // copy frequencies, use the flipped conjugate for negative part - for(unsigned int i = 1;i<=steps;i++) { - auto S = getData(firstStep * i); - constexpr double alpha0 = 0.54; - auto hamming = alpha0 - (1.0 - alpha0) * -cos(PI * i / steps); - S *= hamming; - frequencyDomain[2 * steps - i + 1] = conj(S); - frequencyDomain[i] = S; - } - // use simple extrapolation from lowest two points to extract DC value - auto abs_DC = 2.0 * abs(frequencyDomain[1]) - abs(frequencyDomain[2]); - auto phase_DC = 2.0 * arg(frequencyDomain[1]) - arg(frequencyDomain[2]); - frequencyDomain[0] = polar(abs_DC, phase_DC); - - auto fft_bins = frequencyDomain.size(); - timeDomain.clear(); - timeDomain.resize(fft_bins); - const double fs = 1.0 / (firstStep * fft_bins); - double last_step = 0.0; - - Fft::transform(frequencyDomain, true); - constexpr double c = 299792458; - for(unsigned int i = 0;i::quiet_NaN(); - } - last_step += t.impulseResponse; - timeDomain[i] = t; - } -// auto duration = duration_cast< milliseconds >( -// system_clock::now().time_since_epoch() -// ).count() - starttime; - // cout << "TDR: " << this << " (took " << duration << "ms)" <& Trace::getMathOperations() const { @@ -263,6 +184,7 @@ void Trace::updateLastMath(vector::reverse_iterator start) lastMath = newLast; // relay signals of end of math chain connect(lastMath, &TraceMath::outputSamplesChanged, this, &Trace::dataChanged); + emit typeChanged(this); emit outputSamplesChanged(0, data.size()); } } @@ -272,77 +194,6 @@ void Trace::setReflection(bool value) reflection = value; } -void Trace::addTDRinterest() -{ - if(tdr_users == 0) { - // no recent time domain data available, calculate now - updateTimeDomainData(); - } - tdr_users++; - if(tdr_users == 1) { - emit changedTDRstate(true); - } -} - -void Trace::removeTDRinterest() -{ - if(tdr_users > 0) { - tdr_users--; - if(tdr_users == 0) { - emit changedTDRstate(false); - } - } -} - -Trace::TimedomainData Trace::getTDR(double position) -{ - TimedomainData ret = {}; - if(!TDRactive() || position < 0) { - return ret; - } - int index = 0; - bool exact = false; - double alpha = 0.0; - if(position <= timeDomain.back().time) { - auto lower = lower_bound(timeDomain.begin(), timeDomain.end(), position, [](const TimedomainData &lhs, const double pos) -> bool { - return lhs.time < pos; - }); - index = lower - timeDomain.begin(); - if(timeDomain.at(index).time == position) { - exact = true; - } else { - alpha = (position - timeDomain.at(index-1).time) / (timeDomain.at(index).time - timeDomain.at(index-1).time); - } - } else { - if(position > timeDomain.back().distance) { - // too high, invalid position - return ret; - } - auto lower = lower_bound(timeDomain.begin(), timeDomain.end(), position, [](const TimedomainData &lhs, const double pos) -> bool { - return lhs.distance < pos; - }); - index = lower - timeDomain.begin(); - if(timeDomain.at(index).distance == position) { - exact = true; - } else { - alpha = (position - timeDomain.at(index-1).distance) / (timeDomain.at(index).distance - timeDomain.at(index-1).distance); - } - } - if(exact) { - return timeDomain.at(index); - } else { - // need to interpolate - auto low = timeDomain.at(index-1); - auto high = timeDomain.at(index); - ret.time = low.time * (1.0 - alpha) + high.time * alpha; - ret.distance = low.distance * (1.0 - alpha) + high.distance * alpha; - ret.stepResponse = low.stepResponse * (1.0 - alpha) + high.stepResponse * alpha; - ret.impulseResponse = low.impulseResponse * (1.0 - alpha) + high.impulseResponse * alpha; - ret.impedance = low.impedance * (1.0 - alpha) + high.impedance * alpha; - return ret; - } -} - QString Trace::description() { return name() + ": measured data"; @@ -502,26 +353,30 @@ unsigned int Trace::size() return lastMath->numSamples(); } -double Trace::minFreq() +double Trace::minX() { - if(size() > 0) { - return data.front().x; + if(lastMath->numSamples() > 0) { + return lastMath->rData().front().x; } else { - return 0.0; + return numeric_limits::quiet_NaN(); } } -double Trace::maxFreq() +double Trace::maxX() { - if(size() > 0) { - return data.back().x; + if(lastMath->numSamples() > 0) { + return lastMath->rData().back().x; } else { - return 0.0; + return numeric_limits::quiet_NaN(); } } double Trace::findExtremumFreq(bool max) { + if(lastMath->getDataType() != DataType::Frequency) { + // not in frequency domain + return numeric_limits::quiet_NaN(); + } double compare = max ? numeric_limits::min() : numeric_limits::max(); double freq = 0.0; for(auto sample : lastMath->rData()) { @@ -537,6 +392,10 @@ double Trace::findExtremumFreq(bool max) std::vector Trace::findPeakFrequencies(unsigned int maxPeaks, double minLevel, double minValley) { + if(lastMath->getDataType() != DataType::Frequency) { + // not in frequency domain + return vector(); + } using peakInfo = struct peakinfo { double frequency; double level_dbm; @@ -587,9 +446,14 @@ std::vector Trace::findPeakFrequencies(unsigned int maxPeaks, double min return frequencies; } -Trace::Data Trace::sample(unsigned int index) +Trace::Data Trace::sample(unsigned int index, SampleType type) { - return lastMath->getSample(index); + auto data = lastMath->getSample(index); + if(type == SampleType::TimeStep) { + // exchange impulse data with step data + data.y = lastMath->getStepResponse(index); + } + return data; } QString Trace::getTouchstoneFilename() const @@ -607,41 +471,23 @@ unsigned int Trace::getTouchstoneParameter() const return touchstoneParameter; } -std::complex Trace::getData(double frequency) -{ - if(lastMath->numSamples() == 0 || frequency < minFreq() || frequency > maxFreq()) { - return std::numeric_limits>::quiet_NaN(); - } - - auto i = index(frequency); - if(lastMath->getSample(i).x == frequency) { - return lastMath->getSample(i).y; - } else { - // no exact frequency match, needs to interpolate - auto high = lastMath->getSample(i); - auto low = lastMath->getSample(i - 1); - double alpha = (frequency - low.x) / (high.x - low.x); - return low.y * (1 - alpha) + high.y * alpha; - } -} - double Trace::getNoise(double frequency) { - if(!isLive() || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2)) { + if(!isLive() || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2) || lastMath->getDataType() != DataType::Frequency) { // data not suitable for noise calculation return std::numeric_limits::quiet_NaN(); } // convert to dbm - auto dbm = 20*log10(abs(getData(frequency))); + auto dbm = 20*log10(abs(lastMath->getInterpolatedSample(frequency).y)); // convert to 1Hz bandwidth dbm -= 10*log10(settings.SA.RBW); return dbm; } -int Trace::index(double frequency) +int Trace::index(double x) { - auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), frequency, [](const Data &lhs, const double freq) -> bool { - return lhs.x < freq; + auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), x, [](const Data &lhs, const double x) -> bool { + return lhs.x < x; }); return lower - lastMath->rData().begin(); } diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h index 3b3b608..34b2c07 100644 --- a/Software/PC_Application/Traces/trace.h +++ b/Software/PC_Application/Traces/trace.h @@ -19,15 +19,6 @@ public: using Data = TraceMath::Data; - class TimedomainData { - public: - double time; - double distance; - double impulseResponse; - double stepResponse; - double impedance; - }; - enum class LiveParameter { S11, S12, @@ -66,9 +57,10 @@ public: bool isReflection(); LiveParameter liveParameter() { return _liveParam; } LivedataType liveType() { return _liveType; } + TraceMath::DataType outputType() { return lastMath->getDataType(); }; unsigned int size(); - double minFreq(); - double maxFreq(); + double minX(); + double maxX(); double findExtremumFreq(bool max); /* Searches for peaks in the trace data and returns the peak frequencies in ascending order. * Up to maxPeaks will be returned, with higher level peaks taking priority over lower level peaks. @@ -76,30 +68,22 @@ public: * To detect the next peak, the signal first has to drop at least minValley below the peak level. */ std::vector findPeakFrequencies(unsigned int maxPeaks = 100, double minLevel = -100.0, double minValley = 3.0); - Data sample(unsigned int index); + enum class SampleType { + Frequency, + TimeImpulse, + TimeStep, + }; + + Data sample(unsigned int index, SampleType type = SampleType::Frequency); QString getTouchstoneFilename() const; unsigned int getTouchstoneParameter() const; - std::complex getData(double frequency); /* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */ double getNoise(double frequency); - int index(double frequency); + int index(double x); std::set getMarkers() const; void setCalibration(bool value); void setReflection(bool value); - // TDR calculation can be ressource intensive, only perform when some other module is interested. - // Each interested module should call addTDRinterest(), read the data with getTDR() and finally - // call removeTDRinterest() once TDR updates are no longer required. - // The data is only updated at the end of a sweep and upon the first addTDRinterest() call. - void addTDRinterest(); - void removeTDRinterest(); - bool TDRactive() { return tdr_users > 0;}; - const std::vector& getTDR() { return timeDomain;} - // interpolates the TDR data - // position is assumed to be the delay if it is smaller than the maximum sampled delay, otherwise it is assumed to be the distance. - // Since the usual delay values are way smaller than the distance values this should work - TimedomainData getTDR(double position); - DataType outputType(DataType inputType) override { Q_UNUSED(inputType) return DataType::Frequency;}; QString description() override; @@ -128,7 +112,6 @@ public slots: void addMarker(TraceMarker *m); void removeMarker(TraceMarker *m); -private: signals: void cleared(Trace *t); void typeChanged(Trace *t); @@ -139,14 +122,8 @@ signals: void colorChanged(Trace *t); void markerAdded(TraceMarker *m); void markerRemoved(TraceMarker *m); - void changedTDRstate(bool enabled); private: - void updateTimeDomainData(); - void printTimeDomain(); -// std::vector _data; - std::vector timeDomain; - unsigned int tdr_users; QString _name; QColor _color; LivedataType _liveType; diff --git a/Software/PC_Application/Traces/traceexportdialog.cpp b/Software/PC_Application/Traces/traceexportdialog.cpp index 95b0ec8..1735190 100644 --- a/Software/PC_Application/Traces/traceexportdialog.cpp +++ b/Software/PC_Application/Traces/traceexportdialog.cpp @@ -145,8 +145,8 @@ void TraceExportDialog::selectionChanged(QComboBox *w) points = t->size(); ui->points->setText(QString::number(points)); if(points > 0) { - lowerFreq = t->minFreq(); - upperFreq = t->maxFreq(); + lowerFreq = t->minX(); + upperFreq = t->maxX(); ui->lowerFreq->setText(QString::number(lowerFreq)); ui->upperFreq->setText(QString::number(upperFreq)); } @@ -157,7 +157,7 @@ void TraceExportDialog::selectionChanged(QComboBox *w) for(auto c : v1) { for(int i=1;icount();i++) { Trace *t = qvariant_cast(c->itemData(i)); - if(t->size() != points || (points > 0 && (t->minFreq() != lowerFreq || t->maxFreq() != upperFreq))) { + if(t->size() != points || (points > 0 && (t->minX() != lowerFreq || t->maxX() != upperFreq))) { // this trace is not available anymore c->removeItem(i); // decrement to check the next index in the next loop iteration diff --git a/Software/PC_Application/Traces/tracemarker.cpp b/Software/PC_Application/Traces/tracemarker.cpp index f632d5f..80c05a8 100644 --- a/Software/PC_Application/Traces/tracemarker.cpp +++ b/Software/PC_Application/Traces/tracemarker.cpp @@ -35,35 +35,6 @@ TraceMarker::~TraceMarker() emit deleted(this); } -void TraceMarker::setTimeDomain(bool timeDomain) -{ - if(timeDomain != this->timeDomain) { - // setting changed, check if actually available - if(timeDomain && !parentTrace->TDRactive()) { - qWarning() << "Attempted to enable TDR marker on trace without active TDR"; - return; - } - this->timeDomain = timeDomain; - - if(timeDomain) { - // need to delete this marker if the TDR data of the trace is no longer available - connect(parentTrace, &Trace::changedTDRstate, [=](bool tdr_available){ - if(!tdr_available) { - delete this; - } - }); - // check if current type still supported - if(!getSupportedTypes().count(type)) { - // unsupported type, change to manual - setType(Type::Manual); - } - } else { - disconnect(parentTrace, &Trace::changedTDRstate, nullptr, nullptr); - } - emit timeDomainChanged(); - } -} - void TraceMarker::assignTrace(Trace *t) { if(parentTrace) { @@ -73,7 +44,6 @@ void TraceMarker::assignTrace(Trace *t) disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged); disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol); } - setTimeDomain(false); parentTrace = t; if(!getSupportedTypes().count(type)) { // new trace does not support the current type @@ -104,44 +74,45 @@ QString TraceMarker::readableData() case Type::Manual: case Type::Maximum: case Type::Minimum: - if(isTimeDomain()) { - QString ret; - ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:"; - if(isnan(timeData.impedance)) { - ret += "Invalid"; - } else { - ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3); - } - return ret; - } else { +// if(isTimeDomain()) { +// QString ret; +// ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:"; +// if(isnan(timeData.impedance)) { +// ret += "Invalid"; +// } else { +// ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3); +// } +// return ret; +// } else + { auto phase = arg(data); return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4); } case Type::Delta: - if(!delta || delta->isTimeDomain() != isTimeDomain()) { + if(!delta /*|| delta->isTimeDomain() != isTimeDomain()*/) { return "Invalid delta marker"; } else { - if(isTimeDomain()) { - // calculate difference between markers - auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse; - auto step = timeData.stepResponse - delta->timeData.stepResponse; - auto impedance = timeData.impedance - delta->timeData.impedance; - QString ret; - ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:"; - if(isnan(timeData.impedance)) { - ret += "Invalid"; - } else { - ret += Unit::ToString(impedance, "Ω", "m k", 3); - } - return ret; - } else { +// if(isTimeDomain()) { +// // calculate difference between markers +// auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse; +// auto step = timeData.stepResponse - delta->timeData.stepResponse; +// auto impedance = timeData.impedance - delta->timeData.impedance; +// QString ret; +// ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:"; +// if(isnan(timeData.impedance)) { +// ret += "Invalid"; +// } else { +// ret += Unit::ToString(impedance, "Ω", "m k", 3); +// } +// return ret; +// } else { // calculate difference between markers auto freqDiff = position - delta->position; auto valueDiff = data / delta->data; auto phase = arg(valueDiff); auto db = 20*log10(abs(valueDiff)); return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4); - } +// } } break; case Type::Noise: @@ -205,22 +176,22 @@ QString TraceMarker::readableData() QString TraceMarker::readableSettings() { - if(timeDomain) { - switch(type) { - case Type::Manual: - case Type::Delta: { - QString unit; - if(position <= parentTrace->getTDR().back().time) { - unit = "s"; - } else { - unit = "m"; - } - return Unit::ToString(position, unit, "fpnum k", 4); - } - default: - return "Unhandled case"; - } - } else { +// if(timeDomain) { +// switch(type) { +// case Type::Manual: +// case Type::Delta: { +// QString unit; +// if(position <= parentTrace->getTDR().back().time) { +// unit = "s"; +// } else { +// unit = "m"; +// } +// return Unit::ToString(position, unit, "fpnum k", 4); +// } +// default: +// return "Unhandled case"; +// } +// } else { switch(type) { case Type::Manual: case Type::Maximum: @@ -241,7 +212,7 @@ QString TraceMarker::readableSettings() default: return "Unhandled case"; } - } +// } } QString TraceMarker::readableType() @@ -270,12 +241,12 @@ void TraceMarker::traceDataChanged() { // some data of the parent trace changed, check if marker data also changed complex newdata; - if (timeDomain) { - timeData = parentTrace->getTDR(position); - newdata = complex(timeData.stepResponse, timeData.impulseResponse); - } else { - newdata = parentTrace->getData(position); - } +// if (timeDomain) { +// timeData = parentTrace->getTDR(position); +// newdata = complex(timeData.stepResponse, timeData.impulseResponse); +// } else { + newdata = parentTrace->sample(parentTrace->index(position)).y; +// } if (newdata != data) { data = newdata; update(); @@ -309,11 +280,11 @@ std::set TraceMarker::getSupportedTypes() { set supported; if(parentTrace) { - if(timeDomain) { - // only basic markers in time domain - supported.insert(Type::Manual); - supported.insert(Type::Delta); - } else { +// if(timeDomain) { +// // only basic markers in time domain +// supported.insert(Type::Manual); +// supported.insert(Type::Delta); +// } else { // all traces support some basic markers supported.insert(Type::Manual); supported.insert(Type::Maximum); @@ -340,7 +311,7 @@ std::set TraceMarker::getSupportedTypes() break; } } - } +// } } return supported; } @@ -348,21 +319,21 @@ std::set TraceMarker::getSupportedTypes() void TraceMarker::constrainPosition() { if(parentTrace) { - if(timeDomain) { - if(position < 0) { - position = 0; - } else if(position > parentTrace->getTDR().back().distance) { - position = parentTrace->getTDR().back().distance; - } - } else { +// if(timeDomain) { +// if(position < 0) { +// position = 0; +// } else if(position > parentTrace->getTDR().back().distance) { +// position = parentTrace->getTDR().back().distance; +// } +// } else { if(parentTrace->size() > 0) { - if(position > parentTrace->maxFreq()) { - position = parentTrace->maxFreq(); - } else if(position < parentTrace->minFreq()) { - position = parentTrace->minFreq(); + if(position > parentTrace->maxX()) { + position = parentTrace->maxX(); + } else if(position < parentTrace->minX()) { + position = parentTrace->minX(); } } - } +// } traceDataChanged(); } } @@ -410,10 +381,10 @@ void TraceMarker::setType(TraceMarker::Type t) // invalid delta marker assigned, attempt to find a matching marker for(int pass = 0;pass < 3;pass++) { for(auto m : model->getMarkers()) { - if(m->isTimeDomain() != isTimeDomain()) { - // markers are not on the same domain - continue; - } +// if(m->isTimeDomain() != isTimeDomain()) { +// // markers are not on the same domain +// continue; +// } if(pass == 0 && m->parentTrace != parentTrace) { // ignore markers on different traces in first pass continue; @@ -484,11 +455,6 @@ QString TraceMarker::getSuffix() const return suffix; } -bool TraceMarker::isTimeDomain() const -{ - return timeDomain; -} - const std::vector &TraceMarker::getHelperMarkers() const { return helperMarkers; @@ -594,15 +560,15 @@ void TraceMarker::updateTypeFromEditor(QWidget *w) SIUnitEdit *TraceMarker::getSettingsEditor() { - if(timeDomain) { - switch(type) { - case Type::Manual: - case Type::Delta: - return new SIUnitEdit("", "fpnum k", 6); - default: - return nullptr; - } - } else { +// if(timeDomain) { +// switch(type) { +// case Type::Manual: +// case Type::Delta: +// return new SIUnitEdit("", "fpnum k", 6); +// default: +// return nullptr; +// } +// } else { switch(type) { case Type::Manual: case Type::Maximum: @@ -619,7 +585,7 @@ SIUnitEdit *TraceMarker::getSettingsEditor() case Type::TOI: return nullptr; } - } +// } } void TraceMarker::adjustSettings(double value) @@ -798,15 +764,6 @@ std::complex TraceMarker::getData() const return data; } -Trace::TimedomainData TraceMarker::getTimeData() const -{ - Trace::TimedomainData ret = {}; - if(timeDomain) { - ret = timeData; - } - return ret; -} - bool TraceMarker::isMovable() { if(parent) { diff --git a/Software/PC_Application/Traces/tracemarker.h b/Software/PC_Application/Traces/tracemarker.h index f6e13df..9ac544c 100644 --- a/Software/PC_Application/Traces/tracemarker.h +++ b/Software/PC_Application/Traces/tracemarker.h @@ -15,7 +15,6 @@ class TraceMarker : public QObject public: TraceMarker(TraceMarkerModel *model, int number = 1, TraceMarker *parent = nullptr, QString descr = QString()); ~TraceMarker(); - void setTimeDomain(bool timeDomain); void assignTrace(Trace *t); Trace* trace(); QString readableData(); @@ -24,7 +23,6 @@ public: double getPosition() const; std::complex getData() const; - Trace::TimedomainData getTimeData() const; bool isMovable(); QPixmap& getSymbol(); @@ -48,8 +46,6 @@ public: TraceMarker *helperMarker(unsigned int i); QString getSuffix() const; - bool isTimeDomain() const; - public slots: void setPosition(double freq); signals: @@ -114,7 +110,6 @@ private: // Frequency domain: S parameter // Time domain: imag part is impulse response, real part is step response std::complex data; - Trace::TimedomainData timeData; QPixmap symbol; Type type; QString suffix; @@ -128,8 +123,6 @@ private: double peakThreshold; double offset; }; - - bool timeDomain; }; #endif // TRACEMARKER_H diff --git a/Software/PC_Application/Traces/tracemarkermodel.cpp b/Software/PC_Application/Traces/tracemarkermodel.cpp index 59408bf..f4d274c 100644 --- a/Software/PC_Application/Traces/tracemarkermodel.cpp +++ b/Software/PC_Application/Traces/tracemarkermodel.cpp @@ -200,10 +200,7 @@ bool TraceMarkerModel::setData(const QModelIndex &index, const QVariant &value, break; case ColIndexTrace: { auto info = qvariant_cast(value); - // always disable timedomain before switching trace - m->setTimeDomain(false); m->assignTrace(info.trace); - m->setTimeDomain(info.isTimeDomain); } break; case ColIndexSettings: { @@ -288,12 +285,7 @@ QWidget *MarkerTraceDelegate::createEditor(QWidget *parent, const QStyleOptionVi for(auto t : traces) { MarkerWidgetTraceInfo info; info.trace = t; - info.isTimeDomain = false; c->addItem(t->name(), QVariant::fromValue(info)); - if(t->TDRactive()) { - info.isTimeDomain = true; - c->addItem(t->name() + " Time Domain", QVariant::fromValue(info)); - } } return c; } @@ -304,7 +296,6 @@ void MarkerTraceDelegate::setEditorData(QWidget *editor, const QModelIndex &inde auto c = (QComboBox*) editor; MarkerWidgetTraceInfo markerInfo; markerInfo.trace = marker->trace(); - markerInfo.isTimeDomain = marker->isTimeDomain(); for(int i=0;icount();i++) { auto info = qvariant_cast(c->itemData(i)); if(info == markerInfo) { diff --git a/Software/PC_Application/Traces/tracemarkermodel.h b/Software/PC_Application/Traces/tracemarkermodel.h index bb85461..43582dc 100644 --- a/Software/PC_Application/Traces/tracemarkermodel.h +++ b/Software/PC_Application/Traces/tracemarkermodel.h @@ -19,13 +19,11 @@ class MarkerTraceDelegate : public QStyledItemDelegate class MarkerWidgetTraceInfo { public: Trace *trace; - bool isTimeDomain; - }; inline bool operator==(const MarkerWidgetTraceInfo& lhs, const MarkerWidgetTraceInfo& rhs) { - return lhs.trace == rhs.trace && lhs.isTimeDomain == rhs.isTimeDomain; + return lhs.trace == rhs.trace; } Q_DECLARE_METATYPE(MarkerWidgetTraceInfo) diff --git a/Software/PC_Application/Traces/tracemodel.cpp b/Software/PC_Application/Traces/tracemodel.cpp index d29e929..21c8e1f 100644 --- a/Software/PC_Application/Traces/tracemodel.cpp +++ b/Software/PC_Application/Traces/tracemodel.cpp @@ -23,9 +23,6 @@ void TraceModel::addTrace(Trace *t) connect(t, &Trace::nameChanged, [=]() { emit traceNameChanged(t); }); - connect(t, &Trace::changedTDRstate, [=](bool enabled) { - emit traceTDRstateChanged(t, enabled); - }); traces.push_back(t); endInsertRows(); emit traceAdded(t); diff --git a/Software/PC_Application/Traces/traceplot.cpp b/Software/PC_Application/Traces/traceplot.cpp index 083cb18..25b2c38 100644 --- a/Software/PC_Application/Traces/traceplot.cpp +++ b/Software/PC_Application/Traces/traceplot.cpp @@ -53,12 +53,14 @@ void TracePlot::enableTrace(Trace *t, bool enabled) connect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot); connect(t, &Trace::markerAdded, this, &TracePlot::markerAdded); connect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved); + connect(t, &Trace::typeChanged, this, &TracePlot::checkIfStillSupported); } else { // disconnect from notifications disconnect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot); disconnect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot); disconnect(t, &Trace::markerAdded, this, &TracePlot::markerAdded); disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved); + disconnect(t, &Trace::typeChanged, this, &TracePlot::checkIfStillSupported); } updateContextMenu(); replot(); @@ -271,6 +273,14 @@ void TracePlot::triggerReplot() } } +void TracePlot::checkIfStillSupported(Trace *t) +{ + if(!supported(t)) { + // something with this trace changed and it can no longer be displayed on this graph + enableTrace(t, false); + } +} + void TracePlot::markerAdded(TraceMarker *m) { connect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot); diff --git a/Software/PC_Application/Traces/traceplot.h b/Software/PC_Application/Traces/traceplot.h index e378b5d..3f87962 100644 --- a/Software/PC_Application/Traces/traceplot.h +++ b/Software/PC_Application/Traces/traceplot.h @@ -59,6 +59,7 @@ protected slots: void newTraceAvailable(Trace *t); void traceDeleted(Trace *t); void triggerReplot(); + void checkIfStillSupported(Trace *t); virtual void markerAdded(TraceMarker *m); virtual void markerRemoved(TraceMarker *m); protected: diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp index 993c04a..5b79f18 100644 --- a/Software/PC_Application/Traces/tracesmithchart.cpp +++ b/Software/PC_Application/Traces/tracesmithchart.cpp @@ -56,12 +56,12 @@ std::complex TraceSmithChart::pixelToData(QPoint p) QPoint TraceSmithChart::markerToPixel(TraceMarker *m) { QPoint ret = QPoint(); - if(!m->isTimeDomain()) { +// if(!m->isTimeDomain()) { if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) { auto d = m->getData(); ret = transform.map(QPoint(d.real() * smithCoordMax, -d.imag() * smithCoordMax)); } - } +// } return ret; } @@ -160,9 +160,9 @@ void TraceSmithChart::draw(QPainter &p) { // only draw markers if the trace has at least one point auto markers = t.first->getMarkers(); for(auto m : markers) { - if (m->isTimeDomain()) { - continue; - } +// if (m->isTimeDomain()) { +// continue; +// } if (limitToSpan && (m->getPosition() < sweep_fmin || m->getPosition() > sweep_fmax)) { continue; } @@ -268,7 +268,7 @@ void TraceSmithChart::updateContextMenu() bool TraceSmithChart::supported(Trace *t) { - if(t->isReflection()) { + if(t->outputType() == Trace::DataType::Frequency && t->isReflection()) { return true; } else { return false; diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp index 4c16231..aa796b8 100644 --- a/Software/PC_Application/Traces/tracexyplot.cpp +++ b/Software/PC_Application/Traces/tracexyplot.cpp @@ -67,27 +67,14 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool } } } while(erased); - - auto oldType = YAxis[axis].type; - if(isTDRtype(YAxis[axis].type) && !isTDRtype(type)) { - // not TDR axis anymore - for(auto t : tracesAxis[axis]) { - t->removeTDRinterest(); - } - } YAxis[axis].type = type; - if(isTDRtype(type) && !isTDRtype(oldType)) { - // now a TDR axis - for(auto t : tracesAxis[axis]) { - t->addTDRinterest(); - } - } } YAxis[axis].log = log; YAxis[axis].autorange = autorange; YAxis[axis].rangeMin = min; YAxis[axis].rangeMax = max; YAxis[axis].rangeDiv = div; + removeUnsupportedTraces(); updateAxisTicks(); updateContextMenu(); replot(); @@ -100,15 +87,14 @@ void TraceXYPlot::setXAxis(XAxisType type, XAxisMode mode, double min, double ma XAxis.rangeMin = min; XAxis.rangeMax = max; XAxis.rangeDiv = div; + removeUnsupportedTraces(); updateAxisTicks(); } void TraceXYPlot::enableTrace(Trace *t, bool enabled) { for(int axis = 0;axis < 2;axis++) { - if(supported(t, YAxis[axis].type)) { - enableTraceAxis(t, axis, enabled); - } + enableTraceAxis(t, axis, enabled && supported(t, YAxis[axis].type)); } } @@ -184,10 +170,10 @@ void TraceXYPlot::updateContextMenu() }); } -bool TraceXYPlot::supported(Trace *) +bool TraceXYPlot::supported(Trace *t) { // potentially possible to add every kind of trace (depends on axis) - if(YAxis[0].type != YAxisType::Disabled || YAxis[1].type != YAxisType::Disabled) { + if(supported(t, YAxis[0].type) || supported(t, YAxis[1].type)) { return true; } else { // no axis @@ -353,7 +339,7 @@ void TraceXYPlot::draw(QPainter &p) pen.setStyle(Qt::SolidLine); } p.setPen(pen); - auto nPoints = numTraceSamples(t); + auto nPoints = t->size(); for(unsigned int j=1;jgetMarkers(); for(auto m : markers) { - if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) { - // wrong domain, skip this marker - continue; - } +// if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) { +// // wrong domain, skip this marker +// continue; +// } double xPosition; - if(m->isTimeDomain()) { - if(XAxis.type == XAxisType::Distance) { - xPosition = m->getTimeData().distance; - } else { - xPosition = m->getTimeData().time; - } - } else { +// if(m->isTimeDomain()) { +// if(XAxis.type == XAxisType::Distance) { +// xPosition = m->getTimeData().distance; +// } else { +// xPosition = m->getTimeData().time; +// } +// } else { xPosition = m->getPosition(); - } +// } if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) { continue; } - QPointF markerPoint = QPointF(xPosition, traceToCoordinate(m->getData(), YAxis[i].type)); + auto t = m->getTrace(); + QPointF markerPoint = traceToCoordinate(t, t->index(xPosition), YAxis[i].type); auto point = plotValueToPixel(markerPoint, i); if(!plotRect.contains(point)) { // out of screen @@ -412,7 +399,8 @@ void TraceXYPlot::draw(QPainter &p) p.setOpacity(0.5); p.setBrush(Qt::white); p.setPen(Qt::white); - if(YAxis[0].type == YAxisType::Disabled || YAxis[1].type == YAxisType::Disabled) { + if((YAxis[0].type == YAxisType::Disabled || !supported(dropTrace, YAxis[0].type)) + || (YAxis[1].type == YAxisType::Disabled || !supported(dropTrace, YAxis[1].type))) { // only one axis enabled, show drop area over whole plot p.drawRect(plotRect); auto font = p.font(); @@ -500,27 +488,13 @@ void TraceXYPlot::updateAxisTicks() || tracesAxis[1].find(t.first) != tracesAxis[1].end()); auto trace = t.first; if(enabled && trace->isVisible()) { - if(!numTraceSamples(trace)) { + if(!trace->size()) { // empty trace, do not use for automatic axis calculation continue; } // this trace is currently displayed - double trace_min = std::numeric_limits::max(); - double trace_max = std::numeric_limits::lowest(); - switch(XAxis.type) { - case XAxisType::Frequency: - trace_min = trace->minFreq(); - trace_max = trace->maxFreq(); - break; - case XAxisType::Time: - trace_min = trace->getTDR().front().time; - trace_max = trace->getTDR().back().time; - break; - case XAxisType::Distance: - trace_min = trace->getTDR().front().distance; - trace_max = trace->getTDR().back().distance; - break; - } + double trace_min = trace->minX(); + double trace_max = trace->maxX(); if(trace_min < min) { min = trace_min; } @@ -546,7 +520,7 @@ void TraceXYPlot::updateAxisTicks() double max = std::numeric_limits::lowest(); double min = std::numeric_limits::max(); for(auto t : tracesAxis[i]) { - unsigned int samples = numTraceSamples(t); + unsigned int samples = t->size(); for(unsigned int j=0;j max) { max = point.y(); - } else if(point.y() < min) { + } + if(point.y() < min) { min = point.y(); } } @@ -608,14 +583,7 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) if(alreadyEnabled != enabled) { if(enabled) { tracesAxis[axis].insert(t); - // connect signals - if(isTDRtype(YAxis[axis].type)) { - t->addTDRinterest(); - } } else { - if(isTDRtype(YAxis[axis].type)) { - t->removeTDRinterest(); - } tracesAxis[axis].erase(t); if(axis == 0) { disconnect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded); @@ -634,6 +602,22 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) { + switch(XAxis.type) { + case XAxisType::Frequency: + if(t->outputType() != Trace::DataType::Frequency) { + return false; + } + break; + case XAxisType::Distance: + case XAxisType::Time: + if(t->outputType() != Trace::DataType::Time) { + return false; + } + break; + default: + break; + } + switch(type) { case YAxisType::Disabled: return false; @@ -648,66 +632,45 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) return true; } -double TraceXYPlot::traceToCoordinate(std::complex data, TraceXYPlot::YAxisType type) +void TraceXYPlot::removeUnsupportedTraces() { - switch(type) { - case YAxisType::Magnitude: - return 20*log10(abs(data)); - case YAxisType::Phase: - return arg(data) * 180.0 / M_PI; - case YAxisType::VSWR: - if(abs(data) < 1.0) { - return (1+abs(data)) / (1-abs(data)); + for(unsigned int i=0;i<2;i++) { + auto set_copy = tracesAxis[i]; + for(auto t : set_copy) { + if(!supported(t, YAxis[i].type)) { + enableTraceAxis(t, i, false); + } } - break; - case YAxisType::Step: - return data.real(); - case YAxisType::Impulse: - return data.imag(); - case YAxisType::Impedance: - if(abs(data.real()) < 1.0) { - return 50 * (1+data.real()) / (1-data.real()); - } - default: - break; } - return numeric_limits::quiet_NaN(); } QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlot::YAxisType type) { QPointF ret = QPointF(numeric_limits::quiet_NaN(), numeric_limits::quiet_NaN()); + auto data = t->sample(sample); + ret.setX(data.x); switch(type) { case YAxisType::Magnitude: + ret.setY(20*log10(abs(data.y))); + break; case YAxisType::Phase: - case YAxisType::VSWR: { - auto d = t->sample(sample); - ret.setY(traceToCoordinate(d.y, type)); - ret.setX(d.x); - } + ret.setY(arg(data.y) * 180.0 / M_PI); + break; + case YAxisType::VSWR: + if(abs(data.y) < 1.0) { + ret.setY((1+abs(data.y)) / (1-abs(data.y))); + } break; case YAxisType::Impulse: - ret.setY(t->getTDR()[sample].impulseResponse); - if(XAxis.type == XAxisType::Distance) { - ret.setX(t->getTDR()[sample].distance); - } else { - ret.setX(t->getTDR()[sample].time); - } + ret.setY(real(data.y)); break; case YAxisType::Step: - ret.setY(t->getTDR()[sample].stepResponse); - if(XAxis.type == XAxisType::Distance) { - ret.setX(t->getTDR()[sample].distance); - } else { - ret.setX(t->getTDR()[sample].time); - } + ret.setY(t->sample(sample, Trace::SampleType::TimeStep).y.real()); break; case YAxisType::Impedance: { - ret.setY(t->getTDR()[sample].impedance); - if(XAxis.type == XAxisType::Distance) { - ret.setX(t->getTDR()[sample].distance); - } else { - ret.setX(t->getTDR()[sample].time); + double step = t->sample(sample, Trace::SampleType::TimeStep).y.real(); + if(abs(step) < 1.0) { + ret.setY(50 * (1.0+step) / (1.0-step)); } } break; @@ -719,26 +682,17 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo return ret; } -unsigned int TraceXYPlot::numTraceSamples(Trace *t) -{ - if(XAxis.type == XAxisType::Frequency) { - return t->size(); - } else { - return t->getTDR().size(); - } -} - -QPoint TraceXYPlot::dataToPixel(Trace::Data d) -{ - if(d.x < XAxis.rangeMin || d.x > XAxis.rangeMax) { - return QPoint(); - } - auto y = traceToCoordinate(d.y, YAxis[0].type); - QPoint p; - p.setX(Util::Scale(d.x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); - p.setY(Util::Scale(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0)); - return p; -} +//QPoint TraceXYPlot::dataToPixel(Trace::Data d) +//{ +// if(d.x < XAxis.rangeMin || d.x > XAxis.rangeMax) { +// return QPoint(); +// } +// auto y = traceToCoordinate(d.y, YAxis[0].type); +// QPoint p; +// p.setX(Util::Scale(d.x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); +// p.setY(Util::Scale(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0)); +// return p; +//} QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis) { @@ -759,22 +713,12 @@ QPointF TraceXYPlot::pixelToPlotValue(QPoint pixel, int Yaxis) QPoint TraceXYPlot::markerToPixel(TraceMarker *m) { QPoint ret = QPoint(); - if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) { - // invalid domain - return ret; - } - QPointF plotPoint; - plotPoint.setY(traceToCoordinate(m->getData(), YAxis[0].type)); - if(m->isTimeDomain()) { - auto timedata = m->getTimeData(); - if(XAxis.type == XAxisType::Distance) { - plotPoint.setX(timedata.distance); - } else { - plotPoint.setX(timedata.time); - } - } else { - plotPoint.setX(m->getPosition()); - } +// if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) { +// // invalid domain +// return ret; +// } + auto t = m->getTrace(); + QPointF plotPoint = traceToCoordinate(t, t->index(m->getPosition()), YAxis[0].type); return plotValueToPixel(plotPoint, 0); } @@ -786,7 +730,7 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel) } double closestDistance = numeric_limits::max(); double closestXpos = 0; - auto samples = numTraceSamples(t); + auto samples = t->size(); for(unsigned int i=0;i data, YAxisType type); + void removeUnsupportedTraces(); QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type); - unsigned int numTraceSamples(Trace *t); - QPoint dataToPixel(Trace::Data d); +// QPoint dataToPixel(Trace::Data d); QPoint plotValueToPixel(QPointF plotValue, int Yaxis); QPointF pixelToPlotValue(QPoint pixel, int YAxis); QPoint markerToPixel(TraceMarker *m) override;