The open source OpenXR runtime
at main 683 lines 22 kB view raw
1/* 2 * Copyright 2013, Fredrik Hultin. 3 * Copyright 2013, Jakob Bornecrantz. 4 * Copyright 2016 Philipp Zabel 5 * Copyright 2019-2022 Jan Schmidt 6 * Copyright 2023, Collabora, Ltd. 7 * SPDX-License-Identifier: BSL-1.0 8 * 9 */ 10/*! 11 * @file 12 * @brief Driver code for Oculus Rift S headsets 13 * 14 * Implementation for the HMD 3dof and 6dof tracking 15 * 16 * @author Jan Schmidt <jan@centricular.com> 17 * @ingroup drv_rift_s 18 */ 19#include <stdlib.h> 20#include <string.h> 21#include <stdio.h> 22#include <time.h> 23#include <assert.h> 24 25#include "math/m_api.h" 26#include "math/m_clock_tracking.h" 27#include "math/m_space.h" 28#include "math/m_vec3.h" 29 30#include "os/os_time.h" 31 32#include "util/u_debug.h" 33#include "util/u_device.h" 34#include "util/u_sink.h" 35#include "util/u_trace_marker.h" 36#include "util/u_var.h" 37 38#include "xrt/xrt_config_build.h" 39#include "xrt/xrt_config_drivers.h" 40#include "xrt/xrt_device.h" 41 42#ifdef XRT_BUILD_DRIVER_HANDTRACKING 43#include "../drivers/ht/ht_interface.h" 44#include "../multi_wrapper/multi.h" 45#endif 46 47#include "rift_s.h" 48#include "rift_s_interface.h" 49#include "rift_s_util.h" 50#include "rift_s_tracker.h" 51 52#ifdef XRT_FEATURE_SLAM 53static const bool slam_supported = true; 54#else 55static const bool slam_supported = false; 56#endif 57 58#ifdef XRT_BUILD_DRIVER_HANDTRACKING 59static const bool hand_supported = true; 60#else 61static const bool hand_supported = false; 62#endif 63 64//! Specifies whether the user wants to use a SLAM tracker. 65DEBUG_GET_ONCE_BOOL_OPTION(rift_s_slam, "RIFT_S_SLAM", true) 66 67//! Specifies whether the user wants to use the hand tracker. 68DEBUG_GET_ONCE_BOOL_OPTION(rift_s_handtracking, "RIFT_S_HANDTRACKING", true) 69 70static xrt_result_t 71rift_s_tracker_get_tracked_pose_imu(struct xrt_device *xdev, 72 enum xrt_input_name name, 73 int64_t at_timestamp_ns, 74 struct xrt_space_relation *out_relation); 75 76static void 77rift_s_tracker_switch_method_cb(void *t_ptr) 78{ 79 DRV_TRACE_MARKER(); 80 81 struct rift_s_tracker *t = t_ptr; 82 t->slam_over_3dof = !t->slam_over_3dof; 83 struct u_var_button *btn = &t->gui.switch_tracker_btn; 84 85 if (t->slam_over_3dof) { // Use SLAM 86 snprintf(btn->label, sizeof(btn->label), "Switch to 3DoF Tracking"); 87 } else { // Use 3DoF 88 snprintf(btn->label, sizeof(btn->label), "Switch to SLAM Tracking"); 89 90 os_mutex_lock(&t->mutex); 91 m_imu_3dof_reset(&t->fusion.i3dof); 92 t->fusion.i3dof.rot = t->pose.orientation; 93 os_mutex_unlock(&t->mutex); 94 } 95} 96 97XRT_MAYBE_UNUSED void 98rift_s_fill_slam_imu_calibration(struct rift_s_tracker *t, struct rift_s_hmd_config *hmd_config) 99{ 100 /* FIXME: Validate these hard coded standard deviations against 101 * some actual at-rest IMU measurements */ 102 const double a_bias_std = 0.001; 103 const double a_noise_std = 0.016; 104 105 const double g_bias_std = 0.0001; 106 const double g_noise_std = 0.000282; 107 108 /* we pass already corrected accel and gyro 109 * readings to Basalt, so the transforms and 110 * offsets are just identity / zero matrices */ 111 struct t_imu_calibration imu_calib = { 112 .accel = 113 { 114 .transform = 115 { 116 {1.0, 0.0, 0.0}, 117 {0.0, 1.0, 0.0}, 118 {0.0, 0.0, 1.0}, 119 }, 120 .offset = 121 { 122 0, 123 }, 124 .bias_std = {a_bias_std, a_bias_std, a_bias_std}, 125 .noise_std = {a_noise_std, a_noise_std, a_noise_std}, 126 }, 127 .gyro = 128 { 129 .transform = 130 { 131 {1.0, 0.0, 0.0}, 132 {0.0, 1.0, 0.0}, 133 {0.0, 0.0, 1.0}, 134 }, 135 .offset = 136 { 137 0, 138 }, 139 .bias_std = {g_bias_std, g_bias_std, g_bias_std}, 140 .noise_std = {g_noise_std, g_noise_std, g_noise_std}, 141 }, 142 }; 143 144 struct t_slam_imu_calibration calib = { 145 .base = imu_calib, 146 .frequency = hmd_config->imu_config_info.imu_hz, 147 }; 148 149 t->slam_calib.imu = calib; 150} 151 152//! Extended camera calibration for SLAM 153static void 154rift_s_fill_slam_cameras_calibration(struct rift_s_tracker *t, struct rift_s_hmd_config *hmd_config) 155{ 156 /* SLAM frames are every 2nd frame of 60Hz camera feed */ 157 const int CAMERA_FREQUENCY = 30; 158 159 struct rift_s_camera_calibration_block *camera_calibration = &hmd_config->camera_calibration; 160 161 /* Compute the IMU from cam transform for each cam */ 162 struct xrt_pose device_from_imu, imu_from_device; 163 math_pose_from_isometry(&hmd_config->imu_calibration.device_from_imu, &device_from_imu); 164 math_pose_invert(&device_from_imu, &imu_from_device); 165 166 t->slam_calib.cam_count = RIFT_S_CAMERA_COUNT; 167 for (int i = 0; i < RIFT_S_CAMERA_COUNT; i++) { 168 enum rift_s_camera_id cam_id = CAM_IDX_TO_ID[i]; 169 struct rift_s_camera_calibration *cam = &camera_calibration->cameras[cam_id]; 170 171 struct xrt_pose device_from_cam; 172 math_pose_from_isometry(&cam->device_from_camera, &device_from_cam); 173 174 struct xrt_pose P_imu_cam; 175 math_pose_transform(&imu_from_device, &device_from_cam, &P_imu_cam); 176 177 struct xrt_matrix_4x4 T_imu_cam; 178 math_matrix_4x4_isometry_from_pose(&P_imu_cam, &T_imu_cam); 179 180 RIFT_S_DEBUG("IMU cam%d cam pose %f %f %f orient %f %f %f %f", i, P_imu_cam.position.x, 181 P_imu_cam.position.y, P_imu_cam.position.z, P_imu_cam.orientation.x, 182 P_imu_cam.orientation.y, P_imu_cam.orientation.z, P_imu_cam.orientation.w); 183 184 struct t_slam_camera_calibration calib = { 185 .base = rift_s_get_cam_calib(&hmd_config->camera_calibration, cam_id), 186 .frequency = CAMERA_FREQUENCY, 187 .T_imu_cam = T_imu_cam, 188 }; 189 t->slam_calib.cams[i] = calib; 190 } 191} 192 193static void 194rift_s_fill_slam_calibration(struct rift_s_tracker *t, struct rift_s_hmd_config *hmd_config) 195{ 196 rift_s_fill_slam_imu_calibration(t, hmd_config); 197 rift_s_fill_slam_cameras_calibration(t, hmd_config); 198} 199 200static struct xrt_slam_sinks * 201rift_s_create_slam_tracker(struct rift_s_tracker *t, struct xrt_frame_context *xfctx) 202{ 203 DRV_TRACE_MARKER(); 204 205 struct xrt_slam_sinks *sinks = NULL; 206 207#ifdef XRT_FEATURE_SLAM 208 struct t_slam_tracker_config config = {0}; 209 t_slam_fill_default_config(&config); 210 211 /* No need to refcount these parameters */ 212 config.cam_count = RIFT_S_CAMERA_COUNT; 213 config.slam_calib = &t->slam_calib; 214 215 int create_status = t_slam_create(xfctx, &config, &t->tracking.slam, &sinks); 216 if (create_status != 0) { 217 return NULL; 218 } 219 220 int start_status = t_slam_start(t->tracking.slam); 221 if (start_status != 0) { 222 return NULL; 223 } 224 225 RIFT_S_DEBUG("Rift S SLAM tracker successfully started"); 226#endif 227 228 return sinks; 229} 230 231static int 232rift_s_create_hand_tracker(struct rift_s_tracker *t, 233 struct xrt_frame_context *xfctx, 234 struct xrt_hand_masks_sink *masks_sink, 235 struct xrt_slam_sinks **out_sinks, 236 struct xrt_device **out_device) 237{ 238 DRV_TRACE_MARKER(); 239 240 struct xrt_slam_sinks *sinks = NULL; 241 struct xrt_device *device = NULL; 242 243#ifdef XRT_BUILD_DRIVER_HANDTRACKING 244 245 //!@todo What's a sensible boundary for Rift S? 246 struct t_camera_extra_info extra_camera_info = { 247 0, 248 }; 249 extra_camera_info.views[0].boundary_type = HT_IMAGE_BOUNDARY_NONE; 250 extra_camera_info.views[1].boundary_type = HT_IMAGE_BOUNDARY_NONE; 251 252 extra_camera_info.views[0].camera_orientation = CAMERA_ORIENTATION_90; 253 extra_camera_info.views[1].camera_orientation = CAMERA_ORIENTATION_90; 254 255 struct t_hand_tracking_create_info create_info = {.cams_info = extra_camera_info, .masks_sink = masks_sink}; 256 257 int create_status = ht_device_create(xfctx, // 258 t->stereo_calib, // 259 create_info, // 260 &sinks, // 261 &device); 262 if (create_status != 0) { 263 return create_status; 264 } 265 266 if (device != NULL) { 267 // Attach tracking override that links hand pose to the SLAM tracked position 268 // The hand poses need to be rotated 90° because of the way we passed 269 // the stereo camera configuration to the hand tracker. 270 struct xrt_pose left_cam_rotated_from_imu; 271 struct xrt_pose cam_rotate = {.orientation = {.x = 1.0, .y = 0.0, .z = 0.0, .w = 0.0}, 272 .position = {0, 0, 0}}; 273 math_pose_transform(&cam_rotate, &t->left_cam_from_imu, &left_cam_rotated_from_imu); 274 275 device = multi_create_tracking_override(XRT_TRACKING_OVERRIDE_ATTACHED, device, &t->base, 276 XRT_INPUT_GENERIC_TRACKER_POSE, &left_cam_rotated_from_imu); 277 } 278 279 RIFT_S_DEBUG("Rift S HMD hand tracker successfully created"); 280#endif 281 282 *out_sinks = sinks; 283 *out_device = device; 284 285 return 0; 286} 287 288void 289rift_s_tracker_add_debug_ui(struct rift_s_tracker *t, void *root) 290{ 291 u_var_add_gui_header(root, NULL, "Tracking"); 292 293 if (t->tracking.slam_enabled) { 294 t->gui.switch_tracker_btn.cb = rift_s_tracker_switch_method_cb; 295 t->gui.switch_tracker_btn.ptr = t; 296 u_var_add_button(root, &t->gui.switch_tracker_btn, "Switch to 3DoF Tracking"); 297 } 298 299 u_var_add_pose(root, &t->pose, "Tracked Pose"); 300 301 u_var_add_gui_header(root, NULL, "3DoF Tracking"); 302 m_imu_3dof_add_vars(&t->fusion.i3dof, root, ""); 303 304 u_var_add_gui_header(root, NULL, "SLAM Tracking"); 305 u_var_add_ro_text(root, t->gui.slam_status, "Tracker status"); 306 307 u_var_add_gui_header(root, NULL, "Hand Tracking"); 308 u_var_add_ro_text(root, t->gui.hand_status, "Tracker status"); 309} 310 311/*! 312 * Procedure to setup trackers: 3dof, SLAM and hand tracking. 313 * 314 * Determines which trackers to initialize 315 * 316 * @param xfctx the frame server that will own processing nodes 317 * @param hmd_config HMD configuration and firmware info 318 * 319 * @return initialised tracker on success, NULL if creation fails 320 */ 321struct rift_s_tracker * 322rift_s_tracker_create(struct xrt_tracking_origin *origin, 323 struct xrt_frame_context *xfctx, 324 struct rift_s_hmd_config *hmd_config) 325{ 326 struct rift_s_tracker *t = U_DEVICE_ALLOCATE(struct rift_s_tracker, U_DEVICE_ALLOC_TRACKING_NONE, 1, 0); 327 if (t == NULL) { 328 return NULL; 329 } 330 331 t->base.tracking_origin = origin; 332 t->base.get_tracked_pose = rift_s_tracker_get_tracked_pose_imu; 333 334 // Pose / state lock 335 int ret = os_mutex_init(&t->mutex); 336 if (ret != 0) { 337 RIFT_S_ERROR("Failed to init mutex!"); 338 rift_s_tracker_destroy(t); 339 return NULL; 340 } 341 342 // Compute IMU and camera device poses for get_tracked_pose relations 343 math_pose_from_isometry(&hmd_config->imu_calibration.device_from_imu, &t->device_from_imu); 344 345 struct xrt_pose device_from_left_cam; 346 struct rift_s_camera_calibration *left_cam = &hmd_config->camera_calibration.cameras[RIFT_S_CAMERA_FRONT_LEFT]; 347 math_pose_from_isometry(&left_cam->device_from_camera, &device_from_left_cam); 348 349 struct xrt_pose left_cam_from_device; 350 math_pose_invert(&device_from_left_cam, &left_cam_from_device); 351 math_pose_transform(&left_cam_from_device, &t->device_from_imu, &t->left_cam_from_imu); 352 353 // Decide whether to initialize the SLAM tracker 354 bool slam_wanted = debug_get_bool_option_rift_s_slam(); 355 bool slam_enabled = slam_supported && slam_wanted; 356 357 // Decide whether to initialize the hand tracker 358 bool hand_wanted = debug_get_bool_option_rift_s_handtracking(); 359 bool hand_enabled = hand_supported && hand_wanted; 360 361 t->tracking.slam_enabled = slam_enabled; 362 t->tracking.hand_enabled = hand_enabled; 363 364 t->slam_over_3dof = slam_enabled; // We prefer SLAM over 3dof tracking if possible 365 366 const char *slam_status = t->tracking.slam_enabled ? "Enabled" 367 : !slam_wanted ? "Disabled by the user (envvar set to false)" 368 : !slam_supported ? "Unavailable (not built)" 369 : NULL; 370 371 const char *hand_status = t->tracking.hand_enabled ? "Enabled" 372 : !hand_wanted ? "Disabled by the user (envvar set to false)" 373 : !hand_supported ? "Unavailable (not built)" 374 : NULL; 375 376 assert(slam_status != NULL && hand_status != NULL); 377 378 (void)snprintf(t->gui.slam_status, sizeof(t->gui.slam_status), "%s", slam_status); 379 (void)snprintf(t->gui.hand_status, sizeof(t->gui.hand_status), "%s", hand_status); 380 381 // Initialize 3DoF tracker 382 m_imu_3dof_init(&t->fusion.i3dof, M_IMU_3DOF_USE_GRAVITY_DUR_20MS); 383 384 t->pose.orientation.w = 1.0f; // All other values set to zero by U_DEVICE_ALLOCATE (which calls U_CALLOC) 385 386 // Construct the stereo camera calibration for the front cameras 387 t->stereo_calib = rift_s_create_stereo_camera_calib_rotated(&hmd_config->camera_calibration); 388 rift_s_fill_slam_calibration(t, hmd_config); 389 390 // Initialize the input sinks for the camera to send to 391 392 // Initialize SLAM tracker 393 struct xrt_slam_sinks *slam_sinks = NULL; 394 if (t->tracking.slam_enabled) { 395 slam_sinks = rift_s_create_slam_tracker(t, xfctx); 396 if (slam_sinks == NULL) { 397 RIFT_S_WARN("Unable to setup the SLAM tracker"); 398 rift_s_tracker_destroy(t); 399 return NULL; 400 } 401 } 402 403 // Initialize hand tracker 404 struct xrt_slam_sinks *hand_sinks = NULL; 405 struct xrt_device *hand_device = NULL; 406 struct xrt_hand_masks_sink *masks_sink = slam_sinks ? slam_sinks->hand_masks : NULL; 407 if (t->tracking.hand_enabled) { 408 int hand_status = rift_s_create_hand_tracker(t, xfctx, masks_sink, &hand_sinks, &hand_device); 409 if (hand_status != 0 || hand_sinks == NULL || hand_device == NULL) { 410 RIFT_S_WARN("Unable to setup the hand tracker"); 411 rift_s_tracker_destroy(t); 412 return NULL; 413 } 414 } 415 416 // Setup sinks depending on tracking configuration 417 struct xrt_slam_sinks entry_sinks = {0}; 418 if (slam_enabled && hand_enabled) { 419 struct xrt_frame_sink *entry_cam0_sink = NULL; 420 struct xrt_frame_sink *entry_cam1_sink = NULL; 421 422 u_sink_split_create(xfctx, slam_sinks->cams[0], hand_sinks->cams[0], &entry_cam0_sink); 423 u_sink_split_create(xfctx, slam_sinks->cams[1], hand_sinks->cams[1], &entry_cam1_sink); 424 425 entry_sinks = *slam_sinks; 426 entry_sinks.cams[0] = entry_cam0_sink; 427 entry_sinks.cams[1] = entry_cam1_sink; 428 } else if (slam_enabled) { 429 entry_sinks = *slam_sinks; 430 } else if (hand_enabled) { 431 entry_sinks = *hand_sinks; 432 } else { 433 entry_sinks = (struct xrt_slam_sinks){0}; 434 } 435 436 t->slam_sinks = entry_sinks; 437 t->handtracker = hand_device; 438 439 return t; 440} 441 442void 443rift_s_tracker_destroy(struct rift_s_tracker *t) 444{ 445 t_stereo_camera_calibration_reference(&t->stereo_calib, NULL); 446 447 m_imu_3dof_close(&t->fusion.i3dof); 448 os_mutex_destroy(&t->mutex); 449} 450 451struct xrt_slam_sinks * 452rift_s_tracker_get_slam_sinks(struct rift_s_tracker *t) 453{ 454 return &t->in_slam_sinks; 455} 456 457struct xrt_device * 458rift_s_tracker_get_hand_tracking_device(struct rift_s_tracker *t) 459{ 460 return t->handtracker; 461} 462 463void 464rift_s_tracker_clock_update(struct rift_s_tracker *t, uint64_t device_timestamp_ns, timepoint_ns local_timestamp_ns) 465{ 466 os_mutex_lock(&t->mutex); 467 time_duration_ns last_hw2mono = t->hw2mono; 468 const float freq = 250.0; 469 470 t->seen_clock_observations++; 471 if (t->seen_clock_observations < 100) 472 goto done; 473 474 m_clock_offset_a2b(freq, device_timestamp_ns, local_timestamp_ns, &t->hw2mono); 475 476 if (!t->have_hw2mono) { 477 time_duration_ns change_ns = last_hw2mono - t->hw2mono; 478 if (change_ns >= -U_TIME_HALF_MS_IN_NS && change_ns <= U_TIME_HALF_MS_IN_NS) { 479 RIFT_S_INFO("HMD device to local clock map stabilised"); 480 t->have_hw2mono = true; 481 } 482 } 483done: 484 os_mutex_unlock(&t->mutex); 485} 486 487//! Camera specific logic for clock conversion 488static void 489clock_hw2mono_get(struct rift_s_tracker *t, uint64_t device_ts, timepoint_ns *out) 490{ 491 *out = t->hw2mono + device_ts; 492} 493 494void 495rift_s_tracker_imu_update(struct rift_s_tracker *t, 496 uint64_t device_timestamp_ns, 497 const struct xrt_vec3 *accel, 498 const struct xrt_vec3 *gyro) 499{ 500 os_mutex_lock(&t->mutex); 501 502 /* Ignore packets before we're ready and clock is stable */ 503 if (!t->ready_for_data || !t->have_hw2mono) { 504 os_mutex_unlock(&t->mutex); 505 return; 506 } 507 508 /* Get the smoothed monotonic time estimate for this IMU sample */ 509 timepoint_ns local_timestamp_ns; 510 511 clock_hw2mono_get(t, device_timestamp_ns, &local_timestamp_ns); 512 513 if (t->fusion.last_imu_local_timestamp_ns != 0 && local_timestamp_ns < t->fusion.last_imu_local_timestamp_ns) { 514 RIFT_S_WARN("IMU time went backward by %" PRId64 " ns", 515 local_timestamp_ns - t->fusion.last_imu_local_timestamp_ns); 516 } else { 517 m_imu_3dof_update(&t->fusion.i3dof, local_timestamp_ns, accel, gyro); 518 } 519 520 RIFT_S_TRACE("IMU timestamp %" PRIu64 " (dt %f) hw2mono local ts %" PRIu64 " (dt %f) offset %" PRId64, 521 device_timestamp_ns, 522 (double)(device_timestamp_ns - t->fusion.last_imu_timestamp_ns) / 1000000000.0, local_timestamp_ns, 523 (double)(local_timestamp_ns - t->fusion.last_imu_local_timestamp_ns) / 1000000000.0, t->hw2mono); 524 525 t->fusion.last_angular_velocity = *gyro; 526 t->fusion.last_imu_timestamp_ns = device_timestamp_ns; 527 t->fusion.last_imu_local_timestamp_ns = local_timestamp_ns; 528 529 t->pose.orientation = t->fusion.i3dof.rot; 530 531 os_mutex_unlock(&t->mutex); 532 533 if (t->slam_sinks.imu) { 534 /* Push IMU sample to the SLAM tracker */ 535 struct xrt_vec3_f64 accel64 = {accel->x, accel->y, accel->z}; 536 struct xrt_vec3_f64 gyro64 = {gyro->x, gyro->y, gyro->z}; 537 struct xrt_imu_sample sample = { 538 .timestamp_ns = local_timestamp_ns, .accel_m_s2 = accel64, .gyro_rad_secs = gyro64}; 539 540 xrt_sink_push_imu(t->slam_sinks.imu, &sample); 541 } 542} 543 544#define UPPER_32BITS(x) ((x)&0xffffffff00000000ULL) 545 546void 547rift_s_tracker_push_slam_frames(struct rift_s_tracker *t, 548 uint64_t frame_ts_ns, 549 struct xrt_frame *frames[RIFT_S_CAMERA_COUNT]) 550{ 551 timepoint_ns frame_time; 552 553 os_mutex_lock(&t->mutex); 554 555 /* Ignore packets before we're ready */ 556 if (!t->ready_for_data) { 557 os_mutex_unlock(&t->mutex); 558 return; 559 } 560 561 if (!t->have_hw2mono) { 562 /* Drop any frames before we have IMU */ 563 os_mutex_unlock(&t->mutex); 564 return; 565 } 566 567 /* Ensure the input timestamp is within 32-bits of the IMU 568 * time, because the timestamps are reported and extended to 64-bits 569 * separately and can end up in different epochs */ 570 uint64_t adj_frame_ts_ns = frame_ts_ns + t->camera_ts_offset; 571 int64_t frame_to_imu_uS = (adj_frame_ts_ns / 1000 - t->fusion.last_imu_timestamp_ns / 1000); 572 573 if (frame_to_imu_uS < -(int64_t)(1ULL << 31) || frame_to_imu_uS > (int64_t)(1ULL << 31)) { 574 t->camera_ts_offset = 575 (UPPER_32BITS(t->fusion.last_imu_timestamp_ns / 1000) - UPPER_32BITS(frame_ts_ns / 1000)) * 1000; 576 RIFT_S_DEBUG("Applying epoch offset to frame times of %" PRId64 " (frame->imu was %" PRId64 " µS)", 577 t->camera_ts_offset, frame_to_imu_uS); 578 } 579 frame_ts_ns += t->camera_ts_offset; 580 581 clock_hw2mono_get(t, frame_ts_ns, &frame_time); 582 583 if (frame_time < t->last_frame_time) { 584 RIFT_S_WARN("Camera frame time went backward by %" PRId64 " ns", frame_time - t->last_frame_time); 585 os_mutex_unlock(&t->mutex); 586 return; 587 } 588 589 RIFT_S_TRACE("SLAM frame timestamp %" PRIu64 " local %" PRIu64, frame_ts_ns, frame_time); 590 591 t->last_frame_time = frame_time; 592 os_mutex_unlock(&t->mutex); 593 594 for (int i = 0; i < RIFT_S_CAMERA_COUNT; i++) { 595 if (t->slam_sinks.cams[i]) { 596 frames[i]->timestamp = frame_time; 597 xrt_sink_push_frame(t->slam_sinks.cams[i], frames[i]); 598 } 599 } 600} 601 602//! Specific pose correction for Basalt to OpenXR coordinates 603XRT_MAYBE_UNUSED static inline void 604rift_s_tracker_correct_pose_from_basalt(struct xrt_pose *pose) 605{ 606 struct xrt_quat q = {0.70710678, 0, 0, -0.70710678}; 607 math_quat_rotate(&q, &pose->orientation, &pose->orientation); 608 math_quat_rotate_vec3(&q, &pose->position, &pose->position); 609} 610 611static xrt_result_t 612rift_s_tracker_get_tracked_pose_imu(struct xrt_device *xdev, 613 enum xrt_input_name name, 614 int64_t at_timestamp_ns, 615 struct xrt_space_relation *out_relation) 616{ 617 struct rift_s_tracker *tracker = (struct rift_s_tracker *)(xdev); 618 if (name != XRT_INPUT_GENERIC_TRACKER_POSE) { 619 U_LOG_XDEV_UNSUPPORTED_INPUT(&tracker->base, rift_s_log_level, name); 620 return XRT_ERROR_INPUT_UNSUPPORTED; 621 } 622 623 rift_s_tracker_get_tracked_pose(tracker, RIFT_S_TRACKER_POSE_IMU, at_timestamp_ns, out_relation); 624 625 return XRT_SUCCESS; 626} 627 628void 629rift_s_tracker_get_tracked_pose(struct rift_s_tracker *t, 630 enum rift_s_tracker_pose pose, 631 uint64_t at_timestamp_ns, 632 struct xrt_space_relation *out_relation) 633{ 634 struct xrt_relation_chain xrc = {0}; 635 636 if (pose == RIFT_S_TRACKER_POSE_DEVICE) { 637 m_relation_chain_push_pose(&xrc, &t->device_from_imu); 638 } else if (pose == RIFT_S_TRACKER_POSE_LEFT_CAMERA) { 639 m_relation_chain_push_pose(&xrc, &t->left_cam_from_imu); 640 } 641 642 if (t->tracking.slam_enabled && t->slam_over_3dof) { 643 struct xrt_space_relation imu_relation = XRT_SPACE_RELATION_ZERO; 644 645 // Get the IMU pose from the SLAM tracker 646 xrt_tracked_slam_get_tracked_pose(t->tracking.slam, at_timestamp_ns, &imu_relation); 647#ifdef XRT_FEATURE_SLAM 648 // !todo Correct pose depending on the VIT system in use, this should be done in the system itself. 649 // For now, assume that we are using Basalt. 650 rift_s_tracker_correct_pose_from_basalt(&imu_relation.pose); 651#endif 652 imu_relation.relation_flags = (enum xrt_space_relation_flags)( 653 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | XRT_SPACE_RELATION_POSITION_VALID_BIT | 654 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | XRT_SPACE_RELATION_POSITION_TRACKED_BIT); 655 656 m_relation_chain_push_relation(&xrc, &imu_relation); 657 } else { 658 struct xrt_space_relation imu_relation = XRT_SPACE_RELATION_ZERO; 659 660 os_mutex_lock(&t->mutex); 661 // TODO: Estimate pose at timestamp at_timestamp_ns 662 math_quat_normalize(&t->pose.orientation); 663 imu_relation.pose = t->pose; 664 imu_relation.angular_velocity = t->fusion.last_angular_velocity; 665 imu_relation.relation_flags = (enum xrt_space_relation_flags)( 666 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | XRT_SPACE_RELATION_POSITION_VALID_BIT | 667 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT); 668 669 m_relation_chain_push_relation(&xrc, &imu_relation); 670 671 os_mutex_unlock(&t->mutex); 672 } 673 674 m_relation_chain_resolve(&xrc, out_relation); 675} 676 677void 678rift_s_tracker_start(struct rift_s_tracker *t) 679{ 680 os_mutex_lock(&t->mutex); 681 t->ready_for_data = true; 682 os_mutex_unlock(&t->mutex); 683}