minimal median filter implementation for math testing

This commit is contained in:
Jan Käberich 2020-11-27 16:31:05 +01:00
parent 692bb85b5d
commit a168e81cca
9 changed files with 131 additions and 27 deletions

View File

@ -25,6 +25,7 @@ HEADERS += \
SpectrumAnalyzer/tracewidgetsa.h \ SpectrumAnalyzer/tracewidgetsa.h \
Tools/eseries.h \ Tools/eseries.h \
Tools/impedancematchdialog.h \ Tools/impedancematchdialog.h \
Traces/Math/medianfilter.h \
Traces/Math/tracemath.h \ Traces/Math/tracemath.h \
Traces/Math/tracematheditdialog.h \ Traces/Math/tracematheditdialog.h \
Traces/fftcomplex.h \ Traces/fftcomplex.h \
@ -79,6 +80,7 @@ SOURCES += \
SpectrumAnalyzer/tracewidgetsa.cpp \ SpectrumAnalyzer/tracewidgetsa.cpp \
Tools/eseries.cpp \ Tools/eseries.cpp \
Tools/impedancematchdialog.cpp \ Tools/impedancematchdialog.cpp \
Traces/Math/medianfilter.cpp \
Traces/Math/tracemath.cpp \ Traces/Math/tracemath.cpp \
Traces/Math/tracematheditdialog.cpp \ Traces/Math/tracematheditdialog.cpp \
Traces/fftcomplex.cpp \ Traces/fftcomplex.cpp \

View File

@ -0,0 +1,65 @@
#include "medianfilter.h"
using namespace Math;
using namespace std;
MedianFilter::MedianFilter()
{
kernelSize = 3;
order = Order::AbsoluteValue;
}
TraceMath::DataType MedianFilter::outputType(TraceMath::DataType inputType)
{
// domain stays the same
return inputType;
}
QString MedianFilter::description()
{
return "Median filter";
}
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(stop >= input->rData().size()) {
stop = input->rData().size();
}
for(unsigned int i=start;i<stop;i++) {
updateSample(i);
}
emit outputSamplesChanged(start, stop);
}
void MedianFilter::updateSample(int index)
{
vector<complex<double>> 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);
}
sort(values.begin(), values.end(), [=](const complex<double>&a, const complex<double>&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;
}

View File

@ -0,0 +1,33 @@
#ifndef MEDIANFILTER_H
#define MEDIANFILTER_H
#include "tracemath.h"
namespace Math {
class MedianFilter : public TraceMath
{
public:
MedianFilter();
virtual DataType outputType(DataType inputType) override;
virtual QString description() 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;
enum class Order {
AbsoluteValue,
Phase,
Real,
Imag,
} order;
};
}
#endif // MEDIANFILTER_H

View File

@ -36,10 +36,9 @@ void TraceMath::assignInput(TraceMath *input)
this->input = input; this->input = input;
inputTypeChanged(input->dataType); inputTypeChanged(input->dataType);
// do initial calculation // do initial calculation
inputDataChanged(); inputSamplesChanged(0, input->data.size());
// connect to input // connect to input
connect(input, &TraceMath::outputDataChanged, this, &TraceMath::inputDataChanged); connect(input, &TraceMath::outputSamplesChanged, this, &TraceMath::inputSamplesChanged);
connect(input, &TraceMath::outputSampleChanged, this, &TraceMath::inputSampleChanged);
connect(input, &TraceMath::outputTypeChanged, this, &TraceMath::inputTypeChanged); connect(input, &TraceMath::outputTypeChanged, this, &TraceMath::inputTypeChanged);
} }
} }
@ -50,7 +49,7 @@ void TraceMath::inputTypeChanged(TraceMath::DataType type)
if(newType != dataType) { if(newType != dataType) {
dataType = newType; dataType = newType;
data.clear(); data.clear();
inputDataChanged(); inputSamplesChanged(0, input->data.size());
emit outputTypeChanged(dataType); emit outputTypeChanged(dataType);
} }
} }

View File

@ -27,8 +27,8 @@ public:
// indicate whether this function produces time or frequency domain data // indicate whether this function produces time or frequency domain data
virtual DataType outputType(DataType inputType) = 0; virtual DataType outputType(DataType inputType) = 0;
virtual QString description() = 0; virtual QString description() = 0;
virtual void edit(){};
void removeInput(); void removeInput();
void assignInput(TraceMath *input); void assignInput(TraceMath *input);
@ -37,17 +37,14 @@ public:
std::vector<Data>& rData() { return data;}; std::vector<Data>& rData() { return data;};
public slots: public slots:
// a single value of the input data has changed, index determines which sample has changed // some values of the input data have changed, begin/end determine which sample(s) has changed
virtual void inputSampleChanged(unsigned int index){Q_UNUSED(index)}; virtual void inputSamplesChanged(unsigned int begin, unsigned int end){Q_UNUSED(begin) Q_UNUSED(end)};
// the complete input data has changed (e.g. cleared or all data modified by some operation)
virtual void inputDataChanged(){};
void inputTypeChanged(DataType type); void inputTypeChanged(DataType type);
signals: signals:
// emit this whenever a sample changed (alternatively, if all samples are about to change, emit outputDataChanged after they have changed) // 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 outputSamplesChanged(unsigned int begin, unsigned int end);
void outputDataChanged();
// emit when the output type changed // emit when the output type changed
void outputTypeChanged(DataType type); void outputTypeChanged(DataType type);

View File

@ -1,6 +1,8 @@
#include "tracematheditdialog.h" #include "tracematheditdialog.h"
#include "ui_tracematheditdialog.h" #include "ui_tracematheditdialog.h"
#include "medianfilter.h"
TraceMathEditDialog::TraceMathEditDialog(Trace &t, QWidget *parent) : TraceMathEditDialog::TraceMathEditDialog(Trace &t, QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::TraceMathEditDialog) ui(new Ui::TraceMathEditDialog)
@ -21,6 +23,9 @@ TraceMathEditDialog::TraceMathEditDialog(Trace &t, QWidget *parent) :
} }
}); });
connect(ui->bAdd, &QPushButton::clicked, [=](){
model->addOperation(new Math::MedianFilter);
});
connect(ui->bDelete, &QPushButton::clicked, [=](){ connect(ui->bDelete, &QPushButton::clicked, [=](){
model->deleteRow(ui->view->currentIndex().row()); model->deleteRow(ui->view->currentIndex().row());
}); });
@ -110,6 +115,13 @@ Qt::ItemFlags MathModel::flags(const QModelIndex &index) const
return (Qt::ItemFlags) flags; return (Qt::ItemFlags) flags;
} }
void MathModel::addOperation(TraceMath *math)
{
beginInsertRows(QModelIndex(), t.getMathOperations().size(), t.getMathOperations().size());
t.addMathOperation(math);
endInsertRows();
}
void MathModel::deleteRow(unsigned int row) void MathModel::deleteRow(unsigned int row)
{ {
beginRemoveRows(QModelIndex(), row, row); beginRemoveRows(QModelIndex(), row, row);

View File

@ -27,6 +27,7 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;
void addOperation(TraceMath *math);
void deleteRow(unsigned int row); void deleteRow(unsigned int row);
void rowsSwapped(unsigned int top); void rowsSwapped(unsigned int top);

View File

@ -22,9 +22,6 @@ Trace::Trace(QString name, QColor color, LiveParameter live)
updateLastMath(mathOps.rbegin()); updateLastMath(mathOps.rbegin());
self.enabled = false; self.enabled = false;
mathOps.push_back(self);
mathOps.push_back(self);
mathOps.push_back(self);
} }
Trace::~Trace() Trace::~Trace()
@ -39,7 +36,7 @@ void Trace::clear() {
data.clear(); data.clear();
settings.valid = false; settings.valid = false;
emit cleared(this); emit cleared(this);
emit outputDataChanged(); emit outputSamplesChanged(0, 0);
} }
void Trace::addData(const Trace::Data& d) { void Trace::addData(const Trace::Data& d) {
@ -74,7 +71,7 @@ void Trace::addData(const Trace::Data& d) {
// insert at this position // insert at this position
data.insert(lower, d); data.insert(lower, d);
} }
emit outputSampleChanged(lower - data.begin()); emit outputSamplesChanged(lower - data.begin(), lower - data.begin() + 1);
if(lower == data.begin()) { if(lower == data.begin()) {
// received the first point, which means the last sweep just finished // received the first point, which means the last sweep just finished
if(tdr_users) { if(tdr_users) {
@ -173,7 +170,7 @@ void Trace::removeMarker(TraceMarker *m)
void Trace::updateTimeDomainData() void Trace::updateTimeDomainData()
{ {
if(_data.size() < 2) { if(data.size() < 2) {
// can't compute anything // can't compute anything
timeDomain.clear(); timeDomain.clear();
return; return;
@ -258,13 +255,11 @@ void Trace::updateLastMath(vector<MathInfo>::reverse_iterator start)
Q_ASSERT(newLast != nullptr); Q_ASSERT(newLast != nullptr);
if(newLast != lastMath) { if(newLast != lastMath) {
if(lastMath != nullptr) { if(lastMath != nullptr) {
disconnect(lastMath, &TraceMath::outputDataChanged, this, nullptr); disconnect(lastMath, &TraceMath::outputSamplesChanged, this, nullptr);
disconnect(lastMath, &TraceMath::outputSampleChanged, this, nullptr);
} }
lastMath = newLast; lastMath = newLast;
// relay signals of end of math chain // relay signals of end of math chain
connect(lastMath, &TraceMath::outputDataChanged, this, &Trace::dataChanged); connect(lastMath, &TraceMath::outputSamplesChanged, this, &Trace::dataChanged);
connect(lastMath, &TraceMath::outputSampleChanged, this, &Trace::dataChanged);
} }
} }
@ -419,7 +414,7 @@ bool Trace::hasMathOperations()
void Trace::enableMath(bool enable) void Trace::enableMath(bool enable)
{ {
auto start = enable ? mathOps.rbegin() : make_reverse_iterator(mathOps.begin()); auto start = enable ? mathOps.rbegin() : make_reverse_iterator(mathOps.begin() + 1);
updateLastMath(start); updateLastMath(start);
} }

View File

@ -666,8 +666,8 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
case YAxisType::Phase: case YAxisType::Phase:
case YAxisType::VSWR: { case YAxisType::VSWR: {
auto d = t->sample(sample); auto d = t->sample(sample);
ret.setY(traceToCoordinate(d.S, type)); ret.setY(traceToCoordinate(d.y, type));
ret.setX(d.frequency); ret.setX(d.x);
} }
break; break;
case YAxisType::Impulse: case YAxisType::Impulse:
@ -714,12 +714,12 @@ unsigned int TraceXYPlot::numTraceSamples(Trace *t)
QPoint TraceXYPlot::dataToPixel(Trace::Data d) 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(); return QPoint();
} }
auto y = traceToCoordinate(d.S, YAxis[0].type); auto y = traceToCoordinate(d.y, YAxis[0].type);
QPoint p; QPoint p;
p.setX(Util::Scale<double>(d.frequency, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); p.setX(Util::Scale<double>(d.x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
p.setY(Util::Scale<double>(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0)); p.setY(Util::Scale<double>(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0));
return p; return p;
} }