diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index 90b6b5c..a67569d 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -127,6 +127,7 @@ FORMS += \ Device/manualcontroldialog.ui \ Generator/signalgenwidget.ui \ Tools/impedancematchdialog.ui \ + Traces/Math/medianfilterdialog.ui \ Traces/Math/tracematheditdialog.ui \ Traces/markerwidget.ui \ Traces/smithchartdialog.ui \ diff --git a/Software/PC_Application/Calibration/calibration.cpp b/Software/PC_Application/Calibration/calibration.cpp index 28006b6..3b27a6f 100644 --- a/Software/PC_Application/Calibration/calibration.cpp +++ b/Software/PC_Application/Calibration/calibration.cpp @@ -651,15 +651,15 @@ std::vector Calibration::getMeasurementTraces() t->setReflection(prefix == "S11" || prefix == "S22"); for(auto p : m.second.datapoints) { Trace::Data d; - d.frequency = p.frequency; + d.x = p.frequency; if(prefix == "S11") { - d.S = complex(p.real_S11, p.imag_S11); + d.y = complex(p.real_S11, p.imag_S11); } else if(prefix == "S12") { - d.S = complex(p.real_S12, p.imag_S12); + d.y = complex(p.real_S12, p.imag_S12); } else if(prefix == "S21") { - d.S = complex(p.real_S21, p.imag_S21); + d.y = complex(p.real_S21, p.imag_S21); } else { - d.S = complex(p.real_S22, p.imag_S22); + d.y = complex(p.real_S22, p.imag_S22); } t->addData(d); } diff --git a/Software/PC_Application/Traces/Math/medianfilter.cpp b/Software/PC_Application/Traces/Math/medianfilter.cpp index c91937f..759ac4e 100644 --- a/Software/PC_Application/Traces/Math/medianfilter.cpp +++ b/Software/PC_Application/Traces/Math/medianfilter.cpp @@ -1,8 +1,15 @@ #include "medianfilter.h" +#include "ui_medianfilterdialog.h" + +#include using namespace Math; using namespace std; +namespace Ui { +class MedianFilterDialog; +} + MedianFilter::MedianFilter() { kernelSize = 3; @@ -17,49 +24,112 @@ TraceMath::DataType MedianFilter::outputType(TraceMath::DataType inputType) QString MedianFilter::description() { - return "Median filter"; + return "Median filter, size "+QString::number(kernelSize)+", sorting: " + orderToString(order); +} + +void MedianFilter::edit() +{ + auto d = new QDialog(); + auto ui = new Ui::MedianFilterDialog(); + ui->setupUi(d); + ui->kernelSize->setValue(kernelSize); + ui->sortingMethod->setCurrentIndex((int) order); + + connect(ui->kernelSize, qOverload(&QSpinBox::valueChanged), [=](int newval) { + if((newval & 0x01) == 0) { + QMessageBox::information(d, "Median filter", "Only odd values are allowed for the kernel size"); + newval++; + } + ui->kernelSize->setValue(newval); + kernelSize = newval; + }); + + connect(ui->sortingMethod, qOverload(&QComboBox::currentIndexChanged), [=](int index) { + order = (Order) index; + }); + d->show(); } void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) { if(data.size() != input->rData().size()) { data.resize(input->rData().size()); } - int start = (int) begin - (kernelSize-1)/2; - unsigned int stop = (int) end + (kernelSize-1)/2; - if(start < 0) { - start = 0; + if(data.size() > 0) { + auto kernelOffset = (kernelSize-1)/2; + int start = (int) begin - kernelOffset; + unsigned int stop = (int) end + kernelOffset; + if(start < 0) { + start = 0; + } + if(stop >= input->rData().size()) { + stop = input->rData().size(); + } + + auto comp = [=](const complex&a, const complex&b){ + switch(order) { + case Order::AbsoluteValue: return abs(a) < abs(b); + case Order::Phase: return arg(a) < arg(b); + case Order::Real: return real(a) < real(b); + case Order::Imag: return imag(a) < imag(b); + } + }; + + vector> kernel(kernelSize); + for(unsigned int out=start;out in + out) { + inputSample = 0; + } else if(in + kernelOffset + out >= input->rData().size()) { + inputSample = input->rData().size() - 1; + } else { + inputSample = in + out - kernelOffset; + } + auto sample = input->rData().at(inputSample).y; + if(out == (unsigned int) start) { + // this is the first sample to update, fill initial kernel + kernel[in] = sample; + } + } + // sort initial kernel + sort(kernel.begin(), kernel.end(), comp); + } else { + // kernel already filled and sorted from last output sample. Only remove the one input sample that + // is no longer needed for this output and add the one additional input sample + int toRemove = out - kernelOffset - 1; + unsigned int toAdd = out + kernelOffset; + if(toRemove < 0) { + toRemove = 0; + } + if(toAdd >= input->rData().size()) { + toAdd = input->rData().size() - 1; + } + auto sampleToRemove = input->rData().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; + // 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; + } + emit outputSamplesChanged(start, stop); + success(); + } else { + warning("No input data"); } - if(stop >= input->rData().size()) { - stop = input->rData().size(); - } - for(unsigned int i=start;i> values; - for(int i=index - (kernelSize-1)/2;i<=index+(kernelSize-1)/2;i++) { - unsigned int inputSample; - if(i<0) { - inputSample = 0; - } else if(i>=(int) input->rData().size()) { - inputSample = input->rData().size() - 1; - } else { - inputSample = i; - } - values.push_back(input->rData().at(inputSample).y); + switch(o) { + case Order::AbsoluteValue: return "Absolute"; + case Order::Phase: return "Phase"; + case Order::Real: return "Real"; + case Order::Imag: return "Imag"; } - sort(values.begin(), values.end(), [=](const complex&a, const complex&b){ - switch(order) { - case Order::AbsoluteValue: return abs(a) > abs(b); - case Order::Phase: return arg(a) > arg(b); - case Order::Real: return real(a) > real(b); - case Order::Imag: return imag(a) > imag(b); - } - }); - data.at(index).y = values[(kernelSize-1)/2]; - data.at(index).x = input->rData().at(index).x; } diff --git a/Software/PC_Application/Traces/Math/medianfilter.h b/Software/PC_Application/Traces/Math/medianfilter.h index fa70e1f..f28a457 100644 --- a/Software/PC_Application/Traces/Math/medianfilter.h +++ b/Software/PC_Application/Traces/Math/medianfilter.h @@ -13,19 +13,21 @@ public: virtual DataType outputType(DataType inputType) override; virtual QString description() override; + virtual void edit() override; + public slots: // a single value of the input data has changed, index determines which sample has changed virtual void inputSamplesChanged(unsigned int begin, unsigned int end) override; private: - void updateSample(int index); - int kernelSize; + unsigned int kernelSize; enum class Order { - AbsoluteValue, - Phase, - Real, - Imag, + AbsoluteValue = 0, + Phase = 1, + Real = 2, + Imag = 3, } order; + static QString orderToString(Order o); }; } diff --git a/Software/PC_Application/Traces/Math/medianfilterdialog.ui b/Software/PC_Application/Traces/Math/medianfilterdialog.ui index 55b184f..a7fa5ec 100644 --- a/Software/PC_Application/Traces/Math/medianfilterdialog.ui +++ b/Software/PC_Application/Traces/Math/medianfilterdialog.ui @@ -1,19 +1,106 @@ - - Dialog - - + + + MedianFilterDialog + + + Qt::ApplicationModal + + 0 0 - 400 - 300 + 269 + 108 - - Dialog + + Median Filter + + false + + + + + + + + Kernel size: + + + + + + + Number of input samples that the filter uses for each output sample + + + 3 + + + 2 + + + + + + + Sorting method: + + + + + + + + Absolute value + + + + + Phase + + + + + Real part + + + + + Imaginary part + + + + + + + + + + QDialogButtonBox::Ok + + + + - + + + buttonBox + accepted() + MedianFilterDialog + accept() + + + 134 + 86 + + + 134 + 53 + + + + - diff --git a/Software/PC_Application/Traces/Math/tracemath.cpp b/Software/PC_Application/Traces/Math/tracemath.cpp index 743de67..54e2261 100644 --- a/Software/PC_Application/Traces/Math/tracemath.cpp +++ b/Software/PC_Application/Traces/Math/tracemath.cpp @@ -4,6 +4,7 @@ TraceMath::TraceMath() { input = nullptr; dataType = DataType::Invalid; + error("Invalid input"); } TraceMath::Data TraceMath::getSample(unsigned int index) @@ -51,9 +52,44 @@ void TraceMath::inputTypeChanged(TraceMath::DataType type) data.clear(); inputSamplesChanged(0, input->data.size()); emit outputTypeChanged(dataType); + if(dataType == DataType::Invalid) { + error("Invalid input data"); + } } } +void TraceMath::warning(QString warn) +{ + statusString = warn; + status = Status::Warning; + emit statusChanged(); +} + +void TraceMath::error(QString err) +{ + statusString = err; + status = Status::Error; + emit statusChanged(); +} + +void TraceMath::success() +{ + if(status != Status::Ok) { + status = Status::Ok; + emit statusChanged(); + } +} + +QString TraceMath::getStatusDescription() const +{ + return statusString; +} + +TraceMath::Status TraceMath::getStatus() const +{ + return status; +} + 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 index dfc20c3..2e794e1 100644 --- a/Software/PC_Application/Traces/Math/tracemath.h +++ b/Software/PC_Application/Traces/Math/tracemath.h @@ -1,4 +1,4 @@ -#ifndef TRACEMATH_H +#ifndef TRACEMATH_H #define TRACEMATH_H #include @@ -22,6 +22,12 @@ public: Invalid, }; + enum class Status { + Ok, + Warning, + Error, + }; + Data getSample(unsigned int index); unsigned int numSamples(); @@ -35,6 +41,8 @@ public: DataType getDataType() const; std::vector& rData() { return data;}; + Status getStatus() const; + QString getStatusDescription() const; public slots: // some values of the input data have changed, begin/end determine which sample(s) has changed @@ -49,9 +57,19 @@ signals: void outputTypeChanged(DataType type); protected: + // call one of these functions in the derived classes after output data has been updated + void warning(QString warn); + void error(QString err); + void success(); std::vector data; TraceMath *input; DataType dataType; + +private: + Status status; + QString statusString; +signals: + void statusChanged(); }; #endif // TRACEMATH_H diff --git a/Software/PC_Application/Traces/Math/tracematheditdialog.cpp b/Software/PC_Application/Traces/Math/tracematheditdialog.cpp index 3d93335..fd4a012 100644 --- a/Software/PC_Application/Traces/Math/tracematheditdialog.cpp +++ b/Software/PC_Application/Traces/Math/tracematheditdialog.cpp @@ -1,5 +1,6 @@ #include "tracematheditdialog.h" #include "ui_tracematheditdialog.h" +#include #include "medianfilter.h" @@ -11,6 +12,11 @@ TraceMathEditDialog::TraceMathEditDialog(Trace &t, QWidget *parent) : ui->setupUi(this); ui->view->setModel(model); + QHeaderView *headerView = ui->view->horizontalHeader(); + headerView->setSectionResizeMode(MathModel::ColIndexDescription, QHeaderView::Stretch); + headerView->setSectionResizeMode(MathModel::ColIndexStatus, QHeaderView::ResizeToContents); + headerView->setSectionResizeMode(MathModel::ColIndexDomain, QHeaderView::ResizeToContents); + connect(ui->view->selectionModel(), &QItemSelectionModel::currentRowChanged, [=](const QModelIndex ¤t, const QModelIndex &previous){ if(!current.isValid()) { ui->bDelete->setEnabled(false); @@ -23,6 +29,13 @@ TraceMathEditDialog::TraceMathEditDialog(Trace &t, QWidget *parent) : } }); + connect(ui->view, &QTableView::doubleClicked, [&](const QModelIndex &index) { + if(index.isValid()) { + auto math = t.getMathOperations().at(index.row()).math; + math->edit(); + } + }); + connect(ui->bAdd, &QPushButton::clicked, [=](){ model->addOperation(new Math::MedianFilter); }); @@ -72,18 +85,41 @@ QVariant MathModel::data(const QModelIndex &index, int role) const } auto math = t.getMathOperations().at(index.row()); switch(index.column()) { -// case ColIndexEnabled: -// if(role == Qt::CheckStateRole) { -// return math.enabled ? Qt::Checked : Qt::Unchecked; -// } -// break; + case ColIndexStatus: + if(role == Qt::DecorationRole) { + switch(math.math->getStatus()) { + case TraceMath::Status::Ok: + return QApplication::style()->standardIcon(QStyle::SP_DialogApplyButton); + case TraceMath::Status::Warning: + return QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + case TraceMath::Status::Error: + return QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); + } + } else if(role == Qt::ToolTipRole) { + if(math.math->getStatus() != TraceMath::Status::Ok) { + return math.math->getStatusDescription(); + } + } + break; case ColIndexDescription: if(role == Qt::DisplayRole) { return math.math->description(); - } else if(role == Qt::CheckStateRole){ - return math.enabled ? Qt::Checked : Qt::Unchecked; } +// else if(role == Qt::CheckStateRole){ +// return math.enabled ? Qt::Checked : Qt::Unchecked; +// } break; + case ColIndexDomain: + if(role == Qt::DisplayRole) { + switch(math.math->getDataType()) { + case TraceMath::DataType::Time: + return "Time"; + case TraceMath::DataType::Frequency: + return "Frequency"; + case TraceMath::DataType::Invalid: + return "Invalid"; + } + } } return QVariant(); } @@ -92,8 +128,9 @@ QVariant MathModel::headerData(int section, Qt::Orientation orientation, int rol { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch(section) { -// case ColIndexEnabled: return "Enabled"; break; + case ColIndexStatus: return "Status"; break; case ColIndexDescription: return "Description"; break; + case ColIndexDomain: return "Output domain"; break; default: break; } } @@ -107,11 +144,11 @@ Qt::ItemFlags MathModel::flags(const QModelIndex &index) const // the first entry is always the trace itself and not enabled flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable; } - switch(index.column()) { - case ColIndexDescription: flags |= Qt::ItemIsUserCheckable; break; - default: - break; - } +// switch(index.column()) { +// case ColIndexDescription: flags |= Qt::ItemIsUserCheckable; break; +// default: +// break; +// } return (Qt::ItemFlags) flags; } @@ -120,6 +157,8 @@ void MathModel::addOperation(TraceMath *math) beginInsertRows(QModelIndex(), t.getMathOperations().size(), t.getMathOperations().size()); t.addMathOperation(math); endInsertRows(); + // open the editor for the newly added operation + math->edit(); } void MathModel::deleteRow(unsigned int row) diff --git a/Software/PC_Application/Traces/Math/tracematheditdialog.h b/Software/PC_Application/Traces/Math/tracematheditdialog.h index 3423093..f8bcc37 100644 --- a/Software/PC_Application/Traces/Math/tracematheditdialog.h +++ b/Software/PC_Application/Traces/Math/tracematheditdialog.h @@ -16,8 +16,9 @@ public: MathModel(Trace &t, QObject *parent = 0); enum { -// ColIndexEnabled = 0, - ColIndexDescription = 0, + ColIndexStatus = 0, + ColIndexDescription = 1, + ColIndexDomain = 2, ColIndexLast, }; diff --git a/Software/PC_Application/Traces/Math/tracematheditdialog.ui b/Software/PC_Application/Traces/Math/tracematheditdialog.ui index f0805cb..10bd270 100644 --- a/Software/PC_Application/Traces/Math/tracematheditdialog.ui +++ b/Software/PC_Application/Traces/Math/tracematheditdialog.ui @@ -2,19 +2,22 @@ TraceMathEditDialog + + Qt::ApplicationModal + 0 0 - 411 - 302 + 903 + 350 Math functions - true + false @@ -46,7 +49,7 @@ 70 - true + false false @@ -110,7 +113,8 @@ - + + .. @@ -126,7 +130,8 @@ - + + .. diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp index 933f679..22b0157 100644 --- a/Software/PC_Application/Traces/trace.cpp +++ b/Software/PC_Application/Traces/trace.cpp @@ -22,6 +22,7 @@ Trace::Trace(QString name, QColor color, LiveParameter live) updateLastMath(mathOps.rbegin()); self.enabled = false; + dataType = DataType::Frequency; } Trace::~Trace() @@ -35,6 +36,7 @@ void Trace::clear() { } data.clear(); settings.valid = false; + warning("No data"); emit cleared(this); emit outputSamplesChanged(0, 0); } @@ -71,6 +73,7 @@ void Trace::addData(const Trace::Data& d) { // insert at this position data.insert(lower, 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 diff --git a/Software/PC_Application/Traces/traceeditdialog.ui b/Software/PC_Application/Traces/traceeditdialog.ui index ef15584..4842052 100644 --- a/Software/PC_Application/Traces/traceeditdialog.ui +++ b/Software/PC_Application/Traces/traceeditdialog.ui @@ -16,6 +16,9 @@ Edit Trace + + false +