summaryrefslogtreecommitdiffhomepage
path: root/logic/tracker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'logic/tracker.cpp')
-rw-r--r--logic/tracker.cpp385
1 files changed, 385 insertions, 0 deletions
diff --git a/logic/tracker.cpp b/logic/tracker.cpp
new file mode 100644
index 00000000..c7580500
--- /dev/null
+++ b/logic/tracker.cpp
@@ -0,0 +1,385 @@
+/* Copyright (c) 2012-2015 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * 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.
+ */
+
+/*
+ * this file appeared originally in facetracknoir, was rewritten completely
+ * following opentrack fork.
+ *
+ * originally written by Wim Vriend.
+ */
+
+#include "compat/sleep.hpp"
+
+#include "tracker.h"
+#include <cmath>
+#include <algorithm>
+#include <cstdio>
+
+#if defined(_WIN32)
+# include <windows.h>
+#endif
+
+Tracker::Tracker(Mappings &m, SelectedLibraries &libs, TrackLogger &logger) :
+ m(m),
+ newpose {0,0,0, 0,0,0},
+ libs(libs),
+ logger(logger),
+ r_b(get_camera_offset_matrix(c_div).t()),
+ r_b_real(get_camera_offset_matrix(1).t()),
+ t_b {0,0,0},
+ centerp(s.center_at_startup),
+ enabledp(true),
+ zero_(false),
+ should_quit(false)
+{
+}
+
+Tracker::~Tracker()
+{
+ 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, Map& axis)
+{
+ bool altp = (pos < 0) && axis.opts.altp;
+ axis.spline_main.setTrackingActive( !altp );
+ axis.spline_alt.setTrackingActive( altp );
+ auto& fc = altp ? axis.spline_alt : axis.spline_main;
+ return fc.getValue(pos);
+}
+
+void Tracker::t_compensate(const rmat& rmat, const euler_t& xyz_, euler_t& output, bool rz)
+{
+ // TY is really yaw axis. need swapping accordingly.
+ const euler_t ret = rmat * euler_t(xyz_(TZ), -xyz_(TX), -xyz_(TY));
+ if (!rz)
+ output(2) = ret(0);
+ else
+ output(2) = xyz_(2);
+ output(1) = -ret(2);
+ output(0) = -ret(1);
+}
+
+#include "compat/nan.hpp"
+
+static inline double elide_nan(double value, double def)
+{
+ if (nanp(value))
+ {
+ if (nanp(def))
+ return 0;
+ return def;
+ }
+ return value;
+}
+
+static bool is_nan(const dmat<3,3>& r, const dmat<3, 1>& t)
+{
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ if (nanp(r(i, j)))
+ 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()
+{
+ using namespace euler;
+
+ logger.write_dt();
+ logger.reset_dt();
+
+ Pose value, raw;
+
+ for (int i = 0; i < 6; i++)
+ {
+ auto& axis = m(i);
+ int k = axis.opts.src;
+ if (k < 0 || k >= 6)
+ value(i) = 0;
+ else
+ value(i) = newpose[k];
+ raw(i) = newpose[i];
+ }
+
+ logger.write_pose(raw); // raw
+
+ if (is_nan(raw))
+ raw = last_raw;
+
+ // TODO fix gimbal lock by dividing euler angle input by >=3.
+ // maintain the real rmat separately for translation compensation
+ // TODO split this function, it's too big
+ rmat r, r_real;
+
+ {
+ euler_t tmp = d2r * euler_t(&value[Yaw]);
+ r = euler_to_rmat(c_div * tmp);
+ r_real = euler_to_rmat(tmp);
+ }
+
+ euler_t t(value(0), value(1), value(2));
+
+ bool do_center_now = false;
+ bool nan = is_nan(r, t);
+
+ if (centerp && !nan)
+ {
+ for (int i = 0; i < 6; i++)
+ if (fabs(newpose[i]) != 0)
+ {
+ do_center_now = true;
+ break;
+ }
+ }
+
+ const rmat cam = get_camera_offset_matrix(c_div);
+ const rmat cam_real = get_camera_offset_matrix(1);
+
+ r = r * cam;
+ r_real = r_real * cam_real;
+
+ if (do_center_now)
+ {
+ centerp = false;
+
+ if (libs.pFilter)
+ libs.pFilter->center();
+
+ if (libs.pTracker->center())
+ {
+ r_b = cam.t();
+ r_b_real = cam_real.t();
+ r = rmat::eye();
+ r_real = rmat::eye();
+ }
+ else
+ {
+ r_b = r.t();
+ r_b_real = r_real.t();
+ }
+
+ for (int i = 0; i < 3; i++)
+ t_b[i] = t(i);
+ }
+
+ {
+ switch (s.center_method)
+ {
+ // inertial
+ case 0:
+ default:
+ r = r_b * r;
+ break;
+ // camera
+ case 1:
+ r = r * r_b;
+ break;
+ }
+
+ const euler_t rot = r2d * c_mult * rmat_to_euler(r);
+ euler_t pos(t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2]);
+
+ if (s.use_camera_offset_from_centering)
+ t_compensate(r_b_real.t() * cam_real.t(), pos, pos, false);
+ else
+ t_compensate(cam_real.t(), pos, pos, false);
+
+ for (int i = 0; i < 3; i++)
+ {
+ value(i) = pos(i);
+ value(i+3) = rot(i);
+ }
+ }
+
+ logger.write_pose(value); // "corrected" - after various transformations to account for camera position
+
+ // whenever something can corrupt its internal state due to nan/inf, elide the call
+ if (is_nan(value))
+ {
+ nan = true;
+ logger.write_pose(value); // "filtered"
+ }
+ else
+ {
+ {
+ Pose tmp = value;
+
+ if (libs.pFilter)
+ libs.pFilter->filter(tmp, value);
+ }
+ logger.write_pose(value); // "filtered"
+
+ // CAVEAT rotation only, due to tcomp
+ for (int i = 3; i < 6; i++)
+ value(i) = map(value(i), m(i));
+
+ for (int i = 0; i < 6; i++)
+ value(i) += m(i).opts.zero;
+
+ for (int i = 0; i < 6; i++)
+ value(i) *= int(m(i).opts.invert) * -2 + 1;
+
+ if (zero_)
+ for (int i = 0; i < 6; i++)
+ value(i) = 0;
+
+ if (is_nan(value))
+ nan = true;
+ }
+
+ if (s.tcomp_p)
+ {
+ 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(r, value_))
+ nan = true;
+ for (int i = 0; i < 3; i++)
+ value(i) = value_(i);
+ }
+
+ // CAVEAT translation only, due to tcomp
+ for (int i = 0; i < 3; i++)
+ value(i) = map(value(i), m(i));
+
+ logger.write_pose(value); // "mapped"
+
+ if (nan)
+ {
+ value = last_mapped;
+
+ // for widget last value display
+ for (int i = 0; i < 6; i++)
+ (void) map(value(i), m(i));
+ }
+
+ libs.pProtocol->pose(value);
+
+ last_mapped = value;
+ last_raw = raw;
+
+ QMutexLocker foo(&mtx);
+ output_pose = value;
+ raw_6dof = raw;
+
+ logger.reset_dt();
+ logger.next_line();
+}
+
+void Tracker::run()
+{
+#if defined(_WIN32)
+ (void) timeBeginPeriod(1);
+#endif
+
+ setPriority(QThread::HighPriority);
+
+ {
+ static constexpr const char* posechannels[6] = { "TX", "TY", "TZ", "Yaw", "Pitch", "Roll" };
+ static constexpr const char* datachannels[5] = { "dt", "raw", "corrected", "filtered", "mapped" };
+ logger.write(datachannels[0]);
+ char buffer[128];
+ for (unsigned j = 1; j < 5; ++j)
+ {
+ for (unsigned i = 0; i < 6; ++i)
+ {
+ std::sprintf(buffer, "%s%s", datachannels[j], posechannels[i]);
+ logger.write(buffer);
+ }
+ }
+ logger.next_line();
+ }
+
+ t.start();
+ logger.reset_dt();
+
+ while (!should_quit)
+ {
+ double tmp[6] {0,0,0, 0,0,0};
+ libs.pTracker->data(tmp);
+
+ if (enabledp)
+ for (int i = 0; i < 6; i++)
+ newpose[i] = elide_nan(tmp[i], newpose[i]);
+
+ logic();
+
+ 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)));
+ }
+
+ {
+ // filter may inhibit exact origin
+ Pose p;
+ libs.pProtocol->pose(p);
+ }
+
+#if defined(_WIN32)
+ (void) timeEndPeriod(1);
+#endif
+
+ for (int i = 0; i < 6; i++)
+ {
+ m(i).spline_main.setTrackingActive(false);
+ m(i).spline_alt.setTrackingActive(false);
+ }
+}
+
+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);
+ mapped[i] = output_pose(i);
+ }
+}
+