From dced1732d6811cb81134089c9ba8ec046e31e8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Wed, 1 Dec 2021 22:11:50 +0100 Subject: [PATCH] median option for averaging --- .../SpectrumAnalyzer/spectrumanalyzer.cpp | 12 ++ .../SpectrumAnalyzer/spectrumanalyzer.h | 1 + Software/PC_Application/VNA/vna.cpp | 11 ++ Software/PC_Application/VNA/vna.h | 1 + Software/PC_Application/appwindow.cpp | 8 ++ Software/PC_Application/averaging.cpp | 114 +++++++++++++++--- Software/PC_Application/averaging.h | 9 ++ Software/PC_Application/preferences.cpp | 2 + Software/PC_Application/preferences.h | 4 +- Software/PC_Application/preferencesdialog.ui | 32 ++++- 10 files changed, 172 insertions(+), 22 deletions(-) diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index 1f497d4..14c21ed 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -265,6 +265,13 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window) // Set initial sweep settings auto pref = Preferences::getInstance(); + + if(pref.Acquisition.useMedianAveraging) { + average.setMode(Averaging::Mode::Median); + } else { + average.setMode(Averaging::Mode::Mean); + } + if(pref.Startup.RememberSweepSettings) { LoadSweepSettings(); } else { @@ -1075,6 +1082,11 @@ void SpectrumAnalyzer::updateGraphColors() emit graphColorsChanged(); } +void SpectrumAnalyzer::setAveragingMode(Averaging::Mode mode) +{ + average.setMode(mode); +} + QString SpectrumAnalyzer::WindowToString(SpectrumAnalyzer::Window w) { switch(w) { diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h index 8d2c7f9..a4eacca 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h @@ -26,6 +26,7 @@ public: virtual void fromJSON(nlohmann::json j) override; void updateGraphColors(); + void setAveragingMode(Averaging::Mode mode); private: diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 40073fa..dd5c5b8 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -530,6 +530,12 @@ VNA::VNA(AppWindow *window) // Set initial sweep settings auto pref = Preferences::getInstance(); + if(pref.Acquisition.useMedianAveraging) { + average.setMode(Averaging::Mode::Median); + } else { + average.setMode(Averaging::Mode::Mean); + } + if(pref.Startup.RememberSweepSettings) { LoadSweepSettings(); } else { @@ -1466,6 +1472,11 @@ void VNA::updateGraphColors() emit graphColorsChanged(); } +void VNA::setAveragingMode(Averaging::Mode mode) +{ + average.setMode(mode); +} + QString VNA::SweepTypeToString(VNA::SweepType sw) { switch(sw) { diff --git a/Software/PC_Application/VNA/vna.h b/Software/PC_Application/VNA/vna.h index c78f30a..dcb03d4 100644 --- a/Software/PC_Application/VNA/vna.h +++ b/Software/PC_Application/VNA/vna.h @@ -29,6 +29,7 @@ public: virtual void fromJSON(nlohmann::json j) override; void updateGraphColors(); + void setAveragingMode(Averaging::Mode mode); enum class SweepType { Frequency = 0, diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index ede5e71..dc01824 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -204,6 +204,14 @@ AppWindow::AppWindow(QWidget *parent) vna->updateGraphColors(); } + // averaging mode may have changed, update for all relevant modes + if(p.Acquisition.useMedianAveraging) { + spectrumAnalyzer->setAveragingMode(Averaging::Mode::Median); + vna->setAveragingMode(Averaging::Mode::Median); + } else { + spectrumAnalyzer->setAveragingMode(Averaging::Mode::Mean); + vna->setAveragingMode(Averaging::Mode::Mean); + } }); connect(ui->actionAbout, &QAction::triggered, [=](){ diff --git a/Software/PC_Application/averaging.cpp b/Software/PC_Application/averaging.cpp index 8a5cffa..1eeb004 100644 --- a/Software/PC_Application/averaging.cpp +++ b/Software/PC_Application/averaging.cpp @@ -5,6 +5,7 @@ using namespace std; Averaging::Averaging() { averages = 1; + mode = Mode::Mean; } void Averaging::reset(unsigned int points) @@ -45,18 +46,57 @@ Protocol::Datapoint Averaging::process(Protocol::Datapoint d) deque->pop_front(); } - // calculate average - complex sum[4]; - for(auto s : *deque) { - sum[0] += s[0]; - sum[1] += s[1]; - sum[2] += s[2]; - sum[3] += s[3]; + switch(mode) { + case Mode::Mean: { + // calculate average + complex sum[4]; + for(auto s : *deque) { + sum[0] += s[0]; + sum[1] += s[1]; + sum[2] += s[2]; + sum[3] += s[3]; + } + S11 = sum[0] / (double) (deque->size()); + S12 = sum[1] / (double) (deque->size()); + S21 = sum[2] / (double) (deque->size()); + S22 = sum[3] / (double) (deque->size()); + } + break; + case Mode::Median: { + auto size = deque->size(); + // create sorted arrays + std::vector> S11sorted, S12sorted, S21sorted, S22sorted; + S11sorted.reserve(size); + S12sorted.reserve(size); + S21sorted.reserve(size); + S22sorted.reserve(size); + + auto comp = [=](const complex&a, const complex&b){ + return abs(a) < abs(b); + }; + + for(auto d : *deque) { + S11sorted.insert(upper_bound(S11sorted.begin(), S11sorted.end(), d[0], comp), d[0]); + S12sorted.insert(upper_bound(S12sorted.begin(), S12sorted.end(), d[1], comp), d[1]); + S21sorted.insert(upper_bound(S21sorted.begin(), S21sorted.end(), d[2], comp), d[2]); + S22sorted.insert(upper_bound(S22sorted.begin(), S22sorted.end(), d[3], comp), d[3]); + } + if(size & 0x01) { + // odd number of samples + S11 = S11sorted[size / 2]; + S12 = S12sorted[size / 2]; + S21 = S21sorted[size / 2]; + S22 = S22sorted[size / 2]; + } else { + // even number, use average of middle samples + S11 = (S11sorted[size / 2 - 1] + S11sorted[size / 2]) / 2.0; + S12 = (S12sorted[size / 2 - 1] + S12sorted[size / 2]) / 2.0; + S21 = (S21sorted[size / 2 - 1] + S21sorted[size / 2]) / 2.0; + S22 = (S22sorted[size / 2 - 1] + S22sorted[size / 2]) / 2.0; + } + } + break; } - S11 = sum[0] / (double) (deque->size()); - S12 = sum[1] / (double) (deque->size()); - S21 = sum[2] / (double) (deque->size()); - S22 = sum[3] / (double) (deque->size()); } d.real_S11 = S11.real(); @@ -90,16 +130,40 @@ Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerRe deque->pop_front(); } - // calculate average - complex sum[4]; - for(auto s : *deque) { - sum[0] += s[0]; - sum[1] += s[1]; - sum[2] += s[2]; - sum[3] += s[3]; + switch(mode) { + case Mode::Mean: { + // calculate average + complex sum[2]; + for(auto s : *deque) { + sum[0] += s[0]; + sum[1] += s[1]; + } + d.port1 = abs(sum[0] / (double) (deque->size())); + d.port2 = abs(sum[1] / (double) (deque->size())); + } + break; + case Mode::Median: { + auto size = deque->size(); + // create sorted arrays + std::vector port1, port2; + port1.reserve(size); + port2.reserve(size); + for(auto d : *deque) { + port1.insert(upper_bound(port1.begin(), port1.end(), abs(d[0])), abs(d[0])); + port2.insert(upper_bound(port2.begin(), port2.end(), abs(d[0])), abs(d[0])); + } + if(size & 0x01) { + // odd number of samples + d.port1 = port1[size / 2]; + d.port2 = port1[size / 2]; + } else { + // even number, use average of middle samples + d.port1 = (port1[size / 2 - 1] + port1[size / 2]) / 2; + d.port2 = (port2[size / 2 - 1] + port2[size / 2]) / 2; + } + } + break; } - d.port1 = abs(sum[0] / (double) (deque->size())); - d.port2 = abs(sum[1] / (double) (deque->size())); } return d; @@ -122,3 +186,13 @@ unsigned int Averaging::currentSweep() return 0; } } + +Averaging::Mode Averaging::getMode() const +{ + return mode; +} + +void Averaging::setMode(const Mode &value) +{ + mode = value; +} diff --git a/Software/PC_Application/averaging.h b/Software/PC_Application/averaging.h index 7f5a768..84dc68e 100644 --- a/Software/PC_Application/averaging.h +++ b/Software/PC_Application/averaging.h @@ -10,6 +10,11 @@ class Averaging { public: + enum class Mode { + Mean, + Median + }; + Averaging(); void reset(unsigned int points); void setAverages(unsigned int a); @@ -21,10 +26,14 @@ public: // Returns the number of the currently active sweep. Value is incremented whenever the the first point of the sweep is added // Returned values are in range 0 (when no data has been added yet) to averages unsigned int currentSweep(); + Mode getMode() const; + void setMode(const Mode &value); + private: std::vector, 4>>> avg; int maxPoints; unsigned int averages; + Mode mode; }; #endif // AVERAGING_H diff --git a/Software/PC_Application/preferences.cpp b/Software/PC_Application/preferences.cpp index e7b6696..492f38c 100644 --- a/Software/PC_Application/preferences.cpp +++ b/Software/PC_Application/preferences.cpp @@ -134,6 +134,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) : p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked(); p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked(); p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value(); + p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1; p->Graphs.Color.background = ui->GraphsColorBackground->getColor(); p->Graphs.Color.axis = ui->GraphsColorAxis->getColor(); p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked(); @@ -199,6 +200,7 @@ void PreferencesDialog::setInitialGUIState() ui->AcquisitionUseHarmonic->setChecked(p->Acquisition.harmonicMixing); ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode); ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT); + ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0); ui->GraphsColorBackground->setColor(p->Graphs.Color.background); ui->GraphsColorAxis->setColor(p->Graphs.Color.axis); diff --git a/Software/PC_Application/preferences.h b/Software/PC_Application/preferences.h index 698b8f8..5ef0ede 100644 --- a/Software/PC_Application/preferences.h +++ b/Software/PC_Application/preferences.h @@ -66,6 +66,7 @@ public: bool harmonicMixing; bool useDFTinSAmode; double RBWLimitForDFT; + bool useMedianAveraging; } Acquisition; struct { struct { @@ -100,7 +101,7 @@ private: QString name; QVariant def; }; - const std::array descr = {{ + const std::array descr = {{ {&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true}, {&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false}, {&Startup.DefaultSweep.type, "Startup.DefaultSweep.type", "Frequency"}, @@ -128,6 +129,7 @@ private: {&Acquisition.harmonicMixing, "Acquisition.harmonicMixing", false}, {&Acquisition.useDFTinSAmode, "Acquisition.useDFTinSAmode", true}, {&Acquisition.RBWLimitForDFT, "Acquisition.RBWLimitForDFT", 3000.0}, + {&Acquisition.useMedianAveraging, "Acquisition.useMedianAveraging", false}, {&Graphs.Color.background, "Graphs.Color.background", QColor(Qt::black)}, {&Graphs.Color.axis, "Graphs.Color.axis", QColor(Qt::white)}, {&Graphs.Color.Ticks.Background.enabled, "Graphs.Color.Ticks.Background.enabled", true}, diff --git a/Software/PC_Application/preferencesdialog.ui b/Software/PC_Application/preferencesdialog.ui index 23b3fb5..fff4aa0 100644 --- a/Software/PC_Application/preferencesdialog.ui +++ b/Software/PC_Application/preferencesdialog.ui @@ -78,7 +78,7 @@ - 2 + 1 @@ -637,6 +637,36 @@ + + + + Common + + + + + + Averaging mode: + + + + + + + + Mean + + + + + Median + + + + + + +