diff options
author | Michael Welter <DaWelter@users.noreply.github.com> | 2024-05-14 19:50:04 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-14 19:50:04 +0200 |
commit | 9f4bc14bf6d5fbe4534953945a587c89a86a29f5 (patch) | |
tree | cf5bdd7e80107c19d87a8ab24e38a4c7debb4051 | |
parent | aa31de35da5e6a6f43d443aa17abd5a4dc78fbc8 (diff) |
Add new filter: HamiltonAccela which is a combination of ideas from Accela and Hamilton filters (#1843)
-rw-r--r-- | filter-accela-hamilton/CMakeLists.txt | 2 | ||||
-rw-r--r-- | filter-accela-hamilton/accela_hamilton.cpp | 120 | ||||
-rw-r--r-- | filter-accela-hamilton/accela_hamilton.h | 69 | ||||
-rw-r--r-- | filter-accela-hamilton/accela_hamilton_dialog.cpp | 99 | ||||
-rw-r--r-- | filter-accela-hamilton/accela_hamilton_filtercontrols.ui | 485 | ||||
-rw-r--r-- | filter-accela-hamilton/accela_hamilton_settings.hpp | 63 | ||||
-rw-r--r-- | filter-accela-hamilton/lang/nl_NL.ts | 82 | ||||
-rw-r--r-- | filter-accela-hamilton/lang/ru_RU.ts | 82 | ||||
-rw-r--r-- | filter-accela-hamilton/lang/stub.ts | 82 | ||||
-rw-r--r-- | filter-accela-hamilton/lang/zh_CN.ts | 82 |
10 files changed, 1166 insertions, 0 deletions
diff --git a/filter-accela-hamilton/CMakeLists.txt b/filter-accela-hamilton/CMakeLists.txt new file mode 100644 index 00000000..825cef5e --- /dev/null +++ b/filter-accela-hamilton/CMakeLists.txt @@ -0,0 +1,2 @@ +otr_module(filter-accela-hamilton) +target_link_libraries(opentrack-filter-accela-hamilton opentrack-spline) diff --git a/filter-accela-hamilton/accela_hamilton.cpp b/filter-accela-hamilton/accela_hamilton.cpp new file mode 100644 index 00000000..5e7660a8 --- /dev/null +++ b/filter-accela-hamilton/accela_hamilton.cpp @@ -0,0 +1,120 @@ +/* Copyright (c) 2012-2015 Stanislaw Halik + * Copyright (c) 2023-2024 Michael Welter + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#include "accela_hamilton.h" +#include "compat/math.hpp" +#include "api/plugin-api.hpp" +#include "opentrack/defs.hpp" + +#include <algorithm> +#include <QDebug> +#include <QMutexLocker> + +#include "compat/math-imports.hpp" +#include "compat/time.hpp" + + +accela_hamilton::accela_hamilton() +{ + s.make_splines(spline_rot, spline_pos); +} + + +void accela_hamilton::filter(const double* input, double *output) +{ + constexpr float EPSILON = 1e-15F; + + const QQuaternion current_rot = QQuaternion::fromEulerAngles(input[Pitch], input[Yaw], input[Roll]); + const QVector3D current_pos(input[TX], input[TY], input[TZ]); + + if (unlikely(first_run)) + { + first_run = false; + last_rotation = current_rot; + last_position = current_pos; + t.start(); +#if defined DEBUG_ACCELA + debug_max = 0; + debug_timer.start(); +#endif + return; + } + + const float pos_thres{s.pos_smoothing}; + const float pos_dz{ s.pos_deadzone}; + + const float dt = t.elapsed_seconds(); + t.start(); + + // Position + { + const QVector3D delta = current_pos - last_position; + const float delta_len = delta.length(); + QVector3D delta_normed = delta_len>0.F ? delta/delta_len : QVector3D(); // Zero vector when length was zero. + const float gain = dt*spline_pos.get_value_no_save(std::max(0.F, delta_len-pos_dz) / pos_thres); + const QVector3D output_pos = last_position + gain * delta_normed; + output[TX] = output_pos.x(); + output[TY] = output_pos.y(); + output[TZ] = output_pos.z(); + last_position = output_pos; + } + + // Zoom smoothing: + const float zoomed_smoothing = [this](float output_z) { + // Local copies because accessing settings involves thread synchronization + // and I don't like this in the middle of math. + const float max_zoomed_smoothing {s.max_zoomed_smoothing}; + const float max_z {s.max_z}; + // Movement toward the monitor is negative. Negate and clamp it to get a positive value + const float z = std::clamp(-output_z, 0.F, max_z); + return max_zoomed_smoothing * z / (max_z + EPSILON); + }(output[TZ]); + + const float rot_dz{ s.rot_deadzone}; + const float rot_thres = float{s.rot_smoothing} + zoomed_smoothing; + + // Rotation + { + // Inter/extrapolates along the arc between the old and new orientation. + // It's basically a quaternion spherical linear interpolation, where the + // accela gain x dt is the blending parameter. Might actually overshoot + // the new orientation, but that's fine. + + // Compute rotation angle and axis which brings the previous orientation to the current rotation + QVector3D axis; + float angle; + (last_rotation.conjugated() * current_rot).getAxisAndAngle(&axis, &angle); + // Apply the Accela gain magic. Also need to multiply with dt here. + angle = std::max(0.f, angle - std::copysign(rot_dz, angle)) / rot_thres; + const float gain_angle = dt*spline_rot.get_value_no_save(std::abs(angle)) * signum(angle); + // Rotate toward the measured orientation. We take the already computed axis. But the angle is now the accela gain. + const QQuaternion output_rot = last_rotation * QQuaternion::fromAxisAndAngle(axis, gain_angle); + // And back to Euler angles + const QVector3D output_euler = output_rot.toEulerAngles(); + output[Pitch] = output_euler.x(); + output[Yaw] = output_euler.y(); + output[Roll] = output_euler.z(); + last_rotation = output_rot; + } +} + +namespace detail::accela_hamilton { + +void settings_accela_hamilton::make_splines(spline& rot, spline& pos) +{ + rot.clear(); pos.clear(); + + for (const auto& val : rot_gains) + rot.add_point({ val.x, val.y }); + + for (const auto& val : pos_gains) + pos.add_point({ val.x, val.y }); +} + +} // ns detail::accela_hamilton + +OPENTRACK_DECLARE_FILTER(accela_hamilton, dialog_accela_hamilton, accela_hamiltonDll) diff --git a/filter-accela-hamilton/accela_hamilton.h b/filter-accela-hamilton/accela_hamilton.h new file mode 100644 index 00000000..ccd74fbf --- /dev/null +++ b/filter-accela-hamilton/accela_hamilton.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2012-2015 Stanislaw Halik + * Copyright (c) 2023-2024 Michael Welter + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#pragma once + +#include "ui_accela_hamilton_filtercontrols.h" + +#include "accela_hamilton_settings.hpp" +#include "api/plugin-api.hpp" +#include "compat/timer.hpp" +#include "compat/variance.hpp" + +#include <QMutex> +#include <QTimer> +#include <QQuaternion> +#include <QVector3D> + +//#define DEBUG_ACCELA + +struct accela_hamilton : IFilter +{ + accela_hamilton(); + void filter(const double* input, double *output) override; + void center() override { first_run = true; } + spline spline_rot, spline_pos; + module_status initialize() override { return status_ok(); } +private: + settings_accela_hamilton s; + QVector3D last_position = {}; + QQuaternion last_rotation = {}; + Timer t; +#if defined DEBUG_ACCELA + Timer debug_timer; + double debug_max; + variance var; +#endif + bool first_run = true; +}; + +class dialog_accela_hamilton : public IFilterDialog +{ + Q_OBJECT +public: + dialog_accela_hamilton(); + void register_filter(IFilter*) override {} + void unregister_filter() override {} + void save() override; + void reload() override; + bool embeddable() noexcept override { return true; } + void set_buttons_visible(bool x) override; +private: + Ui::AccelaUICdialog_accela ui; + settings_accela_hamilton s; +private slots: + void doOK(); + void doCancel(); +}; + +class accela_hamiltonDll : public Metadata +{ + Q_OBJECT + + QString name() override { return tr("AccelaHamilton"); } + QIcon icon() override { return QIcon(":/images/filter-16.png"); } +}; diff --git a/filter-accela-hamilton/accela_hamilton_dialog.cpp b/filter-accela-hamilton/accela_hamilton_dialog.cpp new file mode 100644 index 00000000..711535f8 --- /dev/null +++ b/filter-accela-hamilton/accela_hamilton_dialog.cpp @@ -0,0 +1,99 @@ +/* Copyright (c) 2012-2015 Stanislaw Halik + * Copyright (c) 2023-2024 Michael Welter + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#include "accela_hamilton.h" +#include <cmath> +#include <QDebug> +#include <algorithm> +#include <QDoubleSpinBox> +#include "api/plugin-api.hpp" +#include "spline/spline-widget.hpp" +#include <QDialog> + +using namespace options; + +dialog_accela_hamilton::dialog_accela_hamilton() +{ + ui.setupUi(this); + + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); + + tie_setting(s.rot_smoothing, ui.rotation_slider); + tie_setting(s.pos_smoothing, ui.translation_slider); + tie_setting(s.rot_deadzone, ui.rot_dz_slider); + tie_setting(s.pos_deadzone, ui.trans_dz_slider); + + tie_setting(s.rot_smoothing, ui.rot_gain, [](const slider_value& s) { return tr("%1°").arg(s, 0, 'g', 4); }); + tie_setting(s.pos_smoothing, ui.trans_gain, [](const slider_value& s) { return tr("%1mm").arg(s, 0, 'g', 4); }); + tie_setting(s.rot_deadzone, ui.rot_dz, [](const slider_value& s) { return tr("%1°").arg(s, 0, 'g', 4); }); + tie_setting(s.pos_deadzone, ui.trans_dz, [](const slider_value& s) { return tr("%1mm").arg(s); }); + + tie_setting(s.max_zoomed_smoothing, ui.max_zoomed_smoothing); + tie_setting(s.max_zoomed_smoothing, ui.lb_max_zoomed_smoothing, [](double x) + { return tr("%1°").arg(x, 0, 'g', 3);}); + + tie_setting(s.max_z, ui.max_z); + tie_setting(s.max_z, ui.lb_max_z, [](double x) + { return tr("%1mm").arg(x, 0, 'g', 3);}); + +//#define SPLINE_ROT_DEBUG +//#define SPLINE_TRANS_DEBUG + +#if defined SPLINE_ROT_DEBUG || defined SPLINE_TRANS_DEBUG + { + spline rot, pos; + s.make_splines(rot, pos); + +#ifdef SPLINE_ROT_DEBUG + QDialog dr; + spline_widget r(&dr); + dr.setWindowTitle("Accela rotation gain"); r.set_preview_only(true); r.setEnabled(true); + r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); r.set_config(&rot); + r.setFixedSize(1024, 600); + dr.show(); + dr.exec(); +#endif + +#ifdef SPLINE_TRANS_DEBUG + QDialog dt; + spline_widget t(&dt); + dt.setWindowTitle("Accela translation gain"); t.set_preview_only(true); t.setEnabled(true); + dt.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); t.set_config(&pos); + t.setFixedSize(1024, 600); + dt.show(); + dt.exec(); +#endif + } +#endif +} + +void dialog_accela_hamilton::doOK() +{ + save(); + close(); +} + +void dialog_accela_hamilton::doCancel() +{ + close(); +} + +void dialog_accela_hamilton::save() +{ + s.b->save(); +} + +void dialog_accela_hamilton::reload() +{ + s.b->reload(); +} + +void dialog_accela_hamilton::set_buttons_visible(bool x) +{ + ui.buttonBox->setVisible(x); +} diff --git a/filter-accela-hamilton/accela_hamilton_filtercontrols.ui b/filter-accela-hamilton/accela_hamilton_filtercontrols.ui new file mode 100644 index 00000000..f6049444 --- /dev/null +++ b/filter-accela-hamilton/accela_hamilton_filtercontrols.ui @@ -0,0 +1,485 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AccelaUICdialog_accela</class> + <widget class="QWidget" name="AccelaUICdialog_accela"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>632</width> + <height>628</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>550</width> + <height>0</height> + </size> + </property> + <property name="windowTitle"> + <string>Filter settings</string> + </property> + <property name="windowIcon"> + <iconset resource="../gui/opentrack-res.qrc"> + <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <property name="topMargin"> + <number>5</number> + </property> + <item row="0" column="0"> + <widget class="QTextBrowser" name="textBrowser"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>130</height> + </size> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustToContents</enum> + </property> + <property name="html"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Accela + Hamilton Filter</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For both rotations and positions: No movement occurs within the dead zone.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.</p></body></html></string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Rotation Smoothing (Added when zoomed)</string> + </property> + <layout class="QGridLayout" name="gridLayout_5"> + <property name="topMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="horizontalSpacing"> + <number>9</number> + </property> + <property name="verticalSpacing"> + <number>2</number> + </property> + <item row="1" column="2"> + <widget class="QSlider" name="max_z"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>25</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Max Added</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QSlider" name="max_zoomed_smoothing"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>49</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + <property name="tickInterval"> + <number>40</number> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="lb_max_zoomed_smoothing"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0° </string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="lb_max_z"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0mm</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Max Z</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="5" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Position filtering (X, Y, Z - translation)</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="topMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="horizontalSpacing"> + <number>9</number> + </property> + <property name="verticalSpacing"> + <number>2</number> + </property> + <item row="1" column="2"> + <widget class="QSlider" name="trans_dz_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>20</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Smoothing</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QSlider" name="translation_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>29</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + <property name="tickInterval"> + <number>40</number> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="trans_gain"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0mm</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="trans_dz"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0mm</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Deadzone</string> + </property> + </widget> + </item> + </layout> + <zorder>trans_dz_slider</zorder> + <zorder>translation_slider</zorder> + <zorder>trans_dz</zorder> + <zorder>label_6</zorder> + <zorder>trans_gain</zorder> + <zorder>label</zorder> + </widget> + </item> + <item row="1" column="0"> + <widget class="QGroupBox" name="groupBox44"> + <property name="title"> + <string>Rotation filtering (Yaw, pitch, and roll)</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="topMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="horizontalSpacing"> + <number>9</number> + </property> + <property name="verticalSpacing"> + <number>2</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="lblSensYaw_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Smoothing</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="rot_gain"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0°</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QSlider" name="rotation_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>49</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + <property name="tickInterval"> + <number>50</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Deadzone</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="rot_dz"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0°</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QSlider" name="rot_dz_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>20</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="4" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>rotation_slider</tabstop> + <tabstop>rot_dz_slider</tabstop> + <tabstop>translation_slider</tabstop> + <tabstop>trans_dz_slider</tabstop> + </tabstops> + <resources> + <include location="../gui/opentrack-res.qrc"/> + </resources> + <connections/> + <slots> + <slot>startEngineClicked()</slot> + <slot>stopEngineClicked()</slot> + <slot>cameraSettingsClicked()</slot> + </slots> +</ui> diff --git a/filter-accela-hamilton/accela_hamilton_settings.hpp b/filter-accela-hamilton/accela_hamilton_settings.hpp new file mode 100644 index 00000000..71ddf479 --- /dev/null +++ b/filter-accela-hamilton/accela_hamilton_settings.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "spline/spline.hpp" +#include "options/options.hpp" + +namespace detail::accela_hamilton { + +using namespace options; + +// ------------------------------------ +// debug knobs +// ------------------------------------ + +//#define DEBUG_ACCELA +//#define SPLINE_ROT_DEBUG +//#define SPLINE_TRANS_DEBUG + +struct settings_accela_hamilton : opts +{ + struct gains + { + double x, y; + }; + + static constexpr gains const rot_gains[] { + { 9, 300 }, + { 8, 200 }, + { 5, 100 }, + { 2.5, 35 }, + { 1.5, 8 }, + { 1, 1.5 }, + { .5, .4 }, + }; + + static constexpr gains const pos_gains[] { + { 9, 200 }, + { 8, 150 }, + { 7, 110 }, + { 5, 60 }, + { 3, 24 }, + { 2, 7.5 }, + { 1.66, 4.5 }, + { 1.33, 2.25 }, + { .66, .75 }, + { .33, .375 }, + { 0, 0 }, + }; + + static void make_splines(spline& rot, spline& pos); + + value<slider_value> rot_smoothing { b, "rotation-sensitivity", { 1.5, .05, 2.5 } }, + pos_smoothing { b, "translation-sensitivity", { 1., .05, 1.5 } }, + rot_deadzone { b, "rotation-deadzone", { .03, 0, .2 } }, + pos_deadzone { b, "translation-deadzone", { .1, 0, 1 } }, + max_zoomed_smoothing { b, "max_zoomed_smoothing", { .0, .0, 10. } }, + max_z { b, "max-z", { 10., .0, 30. } }; + + settings_accela_hamilton() : opts("accela-hamilton-sliders") {} +}; + +} // ns detail::accela_hamilton + +using detail::accela_hamilton::settings_accela_hamilton; diff --git a/filter-accela-hamilton/lang/nl_NL.ts b/filter-accela-hamilton/lang/nl_NL.ts new file mode 100644 index 00000000..13315922 --- /dev/null +++ b/filter-accela-hamilton/lang/nl_NL.ts @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nl_NL"> +<context> + <name>AccelaUICdialog_accela</name> + <message> + <source>Filter settings</source> + <translation type="unfinished">Filter-instellingen</translation> + </message> + <message> + <source>Smoothing</source> + <translation type="unfinished">Verzachten</translation> + </message> + <message> + <source>Position filtering (X, Y, Z - translation)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0mm</source> + <translation type="unfinished">0mm</translation> + </message> + <message> + <source>Deadzone</source> + <translation type="unfinished">Deadzone</translation> + </message> + <message> + <source>Rotation filtering (Yaw, pitch, and roll)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation Smoothing (Added when zoomed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0° </source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Z</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Added</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Accela + Hamilton Filter</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For both rotations and positions: No movement occurs within the dead zone.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>accela_hamiltonDll</name> + <message> + <source>AccelaHamilton</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>dialog_accela_hamilton</name> + <message> + <source>%1°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>%1mm</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/filter-accela-hamilton/lang/ru_RU.ts b/filter-accela-hamilton/lang/ru_RU.ts new file mode 100644 index 00000000..7dd3e1eb --- /dev/null +++ b/filter-accela-hamilton/lang/ru_RU.ts @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>AccelaUICdialog_accela</name> + <message> + <source>Filter settings</source> + <translation>Настройка фильтра</translation> + </message> + <message> + <source>Smoothing</source> + <translation>Сглаживание</translation> + </message> + <message> + <source>Position filtering (X, Y, Z - translation)</source> + <translation>Фильтрация смещений (X, Y, Z)</translation> + </message> + <message> + <source>0mm</source> + <translation>0мм</translation> + </message> + <message> + <source>Deadzone</source> + <translation>Мертвая зона</translation> + </message> + <message> + <source>Rotation filtering (Yaw, pitch, and roll)</source> + <translation>Фильтрация поворотов (Рысканье, тангаж, крен)</translation> + </message> + <message> + <source>0°</source> + <translation></translation> + </message> + <message> + <source>Rotation Smoothing (Added when zoomed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0° </source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Z</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Added</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Accela + Hamilton Filter</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For both rotations and positions: No movement occurs within the dead zone.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>accela_hamiltonDll</name> + <message> + <source>AccelaHamilton</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>dialog_accela_hamilton</name> + <message> + <source>%1°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>%1mm</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/filter-accela-hamilton/lang/stub.ts b/filter-accela-hamilton/lang/stub.ts new file mode 100644 index 00000000..2cd62df5 --- /dev/null +++ b/filter-accela-hamilton/lang/stub.ts @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>AccelaUICdialog_accela</name> + <message> + <source>Filter settings</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Smoothing</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Position filtering (X, Y, Z - translation)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0mm</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Deadzone</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation filtering (Yaw, pitch, and roll)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation Smoothing (Added when zoomed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0° </source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Z</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Added</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Accela + Hamilton Filter</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For both rotations and positions: No movement occurs within the dead zone.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>accela_hamiltonDll</name> + <message> + <source>AccelaHamilton</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>dialog_accela_hamilton</name> + <message> + <source>%1°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>%1mm</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/filter-accela-hamilton/lang/zh_CN.ts b/filter-accela-hamilton/lang/zh_CN.ts new file mode 100644 index 00000000..43a12f5a --- /dev/null +++ b/filter-accela-hamilton/lang/zh_CN.ts @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="zh_CN"> +<context> + <name>AccelaUICdialog_accela</name> + <message> + <source>Filter settings</source> + <translation>过滤器设置</translation> + </message> + <message> + <source>Rotation filtering (Yaw, pitch, and roll)</source> + <translation>旋转过滤器 (偏航, 俯仰, 滚转)</translation> + </message> + <message> + <source>Smoothing</source> + <translation type="unfinished">平滑</translation> + </message> + <message> + <source>0°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Deadzone</source> + <translation>死区</translation> + </message> + <message> + <source>Position filtering (X, Y, Z - translation)</source> + <translation>方位过滤器 (X, Y, Z - 变换)</translation> + </message> + <message> + <source>0mm</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation Smoothing (Added when zoomed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>0° </source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Z</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Max Added</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Accela + Hamilton Filter</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For both rotations and positions: No movement occurs within the dead zone.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>accela_hamiltonDll</name> + <message> + <source>AccelaHamilton</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>dialog_accela_hamilton</name> + <message> + <source>%1°</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>%1mm</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> |