summaryrefslogtreecommitdiffhomepage
path: root/opentrack
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2016-09-20 22:21:32 +0200
committerStanislaw Halik <sthalik@misaki.pl>2016-09-20 23:24:19 +0200
commit8dfddcd7dba3752ab44fd9f30cc1533561aabe75 (patch)
tree93d5b61936c3d1878b1da43ea10176af61a8532d /opentrack
parente258767f32bbafef9f862209aa8868342e946975 (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
Diffstat (limited to 'opentrack')
-rwxr-xr-xopentrack/tracker.cpp264
-rw-r--r--opentrack/tracker.h132
2 files changed, 244 insertions, 152 deletions
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); }
};