From f5610a1ac847f17402415e4bf1080f84fd8d185f Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Mon, 9 Jul 2018 07:18:25 +0200 Subject: tracker/tobii: add prototype support for accumulative mode --- tracker-tobii-eyex/head-tracking.cpp | 89 ++++++ tracker-tobii-eyex/lang/nl_NL.ts | 45 ---- tracker-tobii-eyex/lang/ru_RU.ts | 45 ---- tracker-tobii-eyex/lang/stub.ts | 45 ---- tracker-tobii-eyex/lang/zh_CN.ts | 45 ---- tracker-tobii-eyex/tobii-eyex-dialog.cpp | 5 +- tracker-tobii-eyex/tobii-eyex-dialog.ui | 449 +------------------------------ tracker-tobii-eyex/tobii-eyex.cpp | 106 ++------ tracker-tobii-eyex/tobii-eyex.hpp | 57 ++-- tracker-tobii-eyex/tobii-settings.hpp | 11 +- 10 files changed, 150 insertions(+), 747 deletions(-) create mode 100644 tracker-tobii-eyex/head-tracking.cpp diff --git a/tracker-tobii-eyex/head-tracking.cpp b/tracker-tobii-eyex/head-tracking.cpp new file mode 100644 index 00000000..8973293d --- /dev/null +++ b/tracker-tobii-eyex/head-tracking.cpp @@ -0,0 +1,89 @@ +#include "tobii-eyex.hpp" +#include "compat/math.hpp" +#include "compat/math-imports.hpp" + +real tobii_eyex_tracker::gain(real x) +{ + // simple sigmoid + x = clamp(x * 12 - 6, -6, 6); + x = 1 / (1 + exp(-x)); + x = x * 2 - 1; + return clamp(x, -1, 1); +} + +void tobii_eyex_tracker::data(double* data) +{ + real px, py, max_x, max_y; + bool fresh; + + { + QMutexLocker l(&global_state_mtx); + + if (!dev_state.is_valid()) + return; + + px = dev_state.px; + py = dev_state.py; + max_x = dev_state.display_res_x - 1; + max_y = dev_state.display_res_y - 1; + + fresh = dev_state.fresh; + dev_state.fresh = false; + } + + if (fresh) + { + real x = (2*px - max_x)/max_x; // (-1)->1 + real y = (2*py - max_y)/max_y; // idem + + data[TX] = x * 50; + data[TY] = y * -50; + + const double dt = t.elapsed_seconds(); + t.start(); + + const double max_yaw = *s.acc_max_yaw, max_pitch = *s.acc_max_pitch; + const double dz_ = *s.acc_dz; // closed set of 0->x, some arbitrary x < 1 + + for (auto* k_ : { &x, &y }) + { + real& k = *k_; + + if (std::fabs(k) < dz_) + k = 0; + else + { + // input has reduced dynamic range + k -= copysign(dz_, k); + k *= 1 + dz_; + } + } + + const double c = *s.acc_speed; + + // XXX check this for stability -sh 20180709 + const double yaw_delta = gain(x) * c * dt; + const double pitch_delta = gain(y) * c * dt; + + yaw += yaw_delta; + pitch += pitch_delta; + + yaw = clamp(yaw, -max_yaw, max_yaw); + pitch = clamp(pitch, -max_pitch, max_pitch); + } + + if (do_center) + { + do_center = false; + yaw = 0; + pitch = 0; + } + + data[Yaw] = yaw; + data[Pitch] = pitch; + data[Roll] = 0; + data[TZ] = 0; // XXX TODO + + // tan(x) in 0->.7 is almost linear. we don't need to adjust. + // .7 is 40 degrees which is already quite a lot from the monitor. +} diff --git a/tracker-tobii-eyex/lang/nl_NL.ts b/tracker-tobii-eyex/lang/nl_NL.ts index 35c7f98a..47076f04 100644 --- a/tracker-tobii-eyex/lang/nl_NL.ts +++ b/tracker-tobii-eyex/lang/nl_NL.ts @@ -7,51 +7,6 @@ Tracker options - - Tracking settings - - - - Accumulative mode shifts the view toward a target that may be offscreen then fixes upon it. -On the other hand, the snap mode allows for a quick glance outside the field of vision. - - - - Accumulative mode settings - - - - Screen edge length - - - - Max yaw - - - - Max pitch - - - - Position output - - - - Enabled - - - - Snap mode settings - - - - Tracking mode - - - - Speed - - tobii_eyex_tracker diff --git a/tracker-tobii-eyex/lang/ru_RU.ts b/tracker-tobii-eyex/lang/ru_RU.ts index 806c23b8..65ec8ca5 100644 --- a/tracker-tobii-eyex/lang/ru_RU.ts +++ b/tracker-tobii-eyex/lang/ru_RU.ts @@ -7,51 +7,6 @@ Tracker options - - Tracking settings - - - - Accumulative mode shifts the view toward a target that may be offscreen then fixes upon it. -On the other hand, the snap mode allows for a quick glance outside the field of vision. - - - - Accumulative mode settings - - - - Screen edge length - - - - Max yaw - - - - Max pitch - - - - Position output - - - - Enabled - - - - Snap mode settings - - - - Tracking mode - - - - Speed - - tobii_eyex_tracker diff --git a/tracker-tobii-eyex/lang/stub.ts b/tracker-tobii-eyex/lang/stub.ts index 26acccaa..6e4c7d1a 100644 --- a/tracker-tobii-eyex/lang/stub.ts +++ b/tracker-tobii-eyex/lang/stub.ts @@ -7,51 +7,6 @@ Tracker options - - Tracking settings - - - - Accumulative mode shifts the view toward a target that may be offscreen then fixes upon it. -On the other hand, the snap mode allows for a quick glance outside the field of vision. - - - - Accumulative mode settings - - - - Screen edge length - - - - Max yaw - - - - Max pitch - - - - Position output - - - - Enabled - - - - Snap mode settings - - - - Tracking mode - - - - Speed - - tobii_eyex_tracker diff --git a/tracker-tobii-eyex/lang/zh_CN.ts b/tracker-tobii-eyex/lang/zh_CN.ts index b7896059..6e4c7d1a 100644 --- a/tracker-tobii-eyex/lang/zh_CN.ts +++ b/tracker-tobii-eyex/lang/zh_CN.ts @@ -7,51 +7,6 @@ Tracker options - - Tracking settings - - - - Accumulative mode shifts the view toward a target that may be offscreen then fixes upon it. -On the other hand, the snap mode allows for a quick glance outside the field of vision. - - - - Tracking mode - - - - Accumulative mode settings - - - - Screen edge length - - - - Speed - - - - Max yaw - - - - Max pitch - - - - Position output - - - - Enabled - - - - Snap mode settings - - tobii_eyex_tracker diff --git a/tracker-tobii-eyex/tobii-eyex-dialog.cpp b/tracker-tobii-eyex/tobii-eyex-dialog.cpp index 807542e1..6ab916be 100644 --- a/tracker-tobii-eyex/tobii-eyex-dialog.cpp +++ b/tracker-tobii-eyex/tobii-eyex-dialog.cpp @@ -7,10 +7,7 @@ tobii_eyex_dialog::tobii_eyex_dialog() connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &tobii_eyex_dialog::do_ok); connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &tobii_eyex_dialog::do_cancel); - ui.tracking_mode->addItem("Snap", tobii_snap); - ui.tracking_mode->addItem("Accumulative", tobii_acc); - - tie_setting(s.mode, ui.tracking_mode); + //tie_setting(s.mode, ui.tracking_mode); } void tobii_eyex_dialog::do_ok() diff --git a/tracker-tobii-eyex/tobii-eyex-dialog.ui b/tracker-tobii-eyex/tobii-eyex-dialog.ui index a6aecafb..37451f06 100644 --- a/tracker-tobii-eyex/tobii-eyex-dialog.ui +++ b/tracker-tobii-eyex/tobii-eyex-dialog.ui @@ -6,8 +6,8 @@ 0 0 - 659 - 471 + 184 + 35 @@ -22,447 +22,6 @@ 4 - - - - 0 - 0 - - - - Tracking settings - - - - - - - 0 - 0 - - - - - 617 - 0 - - - - Accumulative mode shifts the view toward a target that may be offscreen then fixes upon it. -On the other hand, the snap mode allows for a quick glance outside the field of vision. - - - Qt::PlainText - - - true - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 4 - - - 4 - - - 4 - - - 4 - - - 9 - - - 4 - - - - - Tracking mode - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 10 - 0 - - - - - - - - - - - - - - - 0 - 0 - - - - Accumulative mode settings - - - - - - - 0 - 0 - - - - Screen edge length - - - - - - - - 9 - 0 - - - - 1 - - - 20 - - - 1 - - - Qt::Horizontal - - - - - - - - 10 - 0 - - - - - - - - - - - - 0 - 0 - - - - Speed - - - - - - - - 0 - 0 - - - - Max yaw - - - - - - - - 9 - 0 - - - - 100 - - - 1 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - Max pitch - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - Position output - - - - - - - - 0 - 0 - - - - Enabled - - - - - - - - - - - 0 - 0 - - - - Snap mode settings - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - Max pitch - - - - - - - - 0 - 0 - - - - Max yaw - - - - - - - - 0 - 0 - - - - Speed - - - - - - - - 8 - 0 - - - - 1 - - - 20 - - - 1 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - - - 10 - 0 - - - - - - - - - - - - 8 - 0 - - - - 100 - - - 1 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - Screen edge length - - - - - - - - 0 - 0 - - - - Enabled - - - - - - - - 0 - 0 - - - - Position output - - - - - - - @@ -479,6 +38,10 @@ On the other hand, the snap mode allows for a quick glance outside the field of + + + + diff --git a/tracker-tobii-eyex/tobii-eyex.cpp b/tracker-tobii-eyex/tobii-eyex.cpp index 8f2edd62..af0d0c05 100644 --- a/tracker-tobii-eyex/tobii-eyex.cpp +++ b/tracker-tobii-eyex/tobii-eyex.cpp @@ -1,6 +1,7 @@ #include "tobii-eyex.hpp" #include "compat/math-imports.hpp" +#include #include #include @@ -32,17 +33,7 @@ static inline tobii_eyex_tracker& to_self(TX_USERPARAM param) return *reinterpret_cast(param); } -tobii_eyex_tracker::tobii_eyex_tracker() : - dev_ctx(TX_EMPTY_HANDLE), - conn_state_changed_ticket(TX_INVALID_TICKET), - event_handler_ticket(TX_INVALID_TICKET), - state_snapshot(TX_EMPTY_HANDLE), - display_state(TX_EMPTY_HANDLE), - yaw(0), - pitch(0), - do_center(false) -{ -} +tobii_eyex_tracker::tobii_eyex_tracker() = default; void tobii_eyex_tracker::call_tx_deinit() { @@ -54,12 +45,12 @@ tobii_eyex_tracker::~tobii_eyex_tracker() { dbg_verbose("dtor"); - (void) txDisableConnection(dev_ctx); + (void) txDisableConnection(ctx); (void) txReleaseObject(&state_snapshot); bool status = true; - status &= txShutdownContext(dev_ctx, TX_CLEANUPTIMEOUT_FORCEIMMEDIATE, TX_FALSE) == TX_RESULT_OK; - status &= txReleaseContext(&dev_ctx) == TX_RESULT_OK; + status &= txShutdownContext(ctx, TX_CLEANUPTIMEOUT_FORCEIMMEDIATE, TX_FALSE) == TX_RESULT_OK; + status &= txReleaseContext(&ctx) == TX_RESULT_OK; // the API cleanup function needs to be called exactly once over image lifetime. // client software communicates with a service and a desktop program. @@ -129,7 +120,7 @@ void tobii_eyex_tracker::snapshot_committed_handler(TX_CONSTHANDLE async_data_ha dbg_notice("snapshot bad result code") << result; } -void tobii_eyex_tracker::connection_state_change_handler(TX_CONNECTIONSTATE state, TX_USERPARAM param) +void tobii_eyex_tracker::state_change_handler(TX_CONNECTIONSTATE state, TX_USERPARAM param) { tobii_eyex_tracker& self = to_self(param); @@ -142,7 +133,7 @@ void tobii_eyex_tracker::connection_state_change_handler(TX_CONNECTIONSTATE stat dbg_notice("connected but failed to initialize data stream"); else { - txGetStateAsync(self.dev_ctx, TX_STATEPATH_EYETRACKINGSCREENBOUNDS, display_state_handler, param); + txGetStateAsync(self.ctx, TX_STATEPATH_EYETRACKINGSCREENBOUNDS, display_state_handler, param); dbg_notice("connected, data stream ok"); } } @@ -222,11 +213,11 @@ module_status tobii_eyex_tracker::start_tracker(QFrame*) bool status = true; status &= txInitializeEyeX(TX_EYEXCOMPONENTOVERRIDEFLAG_NONE, nullptr, nullptr, nullptr, nullptr) == TX_RESULT_OK; - status &= txCreateContext(&dev_ctx, TX_FALSE) == TX_RESULT_OK; - status &= register_state_snapshot(dev_ctx, &state_snapshot); - status &= txRegisterConnectionStateChangedHandler(dev_ctx, &conn_state_changed_ticket, connection_state_change_handler, reinterpret_cast(this)) == TX_RESULT_OK; - status &= txRegisterEventHandler(dev_ctx, &event_handler_ticket, event_handler, reinterpret_cast(this)) == TX_RESULT_OK; - status &= txEnableConnection(dev_ctx) == TX_RESULT_OK; + status &= txCreateContext(&ctx, TX_FALSE) == TX_RESULT_OK; + status &= register_state_snapshot(ctx, &state_snapshot); + status &= txRegisterConnectionStateChangedHandler(ctx, &state_changed_ticket, state_change_handler, (TX_USERPARAM)this) == TX_RESULT_OK; + status &= txRegisterEventHandler(ctx, &event_handler_ticket, event_handler, (TX_USERPARAM)this) == TX_RESULT_OK; + status &= txEnableConnection(ctx) == TX_RESULT_OK; if (!status) return error(tr("Connection can't be established. device missing?")); @@ -234,78 +225,15 @@ module_status tobii_eyex_tracker::start_tracker(QFrame*) return status_ok(); } -tobii_eyex_tracker::num tobii_eyex_tracker::gain(num x) +bool tobii_eyex_tracker::center() { - return 1; + do_center = true; + return true; } -static inline double signum(double x) -{ - return !(x < 0) - (x < 0); -} - -void tobii_eyex_tracker::data(double* data) -{ - TX_REAL px, py, dw, dh, x_, y_; - bool fresh; - - { - QMutexLocker l(&global_state_mtx); - - if (!dev_state.is_valid()) - return; - - px = dev_state.px; - py = dev_state.py; - dw = dev_state.display_res_x; - dh = dev_state.display_res_y; - - fresh = dev_state.fresh; - dev_state.fresh = false; - } - - x_ = (px-dw/2.) / (dw/2.); - y_ = (py-dh/2.) / (dh/2.); +settings::settings() : opts("tobii-eyex") {} - data[TX] = x_ * 50; - data[TY] = y_ * -50; - - if (fresh) - { - const double dt = t.elapsed_seconds(); - t.start(); - - using std::fabs; - - constexpr double max_yaw = 45, max_pitch = 30; - constexpr double c_yaw = 3; - constexpr double c_pitch = c_yaw * max_pitch / max_yaw; - - const double yaw_delta = gain(fabs(x_)) * signum(x_) * c_yaw * dt; - const double pitch_delta = gain(fabs(y_)) * signum(y_) * c_pitch * dt; - - yaw += yaw_delta; - pitch += pitch_delta; - - yaw = clamp(yaw, -max_yaw, max_yaw); - pitch = clamp(pitch, -max_pitch, max_pitch); - } - - if (do_center) - { - do_center = false; - yaw = 0; - pitch = 0; - } - - data[Yaw] = yaw; - data[Pitch] = pitch; - data[Roll] = 0; - data[TZ] = 0; // XXX TODO - - // tan(x) in 0->.7 is almost linear. we don't need to adjust. - // .7 is 40 degrees which is already quite a lot from the monitor. -} +state::state() = default; #include "tobii-eyex-dialog.hpp" diff --git a/tracker-tobii-eyex/tobii-eyex.hpp b/tracker-tobii-eyex/tobii-eyex.hpp index 84407930..def2ea2e 100644 --- a/tracker-tobii-eyex/tobii-eyex.hpp +++ b/tracker-tobii-eyex/tobii-eyex.hpp @@ -23,6 +23,20 @@ using namespace options; #include #include +//using real = TX_REAL; +using real = double; + +struct state +{ + real display_res_x = -1, display_res_y = -1; + real px = -1, py = -1; + real last_timestamp = -1; + bool fresh = false; + + state(); + bool is_valid() const { return !(display_res_x < 0 || px < 0); } +}; + class tobii_eyex_tracker : public TR, public ITracker { Q_OBJECT @@ -30,13 +44,9 @@ class tobii_eyex_tracker : public TR, public ITracker public: tobii_eyex_tracker(); ~tobii_eyex_tracker() override; - module_status start_tracker(QFrame *) override; + module_status start_tracker(QFrame*) override; void data(double *data) override; - bool center() override - { - do_center = true; - return true; - } + bool center() override; private: static constexpr inline const char* const client_id = "opentrack-tobii-eyex"; @@ -44,47 +54,36 @@ private: static bool register_state_snapshot(TX_CONTEXTHANDLE ctx, TX_HANDLE* state_snapshot_ptr); static std::atomic_flag atexit_done; - static void TX_CALLCONVENTION connection_state_change_handler(TX_CONNECTIONSTATE state, TX_USERPARAM param); + static void TX_CALLCONVENTION state_change_handler(TX_CONNECTIONSTATE state, TX_USERPARAM param); static void TX_CALLCONVENTION event_handler(TX_CONSTHANDLE async_data_handle, TX_USERPARAM param); void gaze_data_handler(TX_HANDLE gaze_data_handle); static void TX_CALLCONVENTION snapshot_committed_handler(TX_CONSTHANDLE async_data_handle, TX_USERPARAM param); static void TX_CALLCONVENTION display_state_handler(TX_CONSTHANDLE async_data_handle, TX_USERPARAM param); void process_display_state(TX_HANDLE display_state_handle); - using num = double; + real gain(real x); - num gain(num x); + state dev_state; + real yaw = 0, pitch = 0; - TX_CONTEXTHANDLE dev_ctx; - TX_TICKET conn_state_changed_ticket; - TX_TICKET event_handler_ticket; - TX_HANDLE state_snapshot; - TX_HANDLE display_state; + TX_CONTEXTHANDLE ctx = TX_EMPTY_HANDLE; + TX_TICKET state_changed_ticket = TX_INVALID_TICKET; + TX_TICKET event_handler_ticket = TX_INVALID_TICKET; + TX_HANDLE state_snapshot = TX_EMPTY_HANDLE; + TX_HANDLE display_state = TX_EMPTY_HANDLE; QMutex global_state_mtx; settings s; Timer t; - struct state - { - TX_REAL display_res_x, display_res_y; - TX_REAL px, py; - TX_REAL last_timestamp; - bool fresh; - - state() : display_res_x(-1), display_res_y(-1), px(-1), py(-1), last_timestamp(0), fresh(false) {} - bool is_valid() const { return !(display_res_x < 0 || px < 0); } - } dev_state; - - double yaw, pitch; - std::atomic do_center; + std::atomic do_center = false; }; class tobii_eyex_metadata : public Metadata { Q_OBJECT - QString name() { return QString("Tobii EyeX"); } - QIcon icon() { return QIcon(":/images/tobii-eyex-logo.png"); } + QString name() override { return QString("Tobii EyeX"); } + QIcon icon() override { return QIcon(":/images/tobii-eyex-logo.png"); } }; diff --git a/tracker-tobii-eyex/tobii-settings.hpp b/tracker-tobii-eyex/tobii-settings.hpp index 49b9cae8..6101a28f 100644 --- a/tracker-tobii-eyex/tobii-settings.hpp +++ b/tracker-tobii-eyex/tobii-settings.hpp @@ -21,6 +21,7 @@ enum max_pitch struct settings final : public opts { +#if 0 value mode { b, "mode", tobii_snap }; value snap_speed {b, "snap-speed", { .1, .05, 1 }}, @@ -28,9 +29,15 @@ struct settings final : public opts value acc_speed {b, "acc-speed", { .1, .05, 1 }}, acc_dz_len {b, "acc-screen-edge-length", { .1, .1, 1 }}; value snap_yaw {b, "snap-max-yaw", y20}, - acc_yaw {b, "acc-max-yaw", y20}; value snap_pitch {b, "snap-max-pitch", p15}, acc_pitch {b, "acc-max-pitch", p15}; + acc_yaw {b, "acc-max-yaw", y20}; +#endif + value acc_speed { b, "acc-max-speed-deg", { 3, 1, 10 } }; + value acc_dz { b, "acc-deadzone", { .15, .0, .3 } }; - settings() : opts("tobii-eyex") {} + value acc_max_yaw { b, "acc-max-yaw", { 30, 15, 45} }, + acc_max_pitch { b, "acc-max-pitch", { 30, 15, 45 } }; + + settings(); }; -- cgit v1.2.3