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 cd395f1bf..5d274790e 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 d2eae43d6..7bf012a6b 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 ccf894db5..f3626caeb 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 93f3cb919..ad307daf1 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 d68712e95..540e2d51b 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 9ea3d1587..31e5d5ccb 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 61c1f5bcc..3b9608046 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 d6490d99e..92521e0fe 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