From 89c346c1b1a7d0d395d446a2113ff5e6771441fe Mon Sep 17 00:00:00 2001 From: Tom Brazier Date: Mon, 5 Jun 2023 15:53:14 +0100 Subject: Changed nm filter to use vectors and quaterions like the hamilton filter for a spherical deadzone --- filter-nm/ftnoir_filter_nm.cpp | 43 ++-- filter-nm/ftnoir_filter_nm.h | 32 ++- filter-nm/ftnoir_filter_nm_dialog.cpp | 48 +--- filter-nm/ftnoir_nm_filtercontrols.ui | 400 ++++------------------------------ filter-nm/lang/nl_NL.ts | 36 +-- filter-nm/lang/ru_RU.ts | 36 +-- filter-nm/lang/stub.ts | 36 +-- filter-nm/lang/zh_CN.ts | 36 +-- 8 files changed, 136 insertions(+), 531 deletions(-) diff --git a/filter-nm/ftnoir_filter_nm.cpp b/filter-nm/ftnoir_filter_nm.cpp index cd395f1b..5d274790 100644 --- a/filter-nm/ftnoir_filter_nm.cpp +++ b/filter-nm/ftnoir_filter_nm.cpp @@ -19,32 +19,47 @@ filter_nm::filter_nm() void filter_nm::filter(const double* input, double* output) { + tVector position = { input[TX], input[TY], input[TZ] }; + tQuat rotation = QuatFromYPR(input + Yaw); + // order of axes: x, y, z, yaw, pitch, roll if (unlikely(first_run)) { first_run = false; t.start(); - std::fill(speeds, speeds + 6, 0.0); - std::copy(input, input + 6, filtered_output); + last_pos_speed = tVector(); + last_rot_speed = tQuat(); + last_pos_out = position; + last_rot_out = rotation; } else { const double dt = t.elapsed_seconds(); t.start(); - for (int i = 0; i < 6; i++) - { - double speed = (input[i] - last_input[i]) / dt; - double timescale = 1. / *(s.responsiveness[i]); - double alpha = dt / (dt + timescale); - speeds[i] += alpha * (speed - speeds[i]); // EWA - filtered_output[i] += alpha * min(1.0, abs(speeds[i]) / *(s.drift_speeds[i])) * (input[i] - filtered_output[i]); - } - } - - std::copy(input, input + 6, last_input); - std::copy(filtered_output, filtered_output + 6, output); + const tVector pos_speed = (position - last_pos_in) / dt; + const double pos_tau = 1. / *s.pos_responsiveness; + double alpha = dt / (dt + pos_tau); + last_pos_speed += (pos_speed - last_pos_speed) * alpha; + alpha *= min(1.0, VectorLength(last_pos_speed) / *s.pos_drift_speed); + last_pos_out += (position - last_pos_out) * alpha; + + const tQuat rot_delta = QuatDivide(rotation, last_rot_in); + const double ms_per_s = 1000.0; // angular speed quaternions need to be small to work so use °/ms + const tQuat rot_speed = Slerp(tQuat(), rot_delta, 1.0 / dt / ms_per_s); + const double rot_tau = 1. / *s.rot_responsiveness; + alpha = dt / (dt + rot_tau); + last_rot_speed = Slerp(last_rot_speed, rot_speed, alpha); + const double angular_speed = AngleBetween(tQuat(), last_rot_speed) * ms_per_s; + alpha *= min(1.0, angular_speed / *s.rot_drift_speed); + last_rot_out = Slerp(last_rot_out, rotation, alpha); + } + + last_pos_in = position; + last_rot_in = rotation; + std::copy(last_pos_out.v, last_pos_out.v + 3, output + TX); + QuatToYPR(last_rot_out, &output[Yaw]); } OPENTRACK_DECLARE_FILTER(filter_nm, dialog_nm, nmDll) diff --git a/filter-nm/ftnoir_filter_nm.h b/filter-nm/ftnoir_filter_nm.h index d2eae43d..7bf012a6 100644 --- a/filter-nm/ftnoir_filter_nm.h +++ b/filter-nm/ftnoir_filter_nm.h @@ -10,29 +10,24 @@ #include "api/plugin-api.hpp" #include "compat/timer.hpp" +#include "compat/hamilton-tools.h" #include "options/options.hpp" using namespace options; struct settings_nm : opts { - value responsiveness[6]; - value drift_speeds[6]; + value pos_responsiveness; + value rot_responsiveness; + value pos_drift_speed; + value rot_drift_speed; settings_nm() : opts("nm-filter"), - responsiveness{ value(b, "x-responsiveness", { 10.0, .0, 20.0 }), - value(b, "y-responsiveness", { 10.0, .0, 20.0 }), - value(b, "z-responsiveness", { 10.0, .0, 20.0 }), - value(b, "yaw-responsiveness", { 10.0, .0, 20.0 }), - value(b, "pitch-responsiveness", { 10.0, .0, 20.0 }), - value(b, "roll-responsiveness", { 10.0, .0, 20.0 }) }, - drift_speeds{ value(b, "x-drift-speed", { 50.0, 1.0, 200.0 }), - value(b, "y-drift-speed", { 50.0, 1.0, 200.0 }), - value(b, "z-drift-speed", { 50.0, 1.0, 200.0 }), - value(b, "yaw-drift-speed", { 100.0, 1.0, 400.0 }), - value(b, "pitch-drift-speed", { 100.0, 1.0, 400.0 }), - value(b, "roll-drift-speed", { 100.0, 1.0, 400.0 }) } + pos_responsiveness(value(b, "pos-responsiveness", { 15.0, .0, 20.0 })), + rot_responsiveness(value(b, "rot-responsiveness", { 18.0, .0, 20.0 })), + pos_drift_speed(value(b, "pos-drift-speed", { 30.0, 1.0, 200.0 })), + rot_drift_speed(value(b, "rot-drift-speed", { 45.0, 1.0, 400.0 })) { } }; @@ -45,9 +40,12 @@ struct filter_nm : IFilter module_status initialize() override { return status_ok(); } private: - double last_input[6]{}; - double speeds[6]{}; - double filtered_output[6]{}; + tVector last_pos_in; + tQuat last_rot_in; + tVector last_pos_out; + tQuat last_rot_out; + tVector last_pos_speed; + tQuat last_rot_speed; Timer t; settings_nm s; bool first_run = true; diff --git a/filter-nm/ftnoir_filter_nm_dialog.cpp b/filter-nm/ftnoir_filter_nm_dialog.cpp index ccf894db..f3626cae 100644 --- a/filter-nm/ftnoir_filter_nm_dialog.cpp +++ b/filter-nm/ftnoir_filter_nm_dialog.cpp @@ -12,52 +12,20 @@ dialog_nm::dialog_nm() { ui.setupUi(this); - tie_setting(s.responsiveness[0], ui.x_responsiveness_slider); - tie_setting(s.responsiveness[0], ui.x_responsiveness, [](double x) + tie_setting(s.pos_responsiveness, ui.pos_responsiveness_slider); + tie_setting(s.pos_responsiveness, ui.pos_responsiveness, [](double x) { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - tie_setting(s.responsiveness[1], ui.y_responsiveness_slider); - tie_setting(s.responsiveness[1], ui.y_responsiveness, [](double x) + tie_setting(s.rot_responsiveness, ui.rot_responsiveness_slider); + tie_setting(s.rot_responsiveness, ui.rot_responsiveness, [](double x) { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - tie_setting(s.responsiveness[2], ui.z_responsiveness_slider); - tie_setting(s.responsiveness[2], ui.z_responsiveness, [](double x) + tie_setting(s.pos_drift_speed, ui.pos_drift_speed_slider); + tie_setting(s.pos_drift_speed, ui.pos_drift_speed, [](double x) { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - tie_setting(s.responsiveness[3], ui.yaw_responsiveness_slider); - tie_setting(s.responsiveness[3], ui.yaw_responsiveness, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.responsiveness[4], ui.pitch_responsiveness_slider); - tie_setting(s.responsiveness[4], ui.pitch_responsiveness, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.responsiveness[5], ui.roll_responsiveness_slider); - tie_setting(s.responsiveness[5], ui.roll_responsiveness, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.drift_speeds[0], ui.x_drift_speed_slider); - tie_setting(s.drift_speeds[0], ui.x_drift_speed, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.drift_speeds[1], ui.y_drift_speed_slider); - tie_setting(s.drift_speeds[1], ui.y_drift_speed, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.drift_speeds[2], ui.z_drift_speed_slider); - tie_setting(s.drift_speeds[2], ui.z_drift_speed, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.drift_speeds[3], ui.yaw_drift_speed_slider); - tie_setting(s.drift_speeds[3], ui.yaw_drift_speed, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.drift_speeds[4], ui.pitch_drift_speed_slider); - tie_setting(s.drift_speeds[4], ui.pitch_drift_speed, [](double x) - { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); - - tie_setting(s.drift_speeds[5], ui.roll_drift_speed_slider); - tie_setting(s.drift_speeds[5], ui.roll_drift_speed, [](double x) + tie_setting(s.rot_drift_speed, ui.rot_drift_speed_slider); + tie_setting(s.rot_drift_speed, ui.rot_drift_speed, [](double x) { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); } diff --git a/filter-nm/ftnoir_nm_filtercontrols.ui b/filter-nm/ftnoir_nm_filtercontrols.ui index 93f3cb91..ad307daf 100644 --- a/filter-nm/ftnoir_nm_filtercontrols.ui +++ b/filter-nm/ftnoir_nm_filtercontrols.ui @@ -20,15 +20,15 @@ Responsiveness - - + + - X + Rotation - + 0 @@ -49,31 +49,8 @@ - - - - - 0 - 0 - - - - 40 - - - Qt::Horizontal - - - - - - - Y - - - - + 30 @@ -89,7 +66,7 @@ - + 40 @@ -98,97 +75,14 @@ - - - - Z - - - - - - - - 30 - 0 - - - - 10.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 40 - - - Qt::Horizontal - - - - - - - Yaw - - - - - - - - 30 - 0 - - - - 10.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 40 - - - Qt::Horizontal - - - - - - - Pitch - - - - - - - - 30 - 0 - - - - 10.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + 0 + 0 + - - - - 40 @@ -197,36 +91,10 @@ - - - - Roll - - - - - - - - 30 - 0 - - + + - 10.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 40 - - - Qt::Horizontal + Position @@ -261,85 +129,28 @@ Drift speeds - - - - Z - - - - - - - 1 - - - 200 - - - Qt::Horizontal - - - - - - - Roll - - - - - - - - 40 - 0 - - - - 50 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - °/s - - - - - + + mm/s - - + + - °/s + Position - - - - 1 - - - 200 - - - Qt::Horizontal + + + + + 0 + 0 + - - - - 40 @@ -354,54 +165,8 @@ - - - - - 40 - 0 - - - - 100 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Y - - - - - - - - 40 - 0 - - - - 100 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Pitch - - - - - + + 1 @@ -413,15 +178,15 @@ - - + + - X + Rotation - - + + 40 @@ -436,29 +201,15 @@ - - - - mm/s - - - - - - - mm/s - - - - - + + - Yaw + °/s - + 0 @@ -476,61 +227,6 @@ - - - - °/s - - - - - - - 1 - - - 400 - - - Qt::Horizontal - - - - - - - 1 - - - 400 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 40 - 0 - - - - 50 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - @@ -547,7 +243,7 @@ - Instructions: Set all sliders to minimum. Then on an axis by axis basis: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. + Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -573,18 +269,10 @@ - x_responsiveness_slider - y_responsiveness_slider - z_responsiveness_slider - yaw_responsiveness_slider - pitch_responsiveness_slider - roll_responsiveness_slider - x_drift_speed_slider - y_drift_speed_slider - z_drift_speed_slider - yaw_drift_speed_slider - pitch_drift_speed_slider - roll_drift_speed_slider + pos_responsiveness_slider + rot_responsiveness_slider + pos_drift_speed_slider + rot_drift_speed_slider diff --git a/filter-nm/lang/nl_NL.ts b/filter-nm/lang/nl_NL.ts index d68712e9..540e2d51 100644 --- a/filter-nm/lang/nl_NL.ts +++ b/filter-nm/lang/nl_NL.ts @@ -7,30 +7,6 @@ Dialog - - X - - - - Y - - - - Z - - - - Yaw - - - - Pitch - - - - Roll - - °/s @@ -60,11 +36,19 @@ - Instructions: Set all sliders to minimum. Then on an axis by axis basis: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. + Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. - Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. + Rotation + + + + Position + + + + Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. diff --git a/filter-nm/lang/ru_RU.ts b/filter-nm/lang/ru_RU.ts index 9ea3d158..31e5d5cc 100644 --- a/filter-nm/lang/ru_RU.ts +++ b/filter-nm/lang/ru_RU.ts @@ -7,30 +7,6 @@ Dialog - - X - - - - Y - - - - Z - - - - Yaw - - - - Pitch - - - - Roll - - °/s @@ -60,11 +36,19 @@ - Instructions: Set all sliders to minimum. Then on an axis by axis basis: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. + Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. - Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. + Rotation + + + + Position + + + + Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. diff --git a/filter-nm/lang/stub.ts b/filter-nm/lang/stub.ts index 61c1f5bc..3b960804 100644 --- a/filter-nm/lang/stub.ts +++ b/filter-nm/lang/stub.ts @@ -7,30 +7,6 @@ Dialog - - X - - - - Y - - - - Z - - - - Yaw - - - - Pitch - - - - Roll - - °/s @@ -60,11 +36,19 @@ - Instructions: Set all sliders to minimum. Then on an axis by axis basis: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. + Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. - Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. + Rotation + + + + Position + + + + Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. diff --git a/filter-nm/lang/zh_CN.ts b/filter-nm/lang/zh_CN.ts index d6490d99..92521e0f 100644 --- a/filter-nm/lang/zh_CN.ts +++ b/filter-nm/lang/zh_CN.ts @@ -7,30 +7,6 @@ Dialog - - X - - - - Y - - - - Z - - - - Yaw - - - - Pitch - - - - Roll - - °/s @@ -60,11 +36,19 @@ - Instructions: Set all sliders to minimum. Then on an axis by axis basis: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. + Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. - Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still. + Rotation + + + + Position + + + + Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still. -- cgit v1.2.3