diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index 1b7f92b..9f00806 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -25,6 +25,8 @@ HEADERS += \ SpectrumAnalyzer/tracewidgetsa.h \ Tools/eseries.h \ Tools/impedancematchdialog.h \ + Traces/Math/tracemath.h \ + Traces/Math/tracematheditdialog.h \ Traces/fftcomplex.h \ Traces/markerwidget.h \ Traces/trace.h \ @@ -77,6 +79,8 @@ SOURCES += \ SpectrumAnalyzer/tracewidgetsa.cpp \ Tools/eseries.cpp \ Tools/impedancematchdialog.cpp \ + Traces/Math/tracemath.cpp \ + Traces/Math/tracematheditdialog.cpp \ Traces/fftcomplex.cpp \ Traces/markerwidget.cpp \ Traces/trace.cpp \ @@ -121,6 +125,7 @@ FORMS += \ Device/manualcontroldialog.ui \ Generator/signalgenwidget.ui \ Tools/impedancematchdialog.ui \ + Traces/Math/tracematheditdialog.ui \ Traces/markerwidget.ui \ Traces/smithchartdialog.ui \ Traces/traceeditdialog.ui \ diff --git a/Software/PC_Application/Calibration/calibration.cpp b/Software/PC_Application/Calibration/calibration.cpp index 875af02..7df8753 100644 --- a/Software/PC_Application/Calibration/calibration.cpp +++ b/Software/PC_Application/Calibration/calibration.cpp @@ -599,21 +599,21 @@ std::vector Calibration::getErrorTermTraces() } for(auto p : points) { Trace::Data d; - d.frequency = p.frequency; + d.x = p.frequency; for(int i=0;i<12;i++) { switch(i) { - case 0: d.S = p.fe00; break; - case 1: d.S = p.fe11; break; - case 2: d.S = p.fe10e01; break; - case 3: d.S = p.fe10e32; break; - case 4: d.S = p.fe22; break; - case 5: d.S = p.fe30; break; - case 6: d.S = p.re33; break; - case 7: d.S = p.re11; break; - case 8: d.S = p.re23e32; break; - case 9: d.S = p.re23e01; break; - case 10: d.S = p.re22; break; - case 11: d.S = p.re03; break; + case 0: d.y = p.fe00; break; + case 1: d.y = p.fe11; break; + case 2: d.y = p.fe10e01; break; + case 3: d.y = p.fe10e32; break; + case 4: d.y = p.fe22; break; + case 5: d.y = p.fe30; break; + case 6: d.y = p.re33; break; + case 7: d.y = p.re11; break; + case 8: d.y = p.re23e32; break; + case 9: d.y = p.re23e01; break; + case 10: d.y = p.re22; break; + case 11: d.y = p.re03; break; } traces[i]->addData(d); } diff --git a/Software/PC_Application/Traces/Math/tracemath.cpp b/Software/PC_Application/Traces/Math/tracemath.cpp new file mode 100644 index 0000000..32ffcfb --- /dev/null +++ b/Software/PC_Application/Traces/Math/tracemath.cpp @@ -0,0 +1,61 @@ +#include "tracemath.h" + +TraceMath::TraceMath() +{ + input = nullptr; + dataType = DataType::Invalid; +} + +TraceMath::Data TraceMath::getSample(unsigned int index) +{ + return data.at(index); +} + +unsigned int TraceMath::numSamples() +{ + return data.size(); +} + +void TraceMath::removeInput() +{ + if(input) { + // disconnect everything from the input + disconnect(input, nullptr, this, nullptr); + input = nullptr; + data.clear(); + dataType = DataType::Invalid; + emit outputTypeChanged(dataType); + } +} + +void TraceMath::assignInput(TraceMath *input) +{ + Q_ASSERT(input != nullptr); + if(input != this->input) { + removeInput(); + this->input = input; + inputTypeChanged(input->dataType); + // do initial calculation + inputDataChanged(); + // connect to input + connect(input, &TraceMath::outputDataChanged, this, &TraceMath::inputDataChanged); + connect(input, &TraceMath::outputSampleChanged, this, &TraceMath::inputSampleChanged); + connect(input, &TraceMath::outputTypeChanged, this, &TraceMath::inputTypeChanged); + } +} + +void TraceMath::inputTypeChanged(TraceMath::DataType type) +{ + auto newType = outputType(type); + if(newType != dataType) { + dataType = newType; + data.clear(); + inputDataChanged(); + emit outputTypeChanged(dataType); + } +} + +TraceMath::DataType TraceMath::getDataType() const +{ + return dataType; +} diff --git a/Software/PC_Application/Traces/Math/tracemath.h b/Software/PC_Application/Traces/Math/tracemath.h new file mode 100644 index 0000000..c6b78cf --- /dev/null +++ b/Software/PC_Application/Traces/Math/tracemath.h @@ -0,0 +1,60 @@ +#ifndef TRACEMATH_H +#define TRACEMATH_H + +#include +#include +#include + +class TraceMath : public QObject { + Q_OBJECT +public: + TraceMath(); + + class Data { + public: + double x; + std::complex y; + }; + + enum class DataType { + Frequency, + Time, + Invalid, + }; + + Data getSample(unsigned int index); + unsigned int numSamples(); + + // indicate whether this function produces time or frequency domain data + virtual DataType outputType(DataType inputType) = 0; + + virtual QString description() = 0; + + void removeInput(); + void assignInput(TraceMath *input); + + DataType getDataType() const; + std::vector& rData() { return data;}; + +public slots: + // a single value of the input data has changed, index determines which sample has changed + virtual void inputSampleChanged(unsigned int index){Q_UNUSED(index)}; + // the complete input data has changed (e.g. cleared or all data modified by some operation) + virtual void inputDataChanged(){}; + + void inputTypeChanged(DataType type); + +signals: + // emit this whenever a sample changed (alternatively, if all samples are about to change, emit outputDataChanged after they have changed) + void outputSampleChanged(unsigned int index); + void outputDataChanged(); + // emit when the output type changed + void outputTypeChanged(DataType type); + +protected: + std::vector data; + TraceMath *input; + DataType dataType; +}; + +#endif // TRACEMATH_H diff --git a/Software/PC_Application/Traces/Math/tracematheditdialog.cpp b/Software/PC_Application/Traces/Math/tracematheditdialog.cpp new file mode 100644 index 0000000..d946b59 --- /dev/null +++ b/Software/PC_Application/Traces/Math/tracematheditdialog.cpp @@ -0,0 +1,81 @@ +#include "tracematheditdialog.h" +#include "ui_tracematheditdialog.h" + +TraceMathEditDialog::TraceMathEditDialog(Trace &t, QWidget *parent) : + QDialog(parent), + ui(new Ui::TraceMathEditDialog) +{ + auto model = new MathModel(t); + ui->setupUi(this); + ui->view->setModel(model); +} + +TraceMathEditDialog::~TraceMathEditDialog() +{ + delete ui; +} + +MathModel::MathModel(Trace &t, QObject *parent) + : QAbstractTableModel(parent), + t(t) +{ + +} + +int MathModel::rowCount(const QModelIndex &parent) const +{ + return t.getMath().size(); +} + +int MathModel::columnCount(const QModelIndex &parent) const +{ + return ColIndexLast; +} + +QVariant MathModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) { + return QVariant(); + } + auto math = t.getMath().at(index.row()); + switch(index.column()) { + case ColIndexEnabled: + if(role == Qt::CheckStateRole) { + return math.enabled ? Qt::Checked : Qt::Unchecked; + } + break; + case ColIndexDescription: + if(role == Qt::DisplayRole) { + return math.math->description(); + } + break; + } + return QVariant(); +} + +QVariant MathModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch(section) { + case ColIndexEnabled: return "Enabled"; break; + case ColIndexDescription: return "Description"; break; + default: break; + } + } + return QVariant(); +} + +Qt::ItemFlags MathModel::flags(const QModelIndex &index) const +{ + int flags = Qt::NoItemFlags; + if(index.row() > 1) { + // the first entry is always the trace itself and not enabled + flags |= Qt::ItemIsEnabled; + } + switch(index.column()) { + case ColIndexEnabled: flags |= Qt::ItemIsUserCheckable; break; + default: + break; + } + return (Qt::ItemFlags) flags; +} diff --git a/Software/PC_Application/Traces/Math/tracematheditdialog.h b/Software/PC_Application/Traces/Math/tracematheditdialog.h new file mode 100644 index 0000000..6edcde5 --- /dev/null +++ b/Software/PC_Application/Traces/Math/tracematheditdialog.h @@ -0,0 +1,46 @@ +#ifndef TRACEMATHEDITDIALOG_H +#define TRACEMATHEDITDIALOG_H + +#include +#include +#include "Traces/trace.h" + +namespace Ui { +class TraceMathEditDialog; +} + +class MathModel : public QAbstractTableModel +{ + Q_OBJECT +public: + MathModel(Trace &t, QObject *parent = 0); + + enum { + ColIndexEnabled = 0, + ColIndexDescription = 1, + ColIndexLast, + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + +private: + Trace &t; +}; + +class TraceMathEditDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TraceMathEditDialog(Trace &t, QWidget *parent = nullptr); + ~TraceMathEditDialog(); + +private: + Ui::TraceMathEditDialog *ui; +}; + +#endif // TRACEMATHEDITDIALOG_H diff --git a/Software/PC_Application/Traces/Math/tracematheditdialog.ui b/Software/PC_Application/Traces/Math/tracematheditdialog.ui new file mode 100644 index 0000000..87822c8 --- /dev/null +++ b/Software/PC_Application/Traces/Math/tracematheditdialog.ui @@ -0,0 +1,130 @@ + + + TraceMathEditDialog + + + + 0 + 0 + 411 + 302 + + + + Math functions + + + true + + + + + + + + QAbstractItemView::SelectRows + + + 20 + + + 70 + + + true + + + false + + + + + + + + + + 0 + 0 + + + + Add + + + + + + + :/icons/add.png:/icons/add.png + + + + + + + + 0 + 0 + + + + Delete + + + + + + + :/icons/remove.png:/icons/remove.png + + + + + + + Qt::Vertical + + + + 18 + 186 + + + + + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + TraceMathEditDialog + accept() + + + 216 + 280 + + + 216 + 150 + + + + + diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp index 5f089c9..07e4db5 100644 --- a/Software/PC_Application/Traces/trace.cpp +++ b/Software/PC_Application/Traces/trace.cpp @@ -14,9 +14,12 @@ Trace::Trace(QString name, QColor color, LiveParameter live) visible(true), paused(false), touchstone(false), - calibration(false) + calibration(false), + lastMath(nullptr) { - + MathInfo self = {.math = this, .enabled = true}; + math.push_back(self); + updateLastMath(math.rbegin()); } Trace::~Trace() @@ -28,21 +31,21 @@ void Trace::clear() { if(paused) { return; } - _data.clear(); + data.clear(); settings.valid = false; emit cleared(this); - emit dataChanged(); + emit outputDataChanged(); } void Trace::addData(const Trace::Data& d) { // add or replace data in vector while keeping it sorted with increasing frequency - auto lower = lower_bound(_data.begin(), _data.end(), d, [](const Data &lhs, const Data &rhs) -> bool { - return lhs.frequency < rhs.frequency; + auto lower = lower_bound(data.begin(), data.end(), d, [](const Data &lhs, const Data &rhs) -> bool { + return lhs.x < rhs.x; }); - if(lower == _data.end()) { + if(lower == data.end()) { // highest frequency yet, add to vector - _data.push_back(d); - } else if(lower->frequency == d.frequency) { + data.push_back(d); + } else if(lower->x == d.x) { switch(_liveType) { case LivedataType::Overwrite: // replace this data element @@ -50,13 +53,13 @@ void Trace::addData(const Trace::Data& d) { break; case LivedataType::MaxHold: // replace this data element - if(abs(d.S) > abs(lower->S)) { + if(abs(d.y) > abs(lower->y)) { *lower = d; } break; case LivedataType::MinHold: // replace this data element - if(abs(d.S) < abs(lower->S)) { + if(abs(d.y) < abs(lower->y)) { *lower = d; } break; @@ -64,11 +67,10 @@ void Trace::addData(const Trace::Data& d) { } else { // insert at this position - _data.insert(lower, d); + data.insert(lower, d); } - emit dataAdded(this, d); - emit dataChanged(); - if(lower == _data.begin()) { + emit outputSampleChanged(lower - data.begin()); + if(lower == data.begin()) { // received the first point, which means the last sweep just finished if(tdr_users) { updateTimeDomainData(); @@ -106,8 +108,8 @@ void Trace::fillFromTouchstone(Touchstone &t, unsigned int parameter, QString fi for(unsigned int i=0;irData()[1].x; steps--; } if(firstStep * steps != maxFreq()) { @@ -226,7 +228,34 @@ void Trace::updateTimeDomainData() // auto duration = duration_cast< milliseconds >( // system_clock::now().time_since_epoch() // ).count() - starttime; -// cout << "TDR: " << this << " (took " << duration << "ms)" <& Trace::getMath() const +{ + return math; +} + +void Trace::updateLastMath(vector::reverse_iterator start) +{ + TraceMath *newLast = nullptr; + for(auto it = start;it != math.rend();it++) { + if(it->enabled) { + newLast = it->math; + break; + } + } + Q_ASSERT(newLast != nullptr); + if(newLast != lastMath) { + if(lastMath != nullptr) { + disconnect(lastMath, &TraceMath::outputDataChanged, this, nullptr); + disconnect(lastMath, &TraceMath::outputSampleChanged, this, nullptr); + } + lastMath = newLast; + // relay signals of end of math chain + connect(lastMath, &TraceMath::outputDataChanged, this, &Trace::dataChanged); + connect(lastMath, &TraceMath::outputSampleChanged, this, &Trace::dataChanged); + } } void Trace::setReflection(bool value) @@ -305,6 +334,11 @@ Trace::TimedomainData Trace::getTDR(double position) } } +QString Trace::description() +{ + return name() + ": measured data"; +} + void Trace::setCalibration(bool value) { calibration = value; @@ -363,16 +397,55 @@ bool Trace::isReflection() return reflection; } +bool Trace::mathEnabled() +{ + return lastMath != this; +} + +bool Trace::hasMathOperations() +{ + return math.size() > 1; +} + +void Trace::enableMath(bool enable) +{ + auto start = enable ? math.rbegin() : make_reverse_iterator(math.begin()); + updateLastMath(start); +} + +unsigned int Trace::size() +{ + return lastMath->numSamples(); +} + +double Trace::minFreq() +{ + if(size() > 0) { + return data.front().x; + } else { + return 0.0; + } +} + +double Trace::maxFreq() +{ + if(size() > 0) { + return data.back().x; + } else { + return 0.0; + } +} + double Trace::findExtremumFreq(bool max) { double compare = max ? numeric_limits::min() : numeric_limits::max(); double freq = 0.0; - for(auto d : _data) { - double amplitude = abs(d.S); + for(auto sample : lastMath->rData()) { + double amplitude = abs(sample.y); if((max && (amplitude > compare)) || (!max && (amplitude < compare))) { // higher/lower extremum found compare = amplitude; - freq = d.frequency; + freq = sample.x; } } return freq; @@ -388,11 +461,11 @@ 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 : _data) { - double dbm = 20*log10(abs(d.S)); + for(auto d : lastMath->rData()) { + double dbm = 20*log10(abs(d.y)); if((dbm >= max_dbm) && (min_dbm <= dbm - minValley)) { // potential peak frequency - frequency = d.frequency; + frequency = d.x; max_dbm = dbm; } if(dbm <= min_dbm) { @@ -430,6 +503,11 @@ std::vector Trace::findPeakFrequencies(unsigned int maxPeaks, double min return frequencies; } +Trace::Data Trace::sample(unsigned int index) +{ + return lastMath->getSample(index); +} + QString Trace::getTouchstoneFilename() const { return touchstoneFilename; @@ -447,19 +525,19 @@ unsigned int Trace::getTouchstoneParameter() const std::complex Trace::getData(double frequency) { - if(_data.size() == 0 || frequency < minFreq() || frequency > maxFreq()) { + if(lastMath->numSamples() == 0 || frequency < minFreq() || frequency > maxFreq()) { return std::numeric_limits>::quiet_NaN(); } auto i = index(frequency); - if(_data.at(i).frequency == frequency) { - return _data[i].S; + if(lastMath->getSample(i).x == frequency) { + return lastMath->getSample(i).y; } else { // no exact frequency match, needs to interpolate - auto high = _data[i]; - auto low = _data[i-1]; - double alpha = (frequency - low.frequency) / (high.frequency - low.frequency); - return low.S * (1 - alpha) + high.S * alpha; + 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; } } @@ -478,10 +556,10 @@ double Trace::getNoise(double frequency) int Trace::index(double frequency) { - auto lower = lower_bound(_data.begin(), _data.end(), frequency, [](const Data &lhs, const double freq) -> bool { - return lhs.frequency < freq; + auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), frequency, [](const Data &lhs, const double freq) -> bool { + return lhs.x < freq; }); - return lower - _data.begin(); + return lower - lastMath->rData().begin(); } void Trace::setTouchstoneParameter(int value) diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h index 2584bf7..6f5b805 100644 --- a/Software/PC_Application/Traces/trace.h +++ b/Software/PC_Application/Traces/trace.h @@ -8,19 +8,16 @@ #include #include "touchstone.h" #include "Device/device.h" +#include "Math/tracemath.h" class TraceMarker; -class Trace : public QObject +class Trace : public TraceMath { Q_OBJECT public: - class Data { - public: - double frequency; - std::complex S; - }; + using Data = TraceMath::Data; class TimedomainData { public: @@ -67,11 +64,14 @@ public: bool isCalibration(); bool isLive(); bool isReflection(); + bool mathEnabled(); // check if math operations are enabled + bool hasMathOperations(); // check if math operations are set up (not necessarily enabled) + void enableMath(bool enable); LiveParameter liveParameter() { return _liveParam; } LivedataType liveType() { return _liveType; } - unsigned int size() { return _data.size(); } - double minFreq() { return size() > 0 ? _data.front().frequency : 0.0; }; - double maxFreq() { return size() > 0 ? _data.back().frequency : 0.0; }; + unsigned int size(); + double minFreq(); + double maxFreq(); 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. @@ -79,7 +79,7 @@ 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) { return _data.at(index); } + Data sample(unsigned int index); QString getTouchstoneFilename() const; unsigned int getTouchstoneParameter() const; std::complex getData(double frequency); @@ -103,6 +103,16 @@ public: // 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; + + class MathInfo { + public: + TraceMath *math; + bool enabled; + }; + const std::vector& getMath() const; + public slots: void setTouchstoneParameter(int value); void setTouchstoneFilename(const QString &value); @@ -115,7 +125,6 @@ private: signals: void cleared(Trace *t); void typeChanged(Trace *t); - void dataAdded(Trace *t, Data d); void deleted(Trace *t); void visibilityChanged(Trace *t); void dataChanged(); @@ -128,7 +137,7 @@ signals: private: void updateTimeDomainData(); void printTimeDomain(); - std::vector _data; +// std::vector _data; std::vector timeDomain; unsigned int tdr_users; QString _name; @@ -150,6 +159,10 @@ private: }; bool valid; } settings; + + std::vector math; + TraceMath *lastMath; + void updateLastMath(std::vector::reverse_iterator start); }; #endif // TRACE_H diff --git a/Software/PC_Application/Traces/traceeditdialog.cpp b/Software/PC_Application/Traces/traceeditdialog.cpp index 79c1c88..9adbe63 100644 --- a/Software/PC_Application/Traces/traceeditdialog.cpp +++ b/Software/PC_Application/Traces/traceeditdialog.cpp @@ -2,6 +2,7 @@ #include "ui_traceeditdialog.h" #include #include +#include "Math/tracematheditdialog.h" TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) : QDialog(parent), @@ -92,6 +93,10 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) : connect(ui->GSource, qOverload(&QButtonGroup::buttonClicked), updateFileStatus); connect(ui->touchstoneImport, &TouchstoneImport::statusChanged, updateFileStatus); connect(ui->touchstoneImport, &TouchstoneImport::filenameChanged, updateFileStatus); + connect(ui->mathSetup, &QPushButton::clicked, [&](){ + auto dialog = new TraceMathEditDialog(t); + dialog->show(); + }); updateFileStatus(); } diff --git a/Software/PC_Application/Traces/traceeditdialog.ui b/Software/PC_Application/Traces/traceeditdialog.ui index 2fb98df..ef15584 100644 --- a/Software/PC_Application/Traces/traceeditdialog.ui +++ b/Software/PC_Application/Traces/traceeditdialog.ui @@ -37,17 +37,28 @@ - - - - 0 - 0 - - - - - - + + + + + + 0 + 0 + + + + + + + + + + + Math + + + + diff --git a/Software/PC_Application/Traces/traceexportdialog.cpp b/Software/PC_Application/Traces/traceexportdialog.cpp index 5816d22..95b0ec8 100644 --- a/Software/PC_Application/Traces/traceexportdialog.cpp +++ b/Software/PC_Application/Traces/traceexportdialog.cpp @@ -72,9 +72,9 @@ void TraceExportDialog::on_buttonBox_accepted() } else { Trace *t = qvariant_cast(cTraces[i][j]->itemData(cTraces[i][j]->currentIndex())); // extract frequency (will overwrite for each trace but all traces have the same frequency points anyway) - tData.frequency = t->sample(s).frequency; + tData.frequency = t->sample(s).x; // add S parameter from trace to touchstone - tData.S.push_back(t->sample(s).S); + tData.S.push_back(t->sample(s).y); } } } diff --git a/Software/PC_Application/Traces/tracemarker.cpp b/Software/PC_Application/Traces/tracemarker.cpp index a4dd9d3..f632d5f 100644 --- a/Software/PC_Application/Traces/tracemarker.cpp +++ b/Software/PC_Application/Traces/tracemarker.cpp @@ -695,11 +695,11 @@ void TraceMarker::update() setPosition(peakFreq); // find the cutoff frequency auto index = parentTrace->index(peakFreq); - auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).S)); + auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).y)); auto cutoff = peakAmplitude + cutoffAmplitude; int inc = type == Type::Lowpass ? 1 : -1; while(index >= 0 && index < (int) parentTrace->size()) { - auto amplitude = 20*log10(abs(parentTrace->sample(index).S)); + auto amplitude = 20*log10(abs(parentTrace->sample(index).y)); if(amplitude <= cutoff) { break; } @@ -711,7 +711,7 @@ void TraceMarker::update() index = parentTrace->size() - 1; } // set position of cutoff marker - helperMarkers[0]->setPosition(parentTrace->sample(index).frequency); + helperMarkers[0]->setPosition(parentTrace->sample(index).x); } break; case Type::Bandpass: @@ -725,12 +725,12 @@ void TraceMarker::update() setPosition(peakFreq); // find the cutoff frequencies auto index = parentTrace->index(peakFreq); - auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).S)); + auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).y)); auto cutoff = peakAmplitude + cutoffAmplitude; auto low_index = index; while(low_index >= 0) { - auto amplitude = 20*log10(abs(parentTrace->sample(low_index).S)); + auto amplitude = 20*log10(abs(parentTrace->sample(low_index).y)); if(amplitude <= cutoff) { break; } @@ -740,11 +740,11 @@ void TraceMarker::update() low_index = 0; } // set position of cutoff marker - helperMarkers[0]->setPosition(parentTrace->sample(low_index).frequency); + helperMarkers[0]->setPosition(parentTrace->sample(low_index).x); auto high_index = index; while(high_index < (int) parentTrace->size()) { - auto amplitude = 20*log10(abs(parentTrace->sample(high_index).S)); + auto amplitude = 20*log10(abs(parentTrace->sample(high_index).y)); if(amplitude <= cutoff) { break; } @@ -754,7 +754,7 @@ void TraceMarker::update() high_index = parentTrace->size() - 1; } // set position of cutoff marker - helperMarkers[1]->setPosition(parentTrace->sample(high_index).frequency); + helperMarkers[1]->setPosition(parentTrace->sample(high_index).x); // set center marker inbetween cutoff markers helperMarkers[2]->setPosition((helperMarkers[0]->position + helperMarkers[1]->position) / 2); } diff --git a/Software/PC_Application/Traces/tracemodel.cpp b/Software/PC_Application/Traces/tracemodel.cpp index e90eab7..f68b7ff 100644 --- a/Software/PC_Application/Traces/tracemodel.cpp +++ b/Software/PC_Application/Traces/tracemodel.cpp @@ -52,7 +52,7 @@ void TraceModel::toggleVisibility(unsigned int index) { if (index < traces.size()) { traces[index]->setVisible(!traces[index]->isVisible()); - emit dataChanged(createIndex(index, 0), createIndex(index, 0)); + emit dataChanged(createIndex(index, ColIndexVisible), createIndex(index, ColIndexVisible)); } } @@ -64,11 +64,23 @@ void TraceModel::togglePause(unsigned int index) } else { traces[index]->pause(); } - emit dataChanged(createIndex(index, 1), createIndex(index, 1)); + emit dataChanged(createIndex(index, ColIndexPlayPause), createIndex(index, ColIndexPlayPause)); emit requiredExcitation(PortExcitationRequired(1), PortExcitationRequired(2)); } } +void TraceModel::toggleMath(unsigned int index) +{ + if (index >= traces.size()) { + return; + } + auto trace = traces[index]; + if(trace->hasMathOperations()) { + trace->enableMath(!trace->mathEnabled()); + emit dataChanged(createIndex(index, ColIndexMath), createIndex(index, ColIndexMath)); + } +} + int TraceModel::rowCount(const QModelIndex &) const { return traces.size(); @@ -76,7 +88,7 @@ int TraceModel::rowCount(const QModelIndex &) const int TraceModel::columnCount(const QModelIndex &) const { - return 3; + return ColIndexLast; } QVariant TraceModel::data(const QModelIndex &index, int role) const @@ -86,37 +98,47 @@ QVariant TraceModel::data(const QModelIndex &index, int role) const if ((unsigned int) index.row() >= traces.size()) return QVariant(); - if (index.column() == 0) { + + auto trace = traces[index.row()]; + switch(index.column()) { + case ColIndexVisible: if (role == Qt::DecorationRole) { - if (traces[index.row()]->isVisible()) { + if (trace->isVisible()) { return QIcon(":/icons/visible.svg"); } else { return QIcon(":/icons/invisible.svg"); } - } else { - return QVariant(); } - } else if (index.column() == 1) { - if (role == Qt::DecorationRole && !traces[index.row()]->isTouchstone()) { - if (traces[index.row()]->isPaused()) { + break; + case ColIndexPlayPause: + if (role == Qt::DecorationRole && !trace->isTouchstone()) { + if (trace->isPaused()) { return QIcon(":/icons/pause.svg"); } else { return QIcon(":/icons/play.svg"); } - } else { - return QVariant(); } - } else if (index.column() == 2) { + break; + case ColIndexMath: + if (role == Qt::DecorationRole && trace->hasMathOperations()) { + if(trace->mathEnabled()) { + return QIcon(":icons/math_enabled"); + } else { + return QIcon(":icons/math_disabled"); + } + } + break; + case ColIndexName: if (role == Qt::DisplayRole) { - return traces[index.row()]->name(); + return trace->name(); } else if (role == Qt::ForegroundRole) { - return traces[index.row()]->color(); - } else { - return QVariant(); + return trace->color(); } - } else { - return QVariant(); + break; + default: + break; } + return QVariant(); } std::vector TraceModel::getTraces() @@ -156,12 +178,12 @@ void TraceModel::addVNAData(const Protocol::Datapoint &d, const Protocol::SweepS for(auto t : traces) { if (t->isLive() && !t->isPaused()) { Trace::Data td; - td.frequency = d.frequency; + td.x = d.frequency; switch(t->liveParameter()) { - case Trace::LiveParameter::S11: td.S = complex(d.real_S11, d.imag_S11); break; - case Trace::LiveParameter::S12: td.S = complex(d.real_S12, d.imag_S12); break; - case Trace::LiveParameter::S21: td.S = complex(d.real_S21, d.imag_S21); break; - case Trace::LiveParameter::S22: td.S = complex(d.real_S22, d.imag_S22); break; + case Trace::LiveParameter::S11: td.y = complex(d.real_S11, d.imag_S11); break; + case Trace::LiveParameter::S12: td.y = complex(d.real_S12, d.imag_S12); break; + case Trace::LiveParameter::S21: td.y = complex(d.real_S21, d.imag_S21); break; + case Trace::LiveParameter::S22: td.y = complex(d.real_S22, d.imag_S22); break; default: // not a VNA trace, skip continue; @@ -176,10 +198,10 @@ void TraceModel::addSAData(const Protocol::SpectrumAnalyzerResult& d, const Prot for(auto t : traces) { if (t->isLive() && !t->isPaused()) { Trace::Data td; - td.frequency = d.frequency; + td.x = d.frequency; switch(t->liveParameter()) { - case Trace::LiveParameter::Port1: td.S = complex(d.port1, 0); break; - case Trace::LiveParameter::Port2: td.S = complex(d.port2, 0); break; + case Trace::LiveParameter::Port1: td.y = complex(d.port1, 0); break; + case Trace::LiveParameter::Port2: td.y = complex(d.port2, 0); break; default: // not a SA trace, skip continue; diff --git a/Software/PC_Application/Traces/tracemodel.h b/Software/PC_Application/Traces/tracemodel.h index c6ea214..4f90fc1 100644 --- a/Software/PC_Application/Traces/tracemodel.h +++ b/Software/PC_Application/Traces/tracemodel.h @@ -13,11 +13,20 @@ public: TraceModel(QObject *parent = 0); ~TraceModel(); + enum { + ColIndexVisible = 0, + ColIndexPlayPause = 1, + ColIndexMath = 2, + ColIndexName = 3, + ColIndexLast, + }; + void addTrace(Trace *t); void removeTrace(unsigned int index); Trace *trace(unsigned int index); void toggleVisibility(unsigned int index); void togglePause(unsigned int index); + void toggleMath(unsigned int index); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp index 09ab030..993c04a 100644 --- a/Software/PC_Application/Traces/tracesmithchart.cpp +++ b/Software/PC_Application/Traces/tracesmithchart.cpp @@ -41,10 +41,10 @@ void TraceSmithChart::axisSetupDialog() QPoint TraceSmithChart::dataToPixel(Trace::Data d) { - if(d.frequency < sweep_fmin || d.frequency > sweep_fmax) { + if(d.x < sweep_fmin || d.x > sweep_fmax) { return QPoint(); } - return transform.map(QPoint(d.S.real() * smithCoordMax, -d.S.imag() * smithCoordMax)); + return transform.map(QPoint(d.y.real() * smithCoordMax, -d.y.imag() * smithCoordMax)); } std::complex TraceSmithChart::pixelToData(QPoint p) @@ -83,7 +83,7 @@ double TraceSmithChart::nearestTracePoint(Trace *t, QPoint pixel) closestIndex = i; } } - return t->sample(closestIndex).frequency; + return t->sample(closestIndex).x; } void TraceSmithChart::draw(QPainter &p) { @@ -144,17 +144,17 @@ void TraceSmithChart::draw(QPainter &p) { for(int i=1;isample(i-1); auto now = trace->sample(i); - if (limitToSpan && (last.frequency < sweep_fmin || now.frequency > sweep_fmax)) { + if (limitToSpan && (last.x < sweep_fmin || now.x > sweep_fmax)) { continue; } - if(isnan(now.S.real())) { + if(isnan(now.y.real())) { break; } // scale to size of smith diagram - last.S *= smithCoordMax; - now.S *= smithCoordMax; + last.y *= smithCoordMax; + now.y *= smithCoordMax; // draw line - p.drawLine(std::real(last.S), -std::imag(last.S), std::real(now.S), -std::imag(now.S)); + p.drawLine(std::real(last.y), -std::imag(last.y), std::real(now.y), -std::imag(now.y)); } if(trace->size() > 0) { // only draw markers if the trace has at least one point diff --git a/Software/PC_Application/Traces/tracewidget.cpp b/Software/PC_Application/Traces/tracewidget.cpp index 55e3bb2..0ef1212 100644 --- a/Software/PC_Application/Traces/tracewidget.cpp +++ b/Software/PC_Application/Traces/tracewidget.cpp @@ -109,7 +109,7 @@ void TraceWidget::on_edit_clicked() void TraceWidget::on_view_doubleClicked(const QModelIndex &index) { - if(index.column() == 2) { + if(index.column() == TraceModel::ColIndexName) { auto edit = new TraceEditDialog(*model.trace(index.row())); edit->show(); } @@ -117,9 +117,17 @@ void TraceWidget::on_view_doubleClicked(const QModelIndex &index) void TraceWidget::on_view_clicked(const QModelIndex &index) { - if(index.column()==0) { + switch(index.column()) { + case TraceModel::ColIndexVisible: model.toggleVisibility(index.row()); - } else if(index.column()==1) { + break; + case TraceModel::ColIndexPlayPause: model.togglePause(index.row()); + break; + case TraceModel::ColIndexMath: + model.toggleMath(index.row()); + break; + default: + break; } } diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp index 319e4c3..87edd14 100644 --- a/Software/PC_Application/Traces/tracexyplot.cpp +++ b/Software/PC_Application/Traces/tracexyplot.cpp @@ -654,8 +654,8 @@ QPointF TraceXYPlot::transformY(Trace *t, unsigned int sample, TraceXYPlot::YAxi case YAxisType::Phase: case YAxisType::VSWR: { auto d = t->sample(sample); - ret.setY(transformY(d.S, type)); - ret.setX(d.frequency); + ret.setY(transformY(d.y, type)); + ret.setX(d.x); } break; case YAxisType::Impulse: @@ -702,12 +702,12 @@ unsigned int TraceXYPlot::numTraceSamples(Trace *t) QPoint TraceXYPlot::dataToPixel(Trace::Data d) { - if(d.frequency < XAxis.rangeMin || d.frequency > XAxis.rangeMax) { + if(d.x < XAxis.rangeMin || d.x > XAxis.rangeMax) { return QPoint(); } - auto y = transformY(d.S, YAxis[0].type); + auto y = transformY(d.y, YAxis[0].type); QPoint p; - p.setX(Util::Scale(d.frequency, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); + 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; } @@ -842,5 +842,6 @@ QString TraceXYPlot::AxisUnit(TraceXYPlot::XAxisType type) case XAxisType::Frequency: return "Hz"; break; case XAxisType::Time: return "s"; break; case XAxisType::Distance: return "m"; break; + default: return ""; break; } } diff --git a/Software/PC_Application/icons.qrc b/Software/PC_Application/icons.qrc index de0911a..e6064ca 100644 --- a/Software/PC_Application/icons.qrc +++ b/Software/PC_Application/icons.qrc @@ -33,5 +33,7 @@ icons/zoom-fit.png icons/zoom-in.png icons/zoom-out.png + icons/math_disabled.svg + icons/math_enabled.svg diff --git a/Software/PC_Application/icons/math_disabled.svg b/Software/PC_Application/icons/math_disabled.svg new file mode 100644 index 0000000..d23978e --- /dev/null +++ b/Software/PC_Application/icons/math_disabled.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + f(x) + + + diff --git a/Software/PC_Application/icons/math_enabled.svg b/Software/PC_Application/icons/math_enabled.svg new file mode 100644 index 0000000..e00ef46 --- /dev/null +++ b/Software/PC_Application/icons/math_enabled.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + f(x) + +