diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2016-09-20 22:21:32 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2016-09-20 23:24:19 +0200 |
commit | 8dfddcd7dba3752ab44fd9f30cc1533561aabe75 (patch) | |
tree | 93d5b61936c3d1878b1da43ea10176af61a8532d | |
parent | e258767f32bbafef9f862209aa8868342e946975 (diff) |
api/tracker: merge from unstable
- fix gimbal lock
- use right Tait-Bryan conversion to/from matrix, not generic
inapplicable euler conversion
- adjust for timer inaccuracy, therefore running at constant Hz
that's actually the specified Hz
-rw-r--r-- | opentrack-compat/nan.cpp | 17 | ||||
-rw-r--r-- | opentrack-compat/nan.hpp | 11 | ||||
-rwxr-xr-x | opentrack/tracker.cpp | 264 | ||||
-rw-r--r-- | opentrack/tracker.h | 132 |
4 files changed, 272 insertions, 152 deletions
diff --git a/opentrack-compat/nan.cpp b/opentrack-compat/nan.cpp new file mode 100644 index 00000000..899b907a --- /dev/null +++ b/opentrack-compat/nan.cpp @@ -0,0 +1,17 @@ +#include <cmath> +#include "export.hpp" + +#if defined(__GNUC__) +extern "C" OPENTRACK_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value) +#elif defined(_WIN32) +extern "C" OPENTRACK_COMPAT_EXPORT __declspec(noinline) bool nanp(double value) +#else +extern "C" OPENTRACK_COMPAT_EXPORT bool nanp(double value) +#endif +{ + using std::isnan; + using std::isinf; + + const volatile double x = value; + return isnan(x) || isinf(x); +} diff --git a/opentrack-compat/nan.hpp b/opentrack-compat/nan.hpp new file mode 100644 index 00000000..9926da13 --- /dev/null +++ b/opentrack-compat/nan.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "export.hpp" + +#if defined(__GNUC__) +extern "C" OPENTRACK_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value); +#elif defined(_WIN32) +extern "C" OPENTRACK_COMPAT_EXPORT __declspec(noinline) bool nanp(double value); +#else +extern "C" OPENTRACK_COMPAT_EXPORT bool nanp(double value); +#endif diff --git a/opentrack/tracker.cpp b/opentrack/tracker.cpp index 8313c8fc..d37aea93 100755 --- a/opentrack/tracker.cpp +++ b/opentrack/tracker.cpp @@ -12,35 +12,44 @@ * originally written by Wim Vriend. */ - +#include "opentrack-compat/sleep.hpp" +#include "opentrack-compat/nan.hpp" #include "tracker.h" + #include <cmath> #include <algorithm> +#include <cstdio> #if defined(_WIN32) # include <windows.h> #endif -Tracker::Tracker(main_settings& s, Mappings &m, SelectedLibraries &libs) : +Tracker::Tracker(main_settings& s, Mappings& m, SelectedLibraries& libs) : s(s), m(m), - newpose {0,0,0, 0,0,0}, - centerp(s.center_at_startup), - enabledp(true), - zero_(false), - should_quit(false), - libs(libs), - r_b(dmat<3,3>::eye()), - t_b {0,0,0} + libs(libs) { + set(f_center, s.center_at_startup); } Tracker::~Tracker() { - should_quit = true; + set(f_should_quit, true); wait(); } +Tracker::rmat Tracker::get_camera_offset_matrix(double c) +{ + const double off[] = + { + d2r * c * (double)-s.camera_yaw, + d2r * c * (double)-s.camera_pitch, + d2r * c * (double)-s.camera_roll + }; + + return euler::euler_to_rmat(off); +} + double Tracker::map(double pos, Mapping& axis) { bool altp = (pos < 0) && axis.opts.altp; @@ -50,22 +59,16 @@ double Tracker::map(double pos, Mapping& axis) return fc.getValue(pos); } -void Tracker::t_compensate(const rmat& rmat, const double* xyz, double* output, bool rz) +void Tracker::t_compensate(const rmat& rmat, const euler_t& xyz_, euler_t& output, bool rz) { // TY is really yaw axis. need swapping accordingly. - dmat<3, 1> tvec( xyz[2], -xyz[0], -xyz[1] ); - const dmat<3, 1> ret = rmat * tvec; + const euler_t ret = rmat * euler_t(xyz_(TZ), -xyz_(TX), -xyz_(TY)); if (!rz) - output[2] = ret(0); + output(2) = ret(0); else - output[2] = xyz[2]; - output[1] = -ret(2); - output[0] = -ret(1); -} - -static inline bool nanp(double value) -{ - return std::isnan(value) || std::isinf(value); + output(2) = xyz_(2); + output(1) = -ret(2); + output(0) = -ret(1); } static inline double elide_nan(double value, double def) @@ -79,41 +82,23 @@ static inline double elide_nan(double value, double def) return value; } -static bool is_nan(const dmat<3,3>& r, const dmat<3, 1>& t) +template<int u, int w> +static bool is_nan(const dmat<u,w>& r) { - for (int i = 0; i < 3; i++) - for (int j = 0; j < 3; j++) - if (nanp(r(i, j))) + for (int i = 0; i < u; i++) + for (int j = 0; j < w; j++) + if (nanp(r(u, w))) return true; - for (int i = 0; i < 3; i++) - if (nanp(t(i))) - return true; - return false; } -static bool is_nan(const Pose& value) -{ - for (int i = 0; i < 6; i++) - if (nanp(value(i))) - return true; - return false; -} +constexpr double Tracker::c_mult; +constexpr double Tracker::c_div; void Tracker::logic() { - bool inverts[6] = { - m(0).opts.invert, - m(1).opts.invert, - m(2).opts.invert, - m(3).opts.invert, - m(4).opts.invert, - m(5).opts.invert, - }; - - static constexpr double pi = 3.141592653; - static constexpr double r2d = 180. / pi; + using namespace euler; Pose value, raw; @@ -124,93 +109,132 @@ void Tracker::logic() if (k < 0 || k >= 6) value(i) = 0; else - value(i) = newpose[k]; - raw(i) = newpose[i]; + value(i) = newpose(k); + raw(i) = newpose(i); } if (is_nan(raw)) raw = last_raw; - const double off[] = { - (double)-s.camera_yaw, - (double)-s.camera_pitch, - (double)s.camera_roll - }; - const rmat cam = rmat::euler_to_rmat(off); - rmat r = rmat::euler_to_rmat(&value[Yaw]); - dmat<3, 1> t(value(0), value(1), value(2)); - - r = cam * r; - - bool can_center = false; - const bool nan = is_nan(r, t); + // TODO split this function, it's too big - if (centerp && !nan) { - for (int i = 0; i < 6; i++) - if (fabs(newpose[i]) != 0) - { - can_center = true; - break; - } + euler_t tmp = d2r * euler_t(&value[Yaw]); + scaled_rotation.rotation = euler_to_rmat(c_div * tmp); + real_rotation.rotation = euler_to_rmat(tmp); + tait_bryan_to_matrices(c_div * tmp, scaled_rotation.rr, scaled_rotation.ry, scaled_rotation.rp); } - if (can_center) + scaled_rotation.camera = get_camera_offset_matrix(c_div); + real_rotation.camera = get_camera_offset_matrix(1); + + scaled_rotation.rotation = scaled_rotation.camera * scaled_rotation.rotation; + real_rotation.rotation = real_rotation.camera * real_rotation.rotation; + + bool nanp = is_nan(value) || is_nan(scaled_rotation.rotation) || is_nan(real_rotation.rotation); + + if (!nanp) { - if (libs.pFilter) - libs.pFilter->center(); - centerp = false; - for (int i = 0; i < 3; i++) - t_b[i] = t(i); - r_b = r; + bool can_center = false; + + if (get(f_center) && !nanp) + { + using std::fabs; + + for (int i = 0; i < 6; i++) + if (fabs(newpose(i)) != 0) + { + can_center = true; + break; + } + } + + if (can_center) + { + set(f_center, false); + + if (libs.pFilter) + libs.pFilter->center(); + + euler::tait_bryan_to_matrices(rmat_to_euler(scaled_rotation.rotation), + scaled_rotation.center_roll, + scaled_rotation.center_pitch, + scaled_rotation.center_yaw); +#if 0 + euler::tait_bryan_to_matrices(rmat_to_euler(real_rotation.rotation), + real_rotation.center_roll, + real_rotation.center_pitch, + real_rotation.center_yaw); +#endif + real_rotation.rot_center = real_rotation.rotation.t(); + scaled_rotation.rot_center = scaled_rotation.rotation.t(); + + t_center = euler_t(&value(TX)); + } } { - double tmp[3] = { t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2] }; - t_compensate(cam, tmp, tmp, false); + rmat rotation; + + rotation = scaled_rotation.rotation * scaled_rotation.rot_center; + + const euler_t rot = r2d * c_mult * rmat_to_euler(rotation); + euler_t pos = euler_t(&value[TX]) - t_center; + + t_compensate(real_rotation.camera.t(), pos, pos, false); - const dmat<3, 1> euler = rmat::rmat_to_euler(r * r_b.t()); for (int i = 0; i < 3; i++) { - value(i) = tmp[i]; - value(i+3) = euler(i) * r2d; + value(i) = pos(i); + value(i+3) = rot(i); } } - bool nan_ = false; - // we're checking NaNs after every block of numeric ops + // whenever something can corrupt its internal state due to nan/inf, elide the call if (is_nan(value)) { - nan_ = true; + nanp = true; } else { - Pose tmp = value; + Pose tmp(value); if (libs.pFilter) libs.pFilter->filter(tmp, value); - for (int i = 0; i < 6; i++) + // CAVEAT rotation only, due to tcomp + for (int i = 3; i < 6; i++) value(i) = map(value(i), m(i)); - if (s.tcomp_p) - t_compensate(rmat::euler_to_rmat(&value[Yaw]), - value, - value, - s.tcomp_tz); - - for (int i = 0; i < 6; i++) - value[i] *= inverts[i] ? -1. : 1.; - - if (zero_) + if (get(f_zero)) for (int i = 0; i < 6; i++) value(i) = 0; if (is_nan(value)) - nan_ = true; + nanp = true; + } + + if (s.tcomp_p && !get(f_tcomp_disabled)) + { + euler_t value_(value(TX), value(TY), value(TZ)); + t_compensate(euler_to_rmat(euler_t(value(Yaw) * d2r, value(Pitch) * d2r, value(Roll) * d2r)), + value_, + value_, + s.tcomp_tz); + if (is_nan(value_)) + nanp = true; + for (int i = 0; i < 3; i++) + value(i) = value_(i); } - if (nan_) + // CAVEAT translation only, due to tcomp + for (int i = 0; i < 3; i++) + value(i) = map(value(i), m(i)); + + for (int i = 0; i < 6; i++) + value(i) *= int(m(i).opts.invert) * -2 + 1; + + if (nanp) { value = last_mapped; @@ -229,28 +253,40 @@ void Tracker::logic() raw_6dof = raw; } -void Tracker::run() { - const int sleep_ms = 3; - +void Tracker::run() +{ #if defined(_WIN32) (void) timeBeginPeriod(1); #endif - while (!should_quit) - { - t.start(); + setPriority(QThread::HighPriority); + + t.start(); - double tmp[6] {0,0,0, 0,0,0}; + while (!get(f_should_quit)) + { + Pose tmp; libs.pTracker->data(tmp); - - if (enabledp) + + if (get(f_enabled)) for (int i = 0; i < 6; i++) - newpose[i] = elide_nan(tmp[i], newpose[i]); + newpose[i] = elide_nan(tmp(i), newpose(i)); logic(); - long q = sleep_ms * 1000L - t.elapsed()/1000L; - usleep(std::max(1L, q)); + static constexpr long const_sleep_us = 4000; + + using std::max; + using std::min; + + const long elapsed_usecs = t.elapsed_usecs(); + const long sleep_us = const_sleep_us * 2 - elapsed_usecs; + + const unsigned sleep_time = unsigned(max(1l, min(const_sleep_us * 4, max(1l, (sleep_us + 200l)/1000l)))); + + t.start(); + + portable::sleep(unsigned(max(1u, sleep_time))); } { @@ -270,8 +306,10 @@ void Tracker::run() { } } -void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const { +void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const +{ QMutexLocker foo(&const_cast<Tracker&>(*this).mtx); + for (int i = 0; i < 6; i++) { raw[i] = raw_6dof(i); diff --git a/opentrack/tracker.h b/opentrack/tracker.h index 36b5cad4..06a90305 100644 --- a/opentrack/tracker.h +++ b/opentrack/tracker.h @@ -8,10 +8,8 @@ #pragma once -#include <vector> - #include "opentrack-compat/timer.hpp" -#include "plugin-support.hpp" +#include "opentrack/plugin-support.hpp" #include "mappings.hpp" #include "simple-mat.hpp" #include "selected-libraries.hpp" @@ -23,32 +21,70 @@ #include <QMutex> #include <QThread> -class Pose { -private: - static constexpr double pi = 3.141592653; - static constexpr double d2r = pi/180.0; - static constexpr double r2d = 180./pi; - - double axes[6]; -public: - Pose() : axes {0,0,0, 0,0,0} {} - - inline operator double*() { return axes; } - inline operator const double*() const { return axes; } +#include <atomic> +#include <vector> - inline double& operator()(int i) { return axes[i]; } - inline double operator()(int i) const { return axes[i]; } +#include "export.hpp" + +using Pose = Mat<double, 6, 1>; + +struct bits +{ + enum flags { + f_center = 1 << 0, + f_enabled = 1 << 1, + f_zero = 1 << 2, + f_tcomp_disabled = 1 << 3, + f_should_quit = 1 << 4, + }; + + std::atomic<unsigned> b; + + void set(flags flag_, bool val_) + { + unsigned b_(b); + const unsigned flag = unsigned(flag_); + const unsigned val = unsigned(!!val_); + while (!b.compare_exchange_weak(b_, + unsigned((b_ & ~flag) | (flag * val)), + std::memory_order_seq_cst, + std::memory_order_seq_cst)) + { /* empty */ } + } + + void negate(flags flag_) + { + unsigned b_(b); + const unsigned flag = unsigned(flag_); + while (!b.compare_exchange_weak(b_, + (b_ & ~flag) | (flag & ~b_), + std::memory_order_seq_cst, + std::memory_order_seq_cst)) + { /* empty */ } + } + + bool get(flags flag) + { + return !!(b & flag); + } + + bits() : b(0u) + { + set(f_center, true); + set(f_enabled, true); + set(f_zero, false); + set(f_tcomp_disabled, false); + set(f_should_quit, false); + } }; -#ifdef BUILD_api -# include "opentrack-compat/export.hpp" -#else -# include "opentrack-compat/import.hpp" -#endif - -class OPENTRACK_EXPORT Tracker : private QThread { +class OPENTRACK_API_EXPORT Tracker : private QThread, private bits +{ Q_OBJECT private: + using rmat = euler::rmat; + using euler_t = euler::euler_t; + QMutex mtx; main_settings& s; Mappings& m; @@ -56,31 +92,49 @@ private: Timer t; Pose output_pose, raw_6dof, last_mapped, last_raw; - double newpose[6]; - volatile bool centerp; - volatile bool enabledp; - volatile bool zero_; - volatile bool should_quit; + Pose newpose; SelectedLibraries const& libs; - using rmat = dmat<3, 3>; - - dmat<3, 3> r_b; - double t_b[3]; + struct state + { + rmat center_yaw, center_pitch, center_roll; + rmat rot_center; + rmat camera; + rmat rotation, ry, rp, rr; + + state() : center_yaw(rmat::eye()), center_pitch(rmat::eye()), center_roll(rmat::eye()), rot_center(rmat::eye()) + {} + }; + + state real_rotation, scaled_rotation; + euler_t t_center; double map(double pos, Mapping& axis); void logic(); - - void t_compensate(const dmat<3, 3>& rmat, const double* ypr, double* output, bool rz); + void t_compensate(const rmat& rmat, const euler_t& ypr, euler_t& output, bool rz); void run() override; - + + static constexpr double pi = M_PI; + static constexpr double r2d = 180. / M_PI; + static constexpr double d2r = M_PI / 180.; + + // note: float exponent base is 2 + static constexpr double c_mult = 4; + static constexpr double c_div = 1./c_mult; public: Tracker(main_settings& s, Mappings& m, SelectedLibraries& libs); ~Tracker(); + rmat get_camera_offset_matrix(double c); void get_raw_and_mapped_poses(double* mapped, double* raw) const; void start() { QThread::start(); } - void toggle_enabled() { qDebug() << "toggle enabled"; enabledp = !enabledp; } - void center() { qDebug() << "toggle center"; centerp = !centerp; } - void zero() { qDebug() << "toggle zero"; zero_ = !zero_; } + + void center() { set(f_center, true); } + + void set_toggle(bool value) { set(f_enabled, value); } + void set_zero(bool value) { set(f_zero, value); } + void set_tcomp_disabled(bool x) { set(f_tcomp_disabled, x); } + + void zero() { negate(f_zero); } + void toggle_enabled() { negate(f_enabled); } }; |