diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2016-06-20 11:19:33 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2016-06-20 11:19:33 +0200 |
commit | aaf7965df8b423bcacd411931d4cb0bb238ced61 (patch) | |
tree | 8c0cfe7dcb2816870405742c16bb5bb64ec95a2a | |
parent | afb4f51a5526822681b147d5cbf37b6f818c49ca (diff) |
filter/ewma: cleanup, update
- switch to our timer since it has more than millisecond precision
- use slider_value for settings
- sanitize allowed scaling curve bounds
- ensure min <= max in dialog
- ensure min <= max at filter runtime
- check for NaN taking -ffast-math into account
- simplify dialog logic
- change delta smoothing constant
-rw-r--r-- | filter-ewma2/ftnoir_ewma_filtercontrols.ui | 250 | ||||
-rw-r--r-- | filter-ewma2/ftnoir_filter_ewma2.cpp | 50 | ||||
-rw-r--r-- | filter-ewma2/ftnoir_filter_ewma2.h | 26 | ||||
-rw-r--r-- | filter-ewma2/ftnoir_filter_ewma2_dialog.cpp | 38 |
4 files changed, 145 insertions, 219 deletions
diff --git a/filter-ewma2/ftnoir_ewma_filtercontrols.ui b/filter-ewma2/ftnoir_ewma_filtercontrols.ui index 9387f0d5..36826c2d 100644 --- a/filter-ewma2/ftnoir_ewma_filtercontrols.ui +++ b/filter-ewma2/ftnoir_ewma_filtercontrols.ui @@ -13,11 +13,17 @@ <height>380</height> </rect> </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="windowTitle"> - <string>EWMA2 filter settings</string> + <string>EWMA filter settings</string> </property> <property name="windowIcon"> - <iconset resource="ewma-filter.qrc"> + <iconset resource="../gui/ui-res.qrc"> <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset> </property> <property name="layoutDirection"> @@ -39,30 +45,24 @@ <property name="bottomMargin"> <number>2</number> </property> - <item row="0" column="0"> - <widget class="QLabel" name="lblInvert1_6"> + <item row="1" column="0"> + <widget class="QLabel" name="lblInvert1_7"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> <property name="styleSheet"> <string notr="true"/> </property> <property name="text"> - <string>Min</string> + <string>Max</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QSlider" name="minSmooth"> + <item row="1" column="1"> + <widget class="QSlider" name="maxSmooth"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -70,16 +70,19 @@ </sizepolicy> </property> <property name="minimum"> - <number>1</number> + <number>0</number> </property> <property name="maximum"> - <number>100</number> + <number>1000</number> </property> - <property name="pageStep"> + <property name="singleStep"> <number>5</number> </property> + <property name="pageStep"> + <number>50</number> + </property> <property name="value"> - <number>2</number> + <number>1000</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -89,55 +92,30 @@ </property> </widget> </item> - <item row="0" column="2"> - <widget class="QSpinBox" name="spinMinSmooth"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="styleSheet"> - <string notr="true">background:none;</string> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::PlusMinus</enum> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>120</number> - </property> - <property name="singleStep"> - <number>5</number> - </property> - <property name="value"> - <number>2</number> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="lblInvert1_7"> + <item row="0" column="0"> + <widget class="QLabel" name="lblInvert1_6"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> <property name="styleSheet"> <string notr="true"/> </property> <property name="text"> - <string>Max</string> + <string>Min</string> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QSlider" name="maxSmooth"> + <item row="0" column="1"> + <widget class="QSlider" name="minSmooth"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -148,13 +126,16 @@ <number>1</number> </property> <property name="maximum"> - <number>100</number> + <number>1000</number> </property> - <property name="pageStep"> + <property name="singleStep"> <number>5</number> </property> + <property name="pageStep"> + <number>50</number> + </property> <property name="value"> - <number>10</number> + <number>1000</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -164,38 +145,7 @@ </property> </widget> </item> - <item row="1" column="2"> - <widget class="QSpinBox" name="spinMaxSmooth"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="styleSheet"> - <string notr="true">background:none;</string> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::PlusMinus</enum> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>120</number> - </property> - <property name="singleStep"> - <number>5</number> - </property> - <property name="value"> - <number>10</number> - </property> - </widget> - </item> - <item row="2" column="0"> + <item row="3" column="0"> <widget class="QLabel" name="lblInvert1_8"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> @@ -211,7 +161,7 @@ </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <widget class="QSlider" name="powCurve"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> @@ -220,16 +170,19 @@ </sizepolicy> </property> <property name="minimum"> - <number>1</number> + <number>0</number> </property> <property name="maximum"> - <number>100</number> + <number>1000</number> </property> - <property name="pageStep"> + <property name="singleStep"> <number>5</number> </property> + <property name="pageStep"> + <number>50</number> + </property> <property name="value"> - <number>10</number> + <number>1000</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -239,31 +192,51 @@ </property> </widget> </item> - <item row="2" column="2"> - <widget class="QSpinBox" name="spinPowCurve"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <item row="0" column="2"> + <widget class="QLabel" name="min_label"> + <property name="minimumSize"> + <size> + <width>45</width> + <height>0</height> + </size> </property> - <property name="styleSheet"> - <string notr="true">background:none;</string> + <property name="text"> + <string>100%</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLabel" name="max_label"> + <property name="minimumSize"> + <size> + <width>45</width> + <height>0</height> + </size> </property> - <property name="readOnly"> - <bool>true</bool> + <property name="text"> + <string>100%</string> </property> - <property name="buttonSymbols"> - <enum>QAbstractSpinBox::PlusMinus</enum> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> - <property name="maximum"> - <number>100</number> + </widget> + </item> + <item row="3" column="2"> + <widget class="QLabel" name="curve_label"> + <property name="minimumSize"> + <size> + <width>45</width> + <height>0</height> + </size> </property> - <property name="singleStep"> - <number>5</number> + <property name="text"> + <string>100%</string> </property> - <property name="value"> - <number>10</number> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> @@ -312,58 +285,9 @@ p, li { white-space: pre-wrap; } </layout> </widget> <resources> - <include location="ewma-filter.qrc"/> + <include location="../gui/ui-res.qrc"/> </resources> - <connections> - <connection> - <sender>minSmooth</sender> - <signal>valueChanged(int)</signal> - <receiver>spinMinSmooth</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>303</x> - <y>33</y> - </hint> - <hint type="destinationlabel"> - <x>391</x> - <y>36</y> - </hint> - </hints> - </connection> - <connection> - <sender>maxSmooth</sender> - <signal>valueChanged(int)</signal> - <receiver>spinMaxSmooth</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>281</x> - <y>61</y> - </hint> - <hint type="destinationlabel"> - <x>390</x> - <y>74</y> - </hint> - </hints> - </connection> - <connection> - <sender>powCurve</sender> - <signal>valueChanged(int)</signal> - <receiver>spinPowCurve</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>236</x> - <y>101</y> - </hint> - <hint type="destinationlabel"> - <x>391</x> - <y>98</y> - </hint> - </hints> - </connection> - </connections> + <connections/> <slots> <slot>startEngineClicked()</slot> <slot>stopEngineClicked()</slot> diff --git a/filter-ewma2/ftnoir_filter_ewma2.cpp b/filter-ewma2/ftnoir_filter_ewma2.cpp index bf4b1083..7229c6d6 100644 --- a/filter-ewma2/ftnoir_filter_ewma2.cpp +++ b/filter-ewma2/ftnoir_filter_ewma2.cpp @@ -22,39 +22,42 @@ // to minSmooth at a rate controlled by the powCurve setting. -FTNoIR_Filter::FTNoIR_Filter() +FTNoIR_Filter::FTNoIR_Filter() : first_run(true) { - reset(); -} - -void FTNoIR_Filter::receiveSettings() -{ - s.b->reload(); -} - -void FTNoIR_Filter::reset() -{ - timer.invalidate(); } void FTNoIR_Filter::filter(const double *input, double *output) { // Start the timer and initialise filter state if it's not running. - if (!timer.isValid()) { + if (first_run) + { + first_run = false; timer.start(); - for (int i=0;i<6;i++) { + for (int i=0;i<6;i++) + { last_output[i] = input[i]; - last_delta[i] = 0.0; - last_noise[i] = 0.0; + last_delta[i] = 0; + last_noise[i] = 0; } } // Get the time in seconds since last run and restart the timer. - const double dt = timer.restart() / 1000.; + const double dt = timer.elapsed_seconds(); + timer.start(); // Calculate delta_alpha and noise_alpha from dt. double delta_alpha = dt/(dt + delta_RC); double noise_alpha = dt/(dt + noise_RC); + + // scale curve .01->1 where 1.0 is sqrt(norm_noise). + const double smoothing_scale_curve = static_cast<slider_value>(s.kSmoothingScaleCurve); + // min/max smoothing .01->1 + const double min_smoothing = static_cast<slider_value>(s.kMinSmoothing); + const double max_smoothing = std::max(min_smoothing, static_cast<slider_value>(s.kMaxSmoothing).cur()); + // Calculate the new camera position. - for (int i=0;i<6;i++) { + for (int i=0;i<6;i++) + { + using std::pow; + // Calculate the current and smoothed delta. double delta = input[i] - last_output[i]; last_delta[i] = delta_alpha*delta + (1.0-delta_alpha)*last_delta[i]; @@ -62,15 +65,10 @@ void FTNoIR_Filter::filter(const double *input, double *output) double noise = last_delta[i]*last_delta[i]; last_noise[i] = noise_alpha*noise + (1.0-noise_alpha)*last_noise[i]; // Normalise the noise between 0->1 for 0->9 variances (0->3 stddevs). - double norm_noise = std::min<double>(noise/(9.0*last_noise[i]), 1.0); - if (std::isnan(norm_noise)) - norm_noise = 0; + double norm_noise = last_noise[i] < 1e-10 ? 0 : std::min<double>(noise/(9.0*last_noise[i]), 1.0); // Calculate the smoothing 0.0->1.0 from the normalized noise. - // TODO(abo): change kSmoothingScaleCurve to a float where 1.0 is sqrt(norm_noise). - double smoothing = 1.0 - pow(norm_noise, s.kSmoothingScaleCurve/20.0); - // Currently min/max smoothing are ints 0->100. We want 0.0->3.0 seconds. - // TODO(abo): Change kMinSmoothing, kMaxSmoothing to floats 0.0->3.0 seconds RC. - double RC = 3.0*(s.kMinSmoothing + smoothing*(s.kMaxSmoothing - s.kMinSmoothing))/100.0; + double smoothing = 1.0 - pow(norm_noise, smoothing_scale_curve); + double RC = (min_smoothing + smoothing*(max_smoothing - min_smoothing)); // Calculate the dynamic alpha. double alpha = dt/(dt + RC); // Calculate the new output position. diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h index bdb9cedc..328279ac 100644 --- a/filter-ewma2/ftnoir_filter_ewma2.h +++ b/filter-ewma2/ftnoir_filter_ewma2.h @@ -2,20 +2,20 @@ #include "opentrack/plugin-api.hpp" #include "ui_ftnoir_ewma_filtercontrols.h" -#include <QElapsedTimer> #include <QWidget> #include <QMutex> #include "opentrack-compat/options.hpp" +#include "opentrack-compat/timer.hpp" using namespace options; struct settings : opts { // these are sadly sliders for now due to int/double mismatch -sh - value<int> kMinSmoothing, kMaxSmoothing, kSmoothingScaleCurve; + value<slider_value> kMinSmoothing, kMaxSmoothing, kSmoothingScaleCurve; settings() : opts("ewma-filter"), - kMinSmoothing(b, "min-smoothing", 15), - kMaxSmoothing(b, "max-smoothing", 50), - kSmoothingScaleCurve(b, "smoothing-scale-curve", 10) + kMinSmoothing(b, "min-smoothing", slider_value(.02, .01, 1)), + kMaxSmoothing(b, "max-smoothing", slider_value(.7, .01, 1)), + kSmoothingScaleCurve(b, "smoothing-scale-curve", slider_value(.7, .01, 1)) {} }; @@ -24,19 +24,18 @@ class FTNoIR_Filter : public IFilter { public: FTNoIR_Filter(); - void reset(); void filter(const double *input, double *output); - void receiveSettings(); private: - // Deltas are smoothed over the last 1/60sec (16ms). - const double delta_RC = 0.016; + // Deltas are smoothed over the last 1/20sec. + const double delta_RC = 1./20; // Noise is smoothed over the last 60sec. const double noise_RC = 60.0; double last_delta[6]; double last_noise[6]; double last_output[6]; - QElapsedTimer timer; + Timer timer; settings s; + bool first_run; }; class FilterControls: public IFilterDialog @@ -44,18 +43,17 @@ class FilterControls: public IFilterDialog Q_OBJECT public: FilterControls(); - void register_filter(IFilter* flt); - void unregister_filter(); + void register_filter(IFilter*) override {} + void unregister_filter() override {} private: Ui::UICFilterControls ui; - void save(); settings s; - FTNoIR_Filter* pFilter; private slots: void doOK(); void doCancel(); + void update_labels(int); }; class FTNoIR_FilterDll : public Metadata diff --git a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp index 5425fada..fca1d0ff 100644 --- a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp +++ b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp @@ -1,12 +1,13 @@ #include "ftnoir_filter_ewma2.h" #include <cmath> #include <QDebug> +#include <QString> #include "opentrack/plugin-api.hpp" #include "ui_ftnoir_ewma_filtercontrols.h" -FilterControls::FilterControls() : pFilter(NULL) +FilterControls::FilterControls() { - ui.setupUi( this ); + ui.setupUi(this); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); @@ -14,20 +15,24 @@ FilterControls::FilterControls() : pFilter(NULL) tie_setting(s.kMaxSmoothing, ui.maxSmooth); tie_setting(s.kMinSmoothing, ui.minSmooth); tie_setting(s.kSmoothingScaleCurve, ui.powCurve); -} -void FilterControls::register_filter(IFilter* flt) -{ - pFilter = (FTNoIR_Filter*) flt; -} + connect(ui.powCurve, &QSlider::valueChanged, this, &FilterControls::update_labels); + connect(ui.minSmooth, &QSlider::valueChanged, this, &FilterControls::update_labels); + connect(ui.maxSmooth, &QSlider::valueChanged, this, &FilterControls::update_labels); -void FilterControls::unregister_filter() -{ - pFilter = NULL; + using std::min; + using std::max; + + connect(ui.minSmooth, &QSlider::valueChanged, this, + [&](int v) -> void { if (ui.maxSmooth->value() < v) ui.maxSmooth->setValue(v); }); + + connect(ui.maxSmooth, &QSlider::valueChanged, this, + [&](int v) -> void { if (ui.minSmooth->value() > v) ui.minSmooth->setValue(v); }); } -void FilterControls::doOK() { - save(); +void FilterControls::doOK() +{ + s.b->save(); close(); } @@ -36,8 +41,9 @@ void FilterControls::doCancel() close(); } -void FilterControls::save() { - s.b->save(); - if (pFilter) - pFilter->receiveSettings(); +void FilterControls::update_labels(int) +{ + ui.curve_label->setText(QString::number(static_cast<slider_value>(s.kSmoothingScaleCurve).cur() * 100, 'f', 2) + "%"); + ui.min_label->setText(QString::number(static_cast<slider_value>(s.kMinSmoothing).cur() * 100, 'f', 2) + "%"); + ui.max_label->setText(QString::number(static_cast<slider_value>(s.kMaxSmoothing).cur() * 100, 'f', 2) + "%"); } |