The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at prediction 2068 lines 66 kB view raw
1// Copyright 2018, Philipp Zabel. 2// Copyright 2020-2021, N Madsen. 3// Copyright 2020-2023, Collabora, Ltd. 4// SPDX-License-Identifier: BSL-1.0 5/*! 6 * @file 7 * @brief Driver code for a WMR HMD. 8 * @author Philipp Zabel <philipp.zabel@gmail.com> 9 * @author nima01 <nima_zero_one@protonmail.com> 10 * @author Jakob Bornecrantz <jakob@collabora.com> 11 * @author Mateo de Mayo <mateo.demayo@collabora.com> 12 * @author Nova King <technobaboo@proton.me> 13 * @ingroup drv_wmr 14 */ 15 16#include "xrt/xrt_config_drivers.h" 17#include "xrt/xrt_config_have.h" 18#include "xrt/xrt_config_build.h" 19#include "xrt/xrt_config_os.h" 20#include "xrt/xrt_defines.h" 21#include "xrt/xrt_device.h" 22#include "xrt/xrt_tracking.h" 23 24#include "os/os_time.h" 25#include "os/os_hid.h" 26 27#include "math/m_api.h" 28#include "math/m_mathinclude.h" 29#include "math/m_predict.h" 30#include "math/m_vec2.h" 31 32#include "util/u_var.h" 33#include "util/u_misc.h" 34#include "util/u_time.h" 35#include "util/u_debug.h" 36#include "util/u_device.h" 37#include "util/u_trace_marker.h" 38#include "util/u_distortion_mesh.h" 39#include "util/u_sink.h" 40 41#ifdef XRT_OS_LINUX 42#include "util/u_linux.h" 43#endif 44 45#include "tracking/t_tracking.h" 46 47#include "wmr_hmd.h" 48#include "wmr_common.h" 49#include "wmr_config_key.h" 50#include "wmr_protocol.h" 51#include "wmr_source.h" 52 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <assert.h> 57#ifndef XRT_OS_WINDOWS 58#include <unistd.h> // for sleep() 59#endif 60 61#ifdef XRT_BUILD_DRIVER_HANDTRACKING 62#include "../multi_wrapper/multi.h" 63#include "../drivers/ht/ht_interface.h" 64#endif 65 66// Unsure if these can change nor how to get them if so 67#define CAMERA_FREQUENCY 30 //!< Observed value (OV7251) 68#define IMU_FREQUENCY 1000 //!< Observed value (ICM20602) 69#define IMU_SAMPLES_PER_PACKET 4 //!< There are 4 samples for each USB IMU packet 70 71//! Specifies whether the user wants to use a SLAM tracker. 72DEBUG_GET_ONCE_BOOL_OPTION(wmr_slam, "WMR_SLAM", true) 73 74//! Specifies whether the user wants to use a SLAM tracker. 75DEBUG_GET_ONCE_NUM_OPTION(sleep_seconds, "WMR_DISPLAY_INIT_SLEEP_SECONDS", 4) 76 77//! Specifies whether the user wants to use the hand tracker. 78DEBUG_GET_ONCE_BOOL_OPTION(wmr_handtracking, "WMR_HANDTRACKING", true) 79 80#ifdef XRT_FEATURE_SLAM 81//! Whether to submit samples to the SLAM tracker from the start. 82DEBUG_GET_ONCE_OPTION(slam_submit_from_start, "SLAM_SUBMIT_FROM_START", NULL) 83#endif 84 85//! Specifies the y offset of the views. 86DEBUG_GET_ONCE_NUM_OPTION(left_view_y_offset, "WMR_LEFT_DISPLAY_VIEW_Y_OFFSET", 0) 87DEBUG_GET_ONCE_NUM_OPTION(right_view_y_offset, "WMR_RIGHT_DISPLAY_VIEW_Y_OFFSET", 0) 88 89 90#define WMR_TRACE(d, ...) U_LOG_XDEV_IFL_T(&d->base, d->log_level, __VA_ARGS__) 91#define WMR_DEBUG(d, ...) U_LOG_XDEV_IFL_D(&d->base, d->log_level, __VA_ARGS__) 92#define WMR_DEBUG_HEX(d, data, data_size) U_LOG_XDEV_IFL_D_HEX(&d->base, d->log_level, data, data_size) 93#define WMR_INFO(d, ...) U_LOG_XDEV_IFL_I(&d->base, d->log_level, __VA_ARGS__) 94#define WMR_WARN(d, ...) U_LOG_XDEV_IFL_W(&d->base, d->log_level, __VA_ARGS__) 95#define WMR_ERROR(d, ...) U_LOG_XDEV_IFL_E(&d->base, d->log_level, __VA_ARGS__) 96 97static int 98wmr_hmd_activate_reverb(struct wmr_hmd *wh); 99static void 100wmr_hmd_deactivate_reverb(struct wmr_hmd *wh); 101static void 102wmr_hmd_screen_enable_reverb(struct wmr_hmd *wh, bool enable); 103static int 104wmr_hmd_activate_odyssey_plus(struct wmr_hmd *wh); 105static void 106wmr_hmd_deactivate_odyssey_plus(struct wmr_hmd *wh); 107static void 108wmr_hmd_screen_enable_odyssey_plus(struct wmr_hmd *wh, bool enable); 109 110const struct wmr_headset_descriptor headset_map[] = { 111 {WMR_HEADSET_GENERIC, NULL, "Unknown WMR HMD", NULL, NULL, NULL}, /* Catch-all for unknown headsets */ 112 {WMR_HEADSET_HP_VR1000, "HP Reverb VR Headset VR1000-1xxx", "HP VR1000", NULL, NULL, NULL}, /*! @todo init funcs */ 113 {WMR_HEADSET_REVERB_G1, "HP Reverb VR Headset VR1000-2xxx", "HP Reverb", wmr_hmd_activate_reverb, 114 wmr_hmd_deactivate_reverb, wmr_hmd_screen_enable_reverb}, 115 {WMR_HEADSET_REVERB_G2, "HP Reverb Virtual Reality Headset G2", "HP Reverb G2", wmr_hmd_activate_reverb, 116 wmr_hmd_deactivate_reverb, wmr_hmd_screen_enable_reverb}, 117 {WMR_HEADSET_SAMSUNG_XE700X3AI, "Samsung Windows Mixed Reality XE700X3AI", "Samsung Odyssey", 118 wmr_hmd_activate_odyssey_plus, wmr_hmd_deactivate_odyssey_plus, wmr_hmd_screen_enable_odyssey_plus}, 119 {WMR_HEADSET_SAMSUNG_800ZAA, "Samsung Windows Mixed Reality 800ZAA", "Samsung Odyssey+", 120 wmr_hmd_activate_odyssey_plus, wmr_hmd_deactivate_odyssey_plus, wmr_hmd_screen_enable_odyssey_plus}, 121 {WMR_HEADSET_LENOVO_EXPLORER, "Lenovo VR-2511N", "Lenovo Explorer", NULL, NULL, NULL}, 122 {WMR_HEADSET_MEDION_ERAZER_X1000, "Medion Erazer X1000", "Medion Erazer", NULL, NULL, NULL}, 123 {WMR_HEADSET_DELL_VISOR, "DELL VR118", "Dell Visor", NULL, NULL, NULL}, 124 {WMR_HEADSET_ACER_AH100, "Acer", "AH100", NULL, NULL, NULL}, 125 {WMR_HEADSET_ACER_AH101, "Acer", "AH101", NULL, NULL, NULL}, 126 {WMR_HEADSET_FUJITSU_FMVHDS1, "Fujitsu", "Fujitsu FMVHDS1", NULL, NULL, NULL}, 127}; 128const int headset_map_n = sizeof(headset_map) / sizeof(headset_map[0]); 129 130 131/* 132 * 133 * Hololens decode packets. 134 * 135 */ 136 137static void 138hololens_sensors_decode_packet(struct wmr_hmd *wh, 139 struct hololens_sensors_packet *pkt, 140 const unsigned char *buffer, 141 int size) 142{ 143 WMR_TRACE(wh, " "); 144 145 if (size != 497 && size != 381) { 146 WMR_ERROR(wh, "invalid hololens sensor packet size (expected 381 or 497 but got %d)", size); 147 return; 148 } 149 150 pkt->id = read8(&buffer); 151 for (int i = 0; i < 4; i++) { 152 pkt->temperature[i] = read16(&buffer); 153 } 154 155 for (int i = 0; i < 4; i++) { 156 pkt->gyro_timestamp[i] = read64(&buffer); 157 } 158 159 for (int i = 0; i < 3; i++) { 160 for (int j = 0; j < 32; j++) { 161 pkt->gyro[i][j] = read16(&buffer); 162 } 163 } 164 165 for (int i = 0; i < 4; i++) { 166 pkt->accel_timestamp[i] = read64(&buffer); 167 } 168 169 for (int i = 0; i < 3; i++) { 170 for (int j = 0; j < 4; j++) { 171 pkt->accel[i][j] = read32(&buffer); 172 } 173 } 174 175 for (int i = 0; i < 4; i++) { 176 pkt->video_timestamp[i] = read64(&buffer); 177 } 178} 179 180static void 181hololens_ensure_controller(struct wmr_hmd *wh, uint8_t controller_id, uint16_t vid, uint16_t pid) 182{ 183 if (controller_id >= WMR_MAX_CONTROLLERS) 184 return; 185 186 if (wh->controller[controller_id] != NULL) { 187 return; 188 } 189 190 WMR_DEBUG(wh, "Adding controller device %d", controller_id); 191 192 enum xrt_device_type controller_type = 193 controller_id == 0 ? XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER : XRT_DEVICE_TYPE_RIGHT_HAND_CONTROLLER; 194 uint8_t hmd_cmd_base = controller_id == 0 ? 0x5 : 0xd; 195 196 struct wmr_hmd_controller_connection *controller = 197 wmr_hmd_controller_create(wh, hmd_cmd_base, controller_type, vid, pid, wh->log_level); 198 199 os_mutex_lock(&wh->controller_status_lock); 200 wh->controller[controller_id] = controller; 201 os_mutex_unlock(&wh->controller_status_lock); 202} 203 204/* 205 * 206 * Hololens packets. 207 * 208 */ 209 210static void 211hololens_handle_unknown(struct wmr_hmd *wh, const unsigned char *buffer, int size) 212{ 213 DRV_TRACE_MARKER(); 214 215 WMR_DEBUG(wh, "Unknown hololens sensors message type: %02x, (%i)", buffer[0], size); 216} 217 218static void 219hololens_handle_control(struct wmr_hmd *wh, const unsigned char *buffer, int size) 220{ 221 DRV_TRACE_MARKER(); 222 223 WMR_DEBUG(wh, "WMR_MS_HOLOLENS_MSG_CONTROL: %02x, (%i)", buffer[0], size); 224} 225 226static void 227hololens_handle_controller_status_packet(struct wmr_hmd *wh, const unsigned char *buffer, int size) 228{ 229 DRV_TRACE_MARKER(); 230 231 if (size < 3) { 232 WMR_DEBUG(wh, "Got small packet 0x17 (%i)", size); 233 return; 234 } 235 236 uint8_t controller_id = buffer[1]; 237 uint8_t pkt_type = buffer[2]; 238 239 switch (pkt_type) { 240 case WMR_CONTROLLER_STATUS_UNPAIRED: { 241 WMR_TRACE(wh, "Controller %d is not paired", controller_id); 242 break; 243 } 244 case WMR_CONTROLLER_STATUS_OFFLINE: { 245 if (size < 7) { 246 WMR_TRACE(wh, "Got small controller offline status packet (%i)", size); 247 return; 248 } 249 250 /* Skip packet type, controller id, presence */ 251 buffer += 3; 252 253 uint16_t vid = read16(&buffer); 254 uint16_t pid = read16(&buffer); 255 WMR_TRACE(wh, "Controller %d offline. VID 0x%04x PID 0x%04x", controller_id, vid, pid); 256 break; 257 } 258 case WMR_CONTROLLER_STATUS_ONLINE: { 259 if (size < 7) { 260 WMR_TRACE(wh, "Got small controller online status packet (%i)", size); 261 return; 262 } 263 264 /* Skip packet type, controller id, presence */ 265 buffer += 3; 266 267 uint16_t vid = read16(&buffer); 268 uint16_t pid = read16(&buffer); 269 270 if (size >= 10) { 271 uint8_t unknown1 = read8(&buffer); 272 uint16_t unknown2160 = read16(&buffer); 273 WMR_TRACE(wh, "Controller %d online. VID 0x%04x PID 0x%04x val1 %u val2 %u", controller_id, vid, 274 pid, unknown1, unknown2160); 275 } else { 276 WMR_TRACE(wh, "Controller %d online. VID 0x%04x PID 0x%04x", controller_id, vid, pid); 277 } 278 279 hololens_ensure_controller(wh, controller_id, vid, pid); 280 break; 281 } 282 default: // 283 WMR_DEBUG(wh, "Unknown controller status packet (%i) type 0x%02x", size, pkt_type); 284 break; 285 } 286 287 os_mutex_lock(&wh->controller_status_lock); 288 if (controller_id == 0) 289 wh->have_left_controller_status = true; 290 else if (controller_id == 1) 291 wh->have_right_controller_status = true; 292 if (wh->have_left_controller_status && wh->have_right_controller_status) 293 os_cond_signal(&wh->controller_status_cond); 294 os_mutex_unlock(&wh->controller_status_lock); 295} 296 297static void 298hololens_handle_bt_iface_packet(struct wmr_hmd *wh, const unsigned char *buffer, int size) 299{ 300 DRV_TRACE_MARKER(); 301 302 int pkt_type; 303 304 if (size < 2) 305 return; 306 307 if (size < 6) { 308 WMR_DEBUG(wh, "Short Bluetooth interface packet (%d) type 0x%02x", size, buffer[1]); 309 return; 310 } 311 312 pkt_type = buffer[1]; 313 if (pkt_type != WMR_BT_IFACE_MSG_DEBUG) { 314 WMR_DEBUG(wh, "Unknown Bluetooth interface packet (%d) type 0x%02x", size, pkt_type); 315 WMR_DEBUG_HEX(wh, buffer, size); 316 return; 317 } 318 buffer += 2; 319 320 uint16_t tag = read16(&buffer); 321 uint16_t msg_len = read16(&buffer); 322 323 if (size < msg_len + 6) { 324 WMR_DEBUG(wh, "Bluetooth interface debug packet (%d) too short. tag 0x%x msg len %u", size, tag, 325 msg_len); 326 return; 327 } 328 329 WMR_DEBUG(wh, "BT debug: tag %d: %.*s", tag, msg_len, buffer); 330} 331 332static void 333hololens_handle_controller_packet(struct wmr_hmd *wh, const unsigned char *buffer, int size) 334{ 335 if (size < 45) { 336 WMR_TRACE(wh, "Got unknown short controller packet (%i)\n\t%02x", size, buffer[0]); 337 return; 338 } 339 340 uint8_t packet_id = buffer[0]; 341 struct wmr_controller_connection *controller = NULL; 342 343 if (packet_id == WMR_MS_HOLOLENS_MSG_LEFT_CONTROLLER) { 344 controller = (struct wmr_controller_connection *)wh->controller[0]; 345 } else if (packet_id == WMR_MS_HOLOLENS_MSG_RIGHT_CONTROLLER) { 346 controller = (struct wmr_controller_connection *)wh->controller[1]; 347 } 348 349 if (controller == NULL) 350 return; /* Controller online message not yet seen */ 351 352 uint64_t now_ns = os_monotonic_get_ns(); 353 wmr_controller_connection_receive_bytes(controller, now_ns, (uint8_t *)buffer, size); 354} 355 356static void 357hololens_handle_debug(struct wmr_hmd *wh, const unsigned char *buffer, int size) 358{ 359 DRV_TRACE_MARKER(); 360 361 if (size < 12) { 362 WMR_TRACE(wh, "Got short debug packet (%i) 0x%02x", size, buffer[0]); 363 return; 364 } 365 buffer += 1; 366 367 uint32_t magic = read32(&buffer); 368 if (magic != WMR_MAGIC) { 369 WMR_TRACE(wh, "Debug packet (%i) 0x%02x had strange magic 0x%08x", size, buffer[0], magic); 370 return; 371 } 372 uint32_t timestamp = read32(&buffer); 373 uint16_t seq = read16(&buffer); 374 uint8_t src_tag = read8(&buffer); 375 int msg_len = size - 12; 376 377 WMR_DEBUG(wh, "HMD debug: TS %f seq %u src %d: %.*s", timestamp / 1000.0, seq, src_tag, msg_len, buffer); 378} 379 380static void 381hololens_handle_sensors_avg(struct wmr_hmd *wh, const unsigned char *buffer, int size) 382{ 383 DRV_TRACE_MARKER(); 384 385 // Get the timing as close to reading the packet as possible. 386 uint64_t now_ns = os_monotonic_get_ns(); 387 388 hololens_sensors_decode_packet(wh, &wh->packet, buffer, size); 389 390 // Use a single averaged sample from all the samples in the packet 391 struct xrt_vec3 avg_raw_accel = XRT_VEC3_ZERO; 392 struct xrt_vec3 avg_raw_gyro = XRT_VEC3_ZERO; 393 for (int i = 0; i < IMU_SAMPLES_PER_PACKET; i++) { 394 struct xrt_vec3 a = XRT_VEC3_ZERO; 395 struct xrt_vec3 g = XRT_VEC3_ZERO; 396 vec3_from_hololens_accel(wh->packet.accel, i, &a); 397 vec3_from_hololens_gyro(wh->packet.gyro, i, &g); 398 math_vec3_accum(&a, &avg_raw_accel); 399 math_vec3_accum(&g, &avg_raw_gyro); 400 } 401 math_vec3_scalar_mul(1.0f / IMU_SAMPLES_PER_PACKET, &avg_raw_accel); 402 math_vec3_scalar_mul(1.0f / IMU_SAMPLES_PER_PACKET, &avg_raw_gyro); 403 404 // Calibrate averaged sample 405 struct xrt_vec3 avg_calib_accel = XRT_VEC3_ZERO; 406 struct xrt_vec3 avg_calib_gyro = XRT_VEC3_ZERO; 407 math_matrix_3x3_transform_vec3(&wh->config.sensors.accel.mix_matrix, &avg_raw_accel, &avg_calib_accel); 408 math_matrix_3x3_transform_vec3(&wh->config.sensors.gyro.mix_matrix, &avg_raw_gyro, &avg_calib_gyro); 409 math_vec3_accum(&wh->config.sensors.accel.bias_offsets, &avg_calib_accel); 410 math_vec3_accum(&wh->config.sensors.gyro.bias_offsets, &avg_calib_gyro); 411 math_quat_rotate_vec3(&wh->config.sensors.transforms.P_oxr_acc.orientation, &avg_calib_accel, &avg_calib_accel); 412 math_quat_rotate_vec3(&wh->config.sensors.transforms.P_oxr_gyr.orientation, &avg_calib_gyro, &avg_calib_gyro); 413 414 // Fusion tracking 415 os_mutex_lock(&wh->fusion.mutex); 416 timepoint_ns t = wh->packet.gyro_timestamp[IMU_SAMPLES_PER_PACKET - 1] * WMR_MS_HOLOLENS_NS_PER_TICK; 417 m_imu_3dof_update(&wh->fusion.i3dof, t, &avg_calib_accel, &avg_calib_gyro); 418 wh->fusion.last_imu_timestamp_ns = now_ns; 419 wh->fusion.last_angular_velocity = avg_calib_gyro; 420 os_mutex_unlock(&wh->fusion.mutex); 421 422 // SLAM tracking 423 wmr_source_push_imu_packet(wh->tracking.source, t, avg_raw_accel, avg_raw_gyro); 424} 425 426static void 427hololens_handle_sensors_all(struct wmr_hmd *wh, const unsigned char *buffer, int size) 428{ 429 DRV_TRACE_MARKER(); 430 431 // Get the timing as close to reading the packet as possible. 432 uint64_t now_ns = os_monotonic_get_ns(); 433 434 hololens_sensors_decode_packet(wh, &wh->packet, buffer, size); 435 436 struct xrt_vec3 raw_gyro[IMU_SAMPLES_PER_PACKET]; 437 struct xrt_vec3 raw_accel[IMU_SAMPLES_PER_PACKET]; 438 struct xrt_vec3 calib_gyro[IMU_SAMPLES_PER_PACKET]; 439 struct xrt_vec3 calib_accel[IMU_SAMPLES_PER_PACKET]; 440 441 for (int i = 0; i < IMU_SAMPLES_PER_PACKET; i++) { 442 struct xrt_vec3 *rg = &raw_gyro[i]; 443 struct xrt_vec3 *cg = &calib_gyro[i]; 444 vec3_from_hololens_gyro(wh->packet.gyro, i, rg); 445 math_matrix_3x3_transform_vec3(&wh->config.sensors.gyro.mix_matrix, rg, cg); 446 math_vec3_accum(&wh->config.sensors.gyro.bias_offsets, cg); 447 math_quat_rotate_vec3(&wh->config.sensors.transforms.P_oxr_gyr.orientation, cg, cg); 448 449 struct xrt_vec3 *ra = &raw_accel[i]; 450 struct xrt_vec3 *ca = &calib_accel[i]; 451 vec3_from_hololens_accel(wh->packet.accel, i, ra); 452 math_matrix_3x3_transform_vec3(&wh->config.sensors.accel.mix_matrix, ra, ca); 453 math_vec3_accum(&wh->config.sensors.accel.bias_offsets, ca); 454 math_quat_rotate_vec3(&wh->config.sensors.transforms.P_oxr_acc.orientation, ca, ca); 455 } 456 457 // Fusion tracking 458 os_mutex_lock(&wh->fusion.mutex); 459 for (int i = 0; i < IMU_SAMPLES_PER_PACKET; i++) { 460 m_imu_3dof_update( // 461 &wh->fusion.i3dof, // 462 wh->packet.gyro_timestamp[i] * WMR_MS_HOLOLENS_NS_PER_TICK, // 463 &calib_accel[i], // 464 &calib_gyro[i]); // 465 } 466 wh->fusion.last_imu_timestamp_ns = now_ns; 467 wh->fusion.last_angular_velocity = calib_gyro[3]; 468 os_mutex_unlock(&wh->fusion.mutex); 469 470 // SLAM tracking 471 for (int i = 0; i < IMU_SAMPLES_PER_PACKET; i++) { 472 timepoint_ns t = wh->packet.gyro_timestamp[i] * WMR_MS_HOLOLENS_NS_PER_TICK; 473 wmr_source_push_imu_packet(wh->tracking.source, t, raw_accel[i], raw_gyro[i]); 474 } 475} 476 477static void 478hololens_handle_sensors(struct wmr_hmd *wh, const unsigned char *buffer, int size) 479{ 480 if (wh->average_imus) { 481 // Less overhead and jitter. 482 hololens_handle_sensors_avg(wh, buffer, size); 483 } else { 484 // More sophisticated fusion algorithms might work better with raw data. 485 hololens_handle_sensors_all(wh, buffer, size); 486 } 487} 488 489static bool 490hololens_sensors_read_packets(struct wmr_hmd *wh) 491{ 492 DRV_TRACE_MARKER(); 493 494 WMR_TRACE(wh, " "); 495 496 unsigned char buffer[WMR_FEATURE_BUFFER_SIZE]; 497 498 // Block for 100ms 499 os_mutex_lock(&wh->hid_lock); 500 int size = os_hid_read(wh->hid_hololens_sensors_dev, buffer, sizeof(buffer), 100); 501 os_mutex_unlock(&wh->hid_lock); 502 503 if (size < 0) { 504 WMR_ERROR(wh, "Error reading from Hololens Sensors device. Call to os_hid_read returned %i", size); 505 return false; 506 } 507 if (size == 0) { 508 WMR_TRACE(wh, "No more data to read"); 509 return true; // No more messages, return. 510 } else { 511 WMR_TRACE(wh, "Read %u bytes", size); 512 } 513 514 switch (buffer[0]) { 515 case WMR_MS_HOLOLENS_MSG_SENSORS: // 516 hololens_handle_sensors(wh, buffer, size); 517 break; 518 case WMR_MS_HOLOLENS_MSG_BT_IFACE: // 519 hololens_handle_bt_iface_packet(wh, buffer, size); 520 break; 521 case WMR_MS_HOLOLENS_MSG_LEFT_CONTROLLER: 522 case WMR_MS_HOLOLENS_MSG_RIGHT_CONTROLLER: // 523 hololens_handle_controller_packet(wh, buffer, size); 524 break; 525 case WMR_MS_HOLOLENS_MSG_CONTROLLER_STATUS: // 526 hololens_handle_controller_status_packet(wh, buffer, size); 527 break; 528 case WMR_MS_HOLOLENS_MSG_CONTROL: // 529 hololens_handle_control(wh, buffer, size); 530 break; 531 case WMR_MS_HOLOLENS_MSG_DEBUG: // 532 hololens_handle_debug(wh, buffer, size); 533 break; 534 default: // 535 hololens_handle_unknown(wh, buffer, size); 536 break; 537 } 538 539 return true; 540} 541 542 543/* 544 * 545 * Control packets. 546 * 547 */ 548 549static void 550control_ipd_value_decode(struct wmr_hmd *wh, const unsigned char *buffer, int size) 551{ 552 if (size != 2 && size != 4) { 553 WMR_ERROR(wh, "Invalid control ipd distance packet size (expected 4 but got %i)", size); 554 return; 555 } 556 557 uint8_t id = read8(&buffer); 558 if (id != 0x1) { 559 WMR_ERROR(wh, "Invalid control IPD distance packet ID (expected 0x1 but got %u)", id); 560 return; 561 } 562 563 uint8_t proximity = read8(&buffer); 564 uint16_t ipd_value = (size == 4) ? read16(&buffer) : wh->raw_ipd; 565 566 bool changed = (wh->raw_ipd != ipd_value) || (wh->proximity_sensor != proximity); 567 568 wh->raw_ipd = ipd_value; 569 wh->proximity_sensor = proximity; 570 571 if (changed) { 572 WMR_DEBUG(wh, "Proximity sensor %d IPD: %d", proximity, ipd_value); 573 } 574} 575 576static bool 577control_read_packets(struct wmr_hmd *wh) 578{ 579 DRV_TRACE_MARKER(); 580 581 unsigned char buffer[WMR_FEATURE_BUFFER_SIZE]; 582 583 // Do not block 584 os_mutex_lock(&wh->hid_lock); 585 int size = os_hid_read(wh->hid_control_dev, buffer, sizeof(buffer), 0); 586 os_mutex_unlock(&wh->hid_lock); 587 588 if (size < 0) { 589 WMR_ERROR(wh, "Error reading from companion (HMD control) device. Call to os_hid_read returned %i", 590 size); 591 return false; 592 } 593 if (size == 0) { 594 WMR_TRACE(wh, "No more data to read"); 595 return true; // No more messages, return. 596 } else { 597 WMR_TRACE(wh, "Read %u bytes", size); 598 } 599 600 DRV_TRACE_IDENT(control_packet_got); 601 602 switch (buffer[0]) { 603 case WMR_CONTROL_MSG_IPD_VALUE: // 604 control_ipd_value_decode(wh, buffer, size); 605 break; 606 case WMR_CONTROL_MSG_UNKNOWN_02: // 607 WMR_DEBUG(wh, "Unknown message type: %02x (size %i)", buffer[0], size); 608 if (size == 4) { 609 // Todo: Decode. 610 // On Reverb G1 this message is sometimes received right after a 611 // proximity/IPD message, and it always seems to be '02 XX 0d 26'. 612 WMR_DEBUG(wh, "---> Type and content bytes: %02x %02x %02x %02x", buffer[0], buffer[1], 613 buffer[2], buffer[3]); 614 } 615 break; 616 case WMR_CONTROL_MSG_DEVICE_STATUS: // 617 WMR_DEBUG(wh, "Device status message type: %02x (size %i)", buffer[0], size); 618 if (size != 11) { 619 WMR_DEBUG(wh, 620 "---> Unexpected message size. Expected 11 bytes incl. message type. Got %d bytes", 621 size); 622 WMR_DEBUG_HEX(wh, buffer, size); 623 if (size < 11) { 624 break; 625 } 626 } 627 628 // Todo: HMD state info to be decoded further. 629 // On Reverb G1 this message is received twice after having sent an 'enable screen' command to the HMD 630 // companion device. The first one is received promptly. The second one is received a few seconds later 631 // once the HMD screen backlight visibly powers on. 632 // 1st message: '05 00 01 01 00 00 00 00 00 00 00' 633 // 2nd message: '05 01 01 01 01 00 00 00 00 00 00' 634 WMR_DEBUG(wh, "---> Type and content bytes: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", 635 buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], 636 buffer[8], buffer[9], buffer[10]); 637 WMR_DEBUG(wh, 638 "---> Flags decoded so far: [type: %02x] [display_ready: %02x] [?] [?] [display_ready: %02x] " 639 "[?] [?] [?] [?] [?] [?]", 640 buffer[0], buffer[1], buffer[4]); 641 642 break; 643 default: // 644 WMR_DEBUG(wh, "Unknown message type: %02x (size %i)", buffer[0], size); 645 WMR_DEBUG_HEX(wh, buffer, size); 646 break; 647 } 648 649 return true; 650} 651 652 653/* 654 * 655 * Helpers and internal functions. 656 * 657 */ 658 659static void * 660wmr_run_thread(void *ptr) 661{ 662 struct wmr_hmd *wh = (struct wmr_hmd *)ptr; 663 664 U_TRACE_SET_THREAD_NAME("WMR: USB-HMD"); 665 os_thread_helper_name(&wh->oth, "WMR: USB-HMD"); 666 667#ifdef XRT_OS_LINUX 668 // Try to raise priority of this thread. 669 u_linux_try_to_set_realtime_priority_on_thread(wh->log_level, "WMR: USB-HMD"); 670#endif 671 672 673 os_thread_helper_lock(&wh->oth); 674 while (os_thread_helper_is_running_locked(&wh->oth)) { 675 os_thread_helper_unlock(&wh->oth); 676 677 // Does not block. 678 if (!control_read_packets(wh)) { 679 break; 680 } 681 682 // Does block for a bit. 683 if (!hololens_sensors_read_packets(wh)) { 684 break; 685 } 686 os_thread_helper_lock(&wh->oth); 687 } 688 os_thread_helper_unlock(&wh->oth); 689 690 WMR_DEBUG(wh, "Exiting reading thread."); 691 692 return NULL; 693} 694 695static void 696hololens_sensors_enable_imu(struct wmr_hmd *wh) 697{ 698 DRV_TRACE_MARKER(); 699 700 os_mutex_lock(&wh->hid_lock); 701 int size = os_hid_write(wh->hid_hololens_sensors_dev, hololens_sensors_imu_on, sizeof(hololens_sensors_imu_on)); 702 os_mutex_unlock(&wh->hid_lock); 703 704 if (size <= 0) { 705 WMR_ERROR(wh, "Error writing to device"); 706 return; 707 } 708} 709 710#define HID_SEND(hmd, HID, DATA, STR) \ 711 do { \ 712 os_mutex_lock(&hmd->hid_lock); \ 713 int _ret = os_hid_set_feature(HID, DATA, sizeof(DATA)); \ 714 os_mutex_unlock(&hmd->hid_lock); \ 715 if (_ret < 0) { \ 716 WMR_ERROR(wh, "Send (%s): %i", STR, _ret); \ 717 } \ 718 } while (false); 719 720#define HID_GET(hmd, HID, DATA, STR) \ 721 do { \ 722 os_mutex_lock(&hmd->hid_lock); \ 723 int _ret = os_hid_get_feature(HID, DATA[0], DATA, sizeof(DATA)); \ 724 os_mutex_unlock(&hmd->hid_lock); \ 725 if (_ret < 0) { \ 726 WMR_ERROR(wh, "Get (%s): %i", STR, _ret); \ 727 } else { \ 728 WMR_DEBUG(wh, "0x%02x HID feature returned", DATA[0]); \ 729 WMR_DEBUG_HEX(wh, DATA, _ret); \ 730 } \ 731 } while (false); 732 733static int 734wmr_hmd_activate_reverb(struct wmr_hmd *wh) 735{ 736 DRV_TRACE_MARKER(); 737 738 struct os_hid_device *hid = wh->hid_control_dev; 739 740 WMR_TRACE(wh, "Activating HP Reverb G1/G2 HMD..."); 741 742 // Hack to power up the Reverb G1 display, thanks to OpenHMD contributors. 743 // Sleep before we start seems to improve reliability. 744 // 300ms is what Windows seems to do, so cargo cult that. 745 os_nanosleep(U_TIME_1MS_IN_NS * 300); 746 747 for (int i = 0; i < 4; i++) { 748 unsigned char cmd[64] = {0x50, 0x01}; 749 HID_SEND(wh, hid, cmd, "loop"); 750 751 unsigned char data[64] = {0x50}; 752 HID_GET(wh, hid, data, "loop"); 753 754 os_nanosleep(U_TIME_1MS_IN_NS * 10); // Sleep 10ms 755 } 756 757 unsigned char data[64] = {0x09}; 758 HID_GET(wh, hid, data, "data_1"); 759 760 data[0] = 0x08; 761 HID_GET(wh, hid, data, "data_2"); 762 763 data[0] = 0x06; 764 HID_GET(wh, hid, data, "data_3"); 765 766 WMR_INFO(wh, "Sent activation report."); 767 768 // Enable the HMD screen now, if required. Otherwise, if screen should initially be disabled, then 769 // proactively disable it now. Why? Because some cases of irregular termination of Monado will 770 // leave either the 'Hololens Sensors' device or its 'companion' device alive across restarts. 771 wmr_hmd_screen_enable_reverb(wh, wh->hmd_screen_enable); 772 773 // Allow time for enumeration of available displays by host system, so the compositor can select among them. 774 WMR_INFO(wh, 775 "Sleep until the HMD display is powered up, so the available displays can be enumerated by the host " 776 "system."); 777 778 // Get the sleep amount, then sleep. One or two seconds was not enough. 779 uint64_t seconds = debug_get_num_option_sleep_seconds(); 780 os_nanosleep(U_TIME_1S_IN_NS * seconds); 781 782 return 0; 783} 784 785static void 786wmr_hmd_refresh_debug_gui(struct wmr_hmd *wh) 787{ 788 // Update debug GUI button labels. 789 if (wh) { 790 struct u_var_button *btn = &wh->gui.hmd_screen_enable_btn; 791 snprintf(btn->label, sizeof(btn->label), 792 wh->hmd_screen_enable ? "HMD Screen [On]" : "HMD Screen [Off]"); 793 } 794} 795 796static void 797wmr_hmd_deactivate_reverb(struct wmr_hmd *wh) 798{ 799 DRV_TRACE_MARKER(); 800 801 // Turn the screen off 802 wmr_hmd_screen_enable_reverb(wh, false); 803 804 //! @todo Power down IMU, and maybe more. 805} 806 807static void 808wmr_hmd_screen_enable_reverb(struct wmr_hmd *wh, bool enable) 809{ 810 DRV_TRACE_MARKER(); 811 812 struct os_hid_device *hid = wh->hid_control_dev; 813 814 unsigned char cmd[2] = {0x04, 0x00}; 815 if (enable) { 816 cmd[1] = enable ? 0x01 : 0x00; 817 } 818 819 HID_SEND(wh, hid, cmd, (enable ? "screen_on" : "screen_off")); 820 821 wh->hmd_screen_enable = enable; 822 823 // Update debug GUI button labels. 824 wmr_hmd_refresh_debug_gui(wh); 825} 826 827static int 828wmr_hmd_activate_odyssey_plus(struct wmr_hmd *wh) 829{ 830 DRV_TRACE_MARKER(); 831 832 struct os_hid_device *hid = wh->hid_control_dev; 833 834 WMR_TRACE(wh, "Activating Odyssey HMD..."); 835 836 os_nanosleep(U_TIME_1MS_IN_NS * 300); 837 838 unsigned char data[64] = {0x16}; 839 HID_GET(wh, hid, data, "data_1"); 840 841 data[0] = 0x15; 842 HID_GET(wh, hid, data, "data_2"); 843 844 data[0] = 0x14; 845 HID_GET(wh, hid, data, "data_3"); 846 847 // Enable the HMD screen now, if required. Otherwise, if screen should initially be disabled, then 848 // proactively disable it now. Why? Because some cases of irregular termination of Monado will 849 // leave either the 'Hololens Sensors' device or its 'companion' device alive across restarts. 850 wmr_hmd_screen_enable_odyssey_plus(wh, wh->hmd_screen_enable); 851 852 // Allow time for enumeration of available displays by host system, so the compositor can select among them. 853 WMR_INFO(wh, 854 "Sleep until the HMD display is powered up, so the available displays can be enumerated by the host " 855 "system."); 856 857 os_nanosleep(3LL * U_TIME_1S_IN_NS); 858 859 return 0; 860} 861 862static void 863wmr_hmd_deactivate_odyssey_plus(struct wmr_hmd *wh) 864{ 865 DRV_TRACE_MARKER(); 866 867 // Turn the screen off 868 wmr_hmd_screen_enable_odyssey_plus(wh, false); 869 870 //! @todo Power down IMU, and maybe more. 871} 872 873static void 874wmr_hmd_screen_enable_odyssey_plus(struct wmr_hmd *wh, bool enable) 875{ 876 DRV_TRACE_MARKER(); 877 878 struct os_hid_device *hid = wh->hid_control_dev; 879 880 unsigned char cmd[2] = {0x12, 0x00}; 881 if (enable) { 882 cmd[1] = enable ? 0x01 : 0x00; 883 } 884 885 HID_SEND(wh, hid, cmd, (enable ? "screen_on" : "screen_off")); 886 887 wh->hmd_screen_enable = enable; 888 889 // Update debug GUI button labels. 890 wmr_hmd_refresh_debug_gui(wh); 891} 892 893static void 894wmr_hmd_screen_enable_toggle(void *wh_ptr) 895{ 896 struct wmr_hmd *wh = (struct wmr_hmd *)wh_ptr; 897 if (wh && wh->hmd_desc && wh->hmd_desc->screen_enable_func) { 898 wh->hmd_desc->screen_enable_func(wh, !wh->hmd_screen_enable); 899 } 900} 901 902/* 903 * 904 * Config functions. 905 * 906 */ 907 908static int 909wmr_config_command_sync(struct wmr_hmd *wh, unsigned char type, unsigned char *buf, int len) 910{ 911 DRV_TRACE_MARKER(); 912 913 struct os_hid_device *hid = wh->hid_hololens_sensors_dev; 914 915 unsigned char cmd[64] = {0x02, type}; 916 os_hid_write(hid, cmd, sizeof(cmd)); 917 918 do { 919 int size = os_hid_read(hid, buf, len, 100); 920 if (size < 1) { 921 return -1; 922 } 923 if (buf[0] == WMR_MS_HOLOLENS_MSG_CONTROL) { 924 return size; 925 } 926 } while (true); 927 928 return -1; 929} 930 931static int 932wmr_read_config_part(struct wmr_hmd *wh, unsigned char type, unsigned char *data, int len) 933{ 934 DRV_TRACE_MARKER(); 935 936 unsigned char buf[33]; 937 int offset = 0; 938 int size; 939 940 size = wmr_config_command_sync(wh, 0x0b, buf, sizeof(buf)); 941 if (size != 33 || buf[0] != 0x02) { 942 WMR_ERROR(wh, "Failed to issue command 0b: %02x %02x %02x", buf[0], buf[1], buf[2]); 943 return -1; 944 } 945 946 size = wmr_config_command_sync(wh, type, buf, sizeof(buf)); 947 if (size != 33 || buf[0] != 0x02) { 948 WMR_ERROR(wh, "Failed to issue command %02x: %02x %02x %02x", type, buf[0], buf[1], buf[2]); 949 return -1; 950 } 951 952 while (true) { 953 size = wmr_config_command_sync(wh, 0x08, buf, sizeof(buf)); 954 if (size != 33 || (buf[1] != 0x01 && buf[1] != 0x02)) { 955 WMR_ERROR(wh, "Failed to issue command 08: %02x %02x %02x", buf[0], buf[1], buf[2]); 956 return -1; 957 } 958 959 if (buf[1] != 0x01) { 960 break; 961 } 962 963 if (buf[2] > len || offset + buf[2] > len) { 964 WMR_ERROR(wh, "Getting more information then requested"); 965 return -1; 966 } 967 968 memcpy(data + offset, buf + 3, buf[2]); 969 offset += buf[2]; 970 } 971 972 return offset; 973} 974 975XRT_MAYBE_UNUSED static int 976wmr_read_config_raw(struct wmr_hmd *wh, uint8_t **out_data, size_t *out_size) 977{ 978 DRV_TRACE_MARKER(); 979 980 unsigned char meta[84]; 981 uint8_t *data; 982 int size; 983 int data_size; 984 985 size = wmr_read_config_part(wh, 0x06, meta, sizeof(meta)); 986 WMR_DEBUG(wh, "(0x06, meta) => %d", size); 987 988 if (size < 0) { 989 return -1; 990 } 991 992 /* 993 * No idea what the other 64 bytes of metadata are, but the first two 994 * seem to be little endian size of the data store. 995 */ 996 data_size = meta[0] | (meta[1] << 8); 997 data = calloc(1, data_size + 1); 998 if (!data) { 999 return -1; 1000 } 1001 data[data_size] = '\0'; 1002 1003 size = wmr_read_config_part(wh, 0x04, data, data_size); 1004 WMR_DEBUG(wh, "(0x04, data) => %d", size); 1005 if (size < 0) { 1006 free(data); 1007 return -1; 1008 } 1009 1010 WMR_DEBUG(wh, "Read %d-byte config data", data_size); 1011 1012 *out_data = data; 1013 *out_size = size; 1014 1015 return 0; 1016} 1017 1018static int 1019wmr_read_config(struct wmr_hmd *wh) 1020{ 1021 DRV_TRACE_MARKER(); 1022 1023 unsigned char *data = NULL; 1024 unsigned char *config_json_block; 1025 size_t data_size; 1026 int ret; 1027 1028 // Read config 1029 ret = wmr_read_config_raw(wh, &data, &data_size); 1030 if (ret < 0) 1031 return ret; 1032 1033 /* De-obfuscate the JSON config */ 1034 /* FIXME: The header contains little-endian values that need swapping for big-endian */ 1035 struct wmr_config_header *hdr = (struct wmr_config_header *)data; 1036 1037 /* Take a copy of the header */ 1038 memcpy(&wh->config_hdr, hdr, sizeof(struct wmr_config_header)); 1039 1040 WMR_INFO(wh, "Manufacturer: %.*s", (int)sizeof(hdr->manufacturer), hdr->manufacturer); 1041 WMR_INFO(wh, "Device: %.*s", (int)sizeof(hdr->device), hdr->device); 1042 WMR_INFO(wh, "Serial: %.*s", (int)sizeof(hdr->serial), hdr->serial); 1043 WMR_INFO(wh, "UID: %.*s", (int)sizeof(hdr->uid), hdr->uid); 1044 WMR_INFO(wh, "Name: %.*s", (int)sizeof(hdr->name), hdr->name); 1045 WMR_INFO(wh, "Revision: %.*s", (int)sizeof(hdr->revision), hdr->revision); 1046 WMR_INFO(wh, "Revision Date: %.*s", (int)sizeof(hdr->revision_date), hdr->revision_date); 1047 1048 snprintf(wh->base.str, XRT_DEVICE_NAME_LEN, "%.*s", (int)sizeof(hdr->name), hdr->name); 1049 1050 if (hdr->json_start >= data_size || (data_size - hdr->json_start) < hdr->json_size) { 1051 WMR_ERROR(wh, "Invalid WMR config block - incorrect sizes"); 1052 free(data); 1053 return -1; 1054 } 1055 1056 config_json_block = data + hdr->json_start + sizeof(uint16_t); 1057 for (unsigned int i = 0; i < hdr->json_size - sizeof(uint16_t); i++) { 1058 config_json_block[i] ^= wmr_config_key[i % sizeof(wmr_config_key)]; 1059 } 1060 1061 WMR_DEBUG(wh, "JSON config:\n%s", config_json_block); 1062 1063 if (!wmr_hmd_config_parse(&wh->config, (char *)config_json_block, wh->log_level)) { 1064 free(data); 1065 return -1; 1066 } 1067 1068 free(data); 1069 return 0; 1070} 1071 1072/* 1073 * 1074 * Device members. 1075 * 1076 */ 1077 1078static xrt_result_t 1079wmr_hmd_get_3dof_tracked_pose(struct xrt_device *xdev, 1080 enum xrt_input_name name, 1081 uint64_t at_timestamp_ns, 1082 struct xrt_space_relation *out_relation) 1083{ 1084 DRV_TRACE_MARKER(); 1085 1086 struct wmr_hmd *wh = wmr_hmd(xdev); 1087 1088 if (name != XRT_INPUT_GENERIC_HEAD_POSE) { 1089 U_LOG_XDEV_UNSUPPORTED_INPUT(&wh->base, wh->log_level, name); 1090 return XRT_ERROR_INPUT_UNSUPPORTED; 1091 } 1092 1093 // Variables needed for prediction. 1094 uint64_t last_imu_timestamp_ns = 0; 1095 struct xrt_space_relation relation = {0}; 1096 relation.relation_flags = XRT_SPACE_RELATION_BITMASK_ALL; 1097 relation.pose.position = wh->pose.position; 1098 relation.linear_velocity = (struct xrt_vec3){0, 0, 0}; 1099 1100 // Get data while holding the lock. 1101 os_mutex_lock(&wh->fusion.mutex); 1102 relation.pose.orientation = wh->fusion.i3dof.rot; 1103 relation.angular_velocity = wh->fusion.last_angular_velocity; 1104 last_imu_timestamp_ns = wh->fusion.last_imu_timestamp_ns; 1105 os_mutex_unlock(&wh->fusion.mutex); 1106 1107 // No prediction needed. 1108 if (at_timestamp_ns < last_imu_timestamp_ns) { 1109 *out_relation = relation; 1110 return XRT_SUCCESS; 1111 } 1112 1113 uint64_t prediction_ns = at_timestamp_ns - last_imu_timestamp_ns; 1114 double prediction_s = time_ns_to_s(prediction_ns); 1115 1116 m_predict_relation(&relation, prediction_s, out_relation); 1117 wh->pose = out_relation->pose; 1118 1119 return XRT_SUCCESS; 1120} 1121 1122//! Specific pose corrections for Basalt and a WMR headset 1123XRT_MAYBE_UNUSED static inline struct xrt_pose 1124wmr_hmd_correct_pose_from_basalt(struct xrt_pose pose) 1125{ 1126 struct xrt_quat q = {0.70710678, 0, 0, 0.70710678}; 1127 math_quat_rotate(&q, &pose.orientation, &pose.orientation); 1128 math_quat_rotate_vec3(&q, &pose.position, &pose.position); 1129 1130 // Correct swapped axes 1131 pose.position.y = -pose.position.y; 1132 pose.position.z = -pose.position.z; 1133 pose.orientation.y = -pose.orientation.y; 1134 pose.orientation.z = -pose.orientation.z; 1135 return pose; 1136} 1137 1138static void 1139wmr_hmd_get_slam_tracked_pose(struct xrt_device *xdev, 1140 enum xrt_input_name name, 1141 uint64_t at_timestamp_ns, 1142 struct xrt_space_relation *out_relation) 1143{ 1144 DRV_TRACE_MARKER(); 1145 1146 struct wmr_hmd *wh = wmr_hmd(xdev); 1147 xrt_tracked_slam_get_tracked_pose(wh->tracking.slam, at_timestamp_ns, out_relation); 1148 1149 int pose_bits = XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | XRT_SPACE_RELATION_POSITION_TRACKED_BIT; 1150 bool pose_tracked = out_relation->relation_flags & pose_bits; 1151 1152 if (pose_tracked) { 1153#ifdef XRT_FEATURE_SLAM 1154 // !todo Correct pose depending on the VIT system in use, this should be done in the system itself. 1155 // For now, assume that we are using Basalt. 1156 wh->pose = wmr_hmd_correct_pose_from_basalt(out_relation->pose); 1157#else 1158 wh->pose = out_relation->pose; 1159#endif 1160 } 1161 1162 if (wh->tracking.imu2me) { 1163 math_pose_transform(&wh->pose, &wh->config.sensors.transforms.P_imu_me, &wh->pose); 1164 } 1165 1166 out_relation->pose = wh->pose; 1167 out_relation->relation_flags = (enum xrt_space_relation_flags)( 1168 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | XRT_SPACE_RELATION_POSITION_VALID_BIT | 1169 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | XRT_SPACE_RELATION_POSITION_TRACKED_BIT); 1170} 1171 1172static xrt_result_t 1173wmr_hmd_get_tracked_pose(struct xrt_device *xdev, 1174 enum xrt_input_name name, 1175 int64_t at_timestamp_ns, 1176 struct xrt_space_relation *out_relation) 1177{ 1178 DRV_TRACE_MARKER(); 1179 1180 struct wmr_hmd *wh = wmr_hmd(xdev); 1181 1182 at_timestamp_ns += (int64_t)(wh->tracked_offset_ms.val * (double)U_TIME_1MS_IN_NS); 1183 1184 xrt_result_t xret = XRT_SUCCESS; 1185 if (wh->tracking.slam_enabled && wh->slam_over_3dof) { 1186 wmr_hmd_get_slam_tracked_pose(xdev, name, at_timestamp_ns, out_relation); 1187 } else { 1188 xret = wmr_hmd_get_3dof_tracked_pose(xdev, name, at_timestamp_ns, out_relation); 1189 } 1190 1191 if (xret == XRT_SUCCESS) { 1192 math_pose_transform(&wh->offset, &out_relation->pose, &out_relation->pose); 1193 } 1194 1195 return xret; 1196} 1197 1198static void 1199wmr_hmd_destroy(struct xrt_device *xdev) 1200{ 1201 DRV_TRACE_MARKER(); 1202 1203 struct wmr_hmd *wh = wmr_hmd(xdev); 1204 1205 // Destroy the thread object. 1206 os_thread_helper_destroy(&wh->oth); 1207 1208 // Disconnect tunnelled controllers 1209 os_mutex_lock(&wh->controller_status_lock); 1210 if (wh->controller[0] != NULL) { 1211 struct wmr_controller_connection *wcc = (struct wmr_controller_connection *)wh->controller[0]; 1212 wmr_controller_connection_disconnect(wcc); 1213 } 1214 1215 if (wh->controller[1] != NULL) { 1216 struct wmr_controller_connection *wcc = (struct wmr_controller_connection *)wh->controller[1]; 1217 wmr_controller_connection_disconnect(wcc); 1218 } 1219 os_mutex_unlock(&wh->controller_status_lock); 1220 1221 os_mutex_destroy(&wh->controller_status_lock); 1222 os_cond_destroy(&wh->controller_status_cond); 1223 1224 if (wh->hid_hololens_sensors_dev != NULL) { 1225 os_hid_destroy(wh->hid_hololens_sensors_dev); 1226 wh->hid_hololens_sensors_dev = NULL; 1227 } 1228 1229 if (wh->hid_control_dev != NULL) { 1230 /* Do any deinit if we have a deinit function */ 1231 if (wh->hmd_desc && wh->hmd_desc->deinit_func) { 1232 wh->hmd_desc->deinit_func(wh); 1233 } 1234 os_hid_destroy(wh->hid_control_dev); 1235 wh->hid_control_dev = NULL; 1236 } 1237 1238 // Destroy SLAM source and tracker 1239 xrt_frame_context_destroy_nodes(&wh->tracking.xfctx); 1240 1241 // Destroy the fusion. 1242 m_imu_3dof_close(&wh->fusion.i3dof); 1243 1244 os_mutex_destroy(&wh->fusion.mutex); 1245 os_mutex_destroy(&wh->hid_lock); 1246 1247 u_device_free(&wh->base); 1248} 1249 1250XRT_MAYBE_UNUSED static struct t_camera_calibration 1251wmr_hmd_get_cam_calib(struct wmr_hmd *wh, int cam_index) 1252{ 1253 struct t_camera_calibration res; 1254 struct wmr_camera_config *wcalib = wh->config.tcams[cam_index]; 1255 struct wmr_distortion_6KT *intr = &wcalib->distortion6KT; 1256 1257 res.image_size_pixels.h = wcalib->roi.extent.h; 1258 res.image_size_pixels.w = wcalib->roi.extent.w; 1259 res.intrinsics[0][0] = intr->params.fx * (double)wcalib->roi.extent.w; 1260 res.intrinsics[1][1] = intr->params.fy * (double)wcalib->roi.extent.h; 1261 res.intrinsics[0][2] = intr->params.cx * (double)wcalib->roi.extent.w; 1262 res.intrinsics[1][2] = intr->params.cy * (double)wcalib->roi.extent.h; 1263 res.intrinsics[2][2] = 1.0; 1264 1265 res.distortion_model = T_DISTORTION_WMR; 1266 res.wmr.k1 = intr->params.k[0]; 1267 res.wmr.k2 = intr->params.k[1]; 1268 res.wmr.p1 = intr->params.p1; 1269 res.wmr.p2 = intr->params.p2; 1270 res.wmr.k3 = intr->params.k[2]; 1271 res.wmr.k4 = intr->params.k[3]; 1272 res.wmr.k5 = intr->params.k[4]; 1273 res.wmr.k6 = intr->params.k[5]; 1274 res.wmr.codx = intr->params.dist_x; 1275 res.wmr.cody = intr->params.dist_y; 1276 res.wmr.rpmax = intr->params.metric_radius; 1277 1278 return res; 1279} 1280 1281XRT_MAYBE_UNUSED static struct xrt_vec2 1282wmr_hmd_camera_project(struct wmr_hmd *wh, struct xrt_vec3 p3d) 1283{ 1284 float w = wh->config.cams[0].roi.extent.w; 1285 float h = wh->config.cams[0].roi.extent.h; 1286 float fx = wh->config.cams[0].distortion6KT.params.fx * w; 1287 float fy = wh->config.cams[0].distortion6KT.params.fy * h; 1288 float cx = wh->config.cams[0].distortion6KT.params.cx * w; 1289 float cy = wh->config.cams[0].distortion6KT.params.cy * h; 1290 float k1 = wh->config.cams[0].distortion6KT.params.k[0]; 1291 float k2 = wh->config.cams[0].distortion6KT.params.k[1]; 1292 float p1 = wh->config.cams[0].distortion6KT.params.p1; 1293 float p2 = wh->config.cams[0].distortion6KT.params.p2; 1294 float k3 = wh->config.cams[0].distortion6KT.params.k[2]; 1295 float k4 = wh->config.cams[0].distortion6KT.params.k[3]; 1296 float k5 = wh->config.cams[0].distortion6KT.params.k[4]; 1297 float k6 = wh->config.cams[0].distortion6KT.params.k[5]; 1298 1299 float x = p3d.x; 1300 float y = p3d.y; 1301 float z = p3d.z; 1302 1303 float xp = x / z; 1304 float yp = y / z; 1305 float rp2 = xp * xp + yp * yp; 1306 float cdist = (1 + rp2 * (k1 + rp2 * (k2 + rp2 * k3))) / (1 + rp2 * (k4 + rp2 * (k5 + rp2 * k6))); 1307#if 0 // OpenCV model 1308 float deltaX = 2 * p1 * xp * yp + p2 * (rp2 + 2 * xp * xp); 1309 float deltaY = 2 * p2 * xp * yp + p1 * (rp2 + 2 * yp * yp); 1310#else // Azure Kinect model (see comment in wmr_hmd_create_stereo_camera_calib) 1311 float deltaX = p1 * xp * yp + p2 * (rp2 + 2 * xp * xp); 1312 float deltaY = p2 * xp * yp + p1 * (rp2 + 2 * yp * yp); 1313#endif 1314 float xpp = xp * cdist + deltaX; 1315 float ypp = yp * cdist + deltaY; 1316 float u = fx * xpp + cx; 1317 float v = fy * ypp + cy; 1318 1319 struct xrt_vec2 p2d = {u, v}; 1320 return p2d; 1321} 1322 1323 1324/*! 1325 * Creates an OpenCV-compatible @ref t_stereo_camera_calibration pointer from 1326 * the WMR config. 1327 * 1328 * Note that the camera model used on WMR headsets seems to be the same as the 1329 * one in Azure-Kinect-Sensor-SDK. That model is slightly different than 1330 * OpenCV's in the following ways: 1331 * 1. There are "center of distortion", codx and cody, parameters 1332 * 2. The terms that use the tangential parameters, p1 and p2, aren't multiplied by 2 1333 * 3. There is a "metric radius" that delimits a valid area of distortion/undistortion 1334 * 1335 * Thankfully, parameters of points 1 and 2 tend to be almost zero in practice. For 3, we place metric_radius into 1336 * the calibration struct so that downstream tracking algorithms can use it as needed. 1337 */ 1338XRT_MAYBE_UNUSED static struct t_stereo_camera_calibration * 1339wmr_hmd_create_stereo_camera_calib(struct wmr_hmd *wh) 1340{ 1341 struct t_stereo_camera_calibration *calib = NULL; 1342 t_stereo_camera_calibration_alloc(&calib, T_DISTORTION_WMR); 1343 1344 1345 // Intrinsics 1346 for (int i = 0; i < 2; i++) { 1347 calib->view[i] = wmr_hmd_get_cam_calib(wh, i); 1348 } 1349 1350 // Extrinsics 1351 1352 // Compute transform from HT1 to HT0 (HT0 space into HT1 space) 1353 struct wmr_camera_config *ht1 = &wh->config.cams[1]; 1354 calib->camera_translation[0] = ht1->translation.x; 1355 calib->camera_translation[1] = ht1->translation.y; 1356 calib->camera_translation[2] = ht1->translation.z; 1357 calib->camera_rotation[0][0] = ht1->rotation.v[0]; 1358 calib->camera_rotation[0][1] = ht1->rotation.v[1]; 1359 calib->camera_rotation[0][2] = ht1->rotation.v[2]; 1360 calib->camera_rotation[1][0] = ht1->rotation.v[3]; 1361 calib->camera_rotation[1][1] = ht1->rotation.v[4]; 1362 calib->camera_rotation[1][2] = ht1->rotation.v[5]; 1363 calib->camera_rotation[2][0] = ht1->rotation.v[6]; 1364 calib->camera_rotation[2][1] = ht1->rotation.v[7]; 1365 calib->camera_rotation[2][2] = ht1->rotation.v[8]; 1366 1367 return calib; 1368} 1369 1370//! Extended camera calibration info for SLAM 1371XRT_MAYBE_UNUSED static void 1372wmr_hmd_fill_slam_cams_calibration(struct wmr_hmd *wh) 1373{ 1374 wh->tracking.slam_calib.cam_count = wh->config.tcam_count; 1375 1376 // Fill camera 0 1377 struct xrt_pose P_imu_c0 = wh->config.sensors.accel.pose; 1378 struct xrt_matrix_4x4 T_imu_c0; 1379 math_matrix_4x4_isometry_from_pose(&P_imu_c0, &T_imu_c0); 1380 wh->tracking.slam_calib.cams[0] = (struct t_slam_camera_calibration){ 1381 .base = wmr_hmd_get_cam_calib(wh, 0), 1382 .T_imu_cam = T_imu_c0, 1383 .frequency = CAMERA_FREQUENCY, 1384 }; 1385 1386 // Fill remaining cameras 1387 for (int i = 1; i < wh->config.tcam_count; i++) { 1388 struct xrt_pose P_ci_c0 = wh->config.tcams[i]->pose; 1389 1390 if (i == 2 || i == 3) { 1391 //! @note The calibration json for the reverb G2v2 (the only 4-camera wmr 1392 //! headset we know about) has the HT2 and HT3 extrinsics flipped compared 1393 //! to the order the third and fourth camera images come from usb. 1394 P_ci_c0 = wh->config.tcams[i == 2 ? 3 : 2]->pose; 1395 } 1396 1397 struct xrt_pose P_c0_ci; 1398 math_pose_invert(&P_ci_c0, &P_c0_ci); 1399 1400 struct xrt_pose P_imu_ci; 1401 math_pose_transform(&P_imu_c0, &P_c0_ci, &P_imu_ci); 1402 1403 struct xrt_matrix_4x4 T_imu_ci; 1404 math_matrix_4x4_isometry_from_pose(&P_imu_ci, &T_imu_ci); 1405 1406 wh->tracking.slam_calib.cams[i] = (struct t_slam_camera_calibration){ 1407 .base = wmr_hmd_get_cam_calib(wh, i), 1408 .T_imu_cam = T_imu_ci, 1409 .frequency = CAMERA_FREQUENCY, 1410 }; 1411 } 1412} 1413 1414XRT_MAYBE_UNUSED static struct t_imu_calibration 1415wmr_hmd_get_imu_calib(struct wmr_hmd *wh) 1416{ 1417 float *at = wh->config.sensors.accel.mix_matrix.v; 1418 struct xrt_vec3 ao = wh->config.sensors.accel.bias_offsets; 1419 struct xrt_vec3 ab = wh->config.sensors.accel.bias_var; 1420 struct xrt_vec3 an = wh->config.sensors.accel.noise_std; 1421 1422 float *gt = wh->config.sensors.gyro.mix_matrix.v; 1423 struct xrt_vec3 go = wh->config.sensors.gyro.bias_offsets; 1424 struct xrt_vec3 gb = wh->config.sensors.gyro.bias_var; 1425 struct xrt_vec3 gn = wh->config.sensors.gyro.noise_std; 1426 1427 struct t_imu_calibration calib = { 1428 .accel = 1429 { 1430 .transform = {{at[0], at[1], at[2]}, {at[3], at[4], at[5]}, {at[6], at[7], at[8]}}, 1431 .offset = {-ao.x, -ao.y, -ao.z}, // negative because slam system will add, not subtract 1432 .bias_std = {sqrt(ab.x), sqrt(ab.y), sqrt(ab.z)}, // sqrt because we want stdev not variance 1433 .noise_std = {an.x, an.y, an.z}, 1434 }, 1435 .gyro = 1436 { 1437 .transform = {{gt[0], gt[1], gt[2]}, {gt[3], gt[4], gt[5]}, {gt[6], gt[7], gt[8]}}, 1438 .offset = {-go.x, -go.y, -go.z}, 1439 .bias_std = {sqrt(gb.x), sqrt(gb.y), sqrt(gb.z)}, 1440 .noise_std = {gn.x, gn.y, gn.z}, 1441 }, 1442 }; 1443 return calib; 1444} 1445 1446//! Extended IMU calibration data for SLAM 1447XRT_MAYBE_UNUSED static void 1448wmr_hmd_fill_slam_imu_calibration(struct wmr_hmd *wh) 1449{ 1450 //! @note `average_imus` might change during runtime but the calibration data will be already submitted 1451 double imu_frequency = wh->average_imus ? IMU_FREQUENCY / IMU_SAMPLES_PER_PACKET : IMU_FREQUENCY; 1452 1453 struct t_slam_imu_calibration imu_calib = { 1454 .base = wmr_hmd_get_imu_calib(wh), 1455 .frequency = imu_frequency, 1456 }; 1457 1458 wh->tracking.slam_calib.imu = imu_calib; 1459} 1460 1461XRT_MAYBE_UNUSED static void 1462wmr_hmd_fill_slam_calibration(struct wmr_hmd *wh) 1463{ 1464 wmr_hmd_fill_slam_imu_calibration(wh); 1465 wmr_hmd_fill_slam_cams_calibration(wh); 1466} 1467 1468static void 1469wmr_hmd_switch_hmd_tracker(void *wh_ptr) 1470{ 1471 DRV_TRACE_MARKER(); 1472 1473 struct wmr_hmd *wh = (struct wmr_hmd *)wh_ptr; 1474 wh->slam_over_3dof = !wh->slam_over_3dof; 1475 struct u_var_button *btn = &wh->gui.switch_tracker_btn; 1476 1477 if (wh->slam_over_3dof) { // Use SLAM 1478 snprintf(btn->label, sizeof(btn->label), "Switch to 3DoF Tracking"); 1479 } else { // Use 3DoF 1480 snprintf(btn->label, sizeof(btn->label), "Switch to SLAM Tracking"); 1481 os_mutex_lock(&wh->fusion.mutex); 1482 m_imu_3dof_reset(&wh->fusion.i3dof); 1483 wh->fusion.i3dof.rot = wh->pose.orientation; 1484 os_mutex_unlock(&wh->fusion.mutex); 1485 } 1486} 1487 1488static struct xrt_slam_sinks * 1489wmr_hmd_slam_track(struct wmr_hmd *wh) 1490{ 1491 DRV_TRACE_MARKER(); 1492 1493 struct xrt_slam_sinks *sinks = NULL; 1494 1495#ifdef XRT_FEATURE_SLAM 1496 struct t_slam_tracker_config config = {0}; 1497 t_slam_fill_default_config(&config); 1498 config.cam_count = wh->config.slam_cam_count; 1499 wh->tracking.slam_calib.cam_count = wh->config.slam_cam_count; 1500 config.slam_calib = &wh->tracking.slam_calib; 1501 if (debug_get_option_slam_submit_from_start() == NULL) { 1502 config.submit_from_start = true; 1503 } 1504 1505 int create_status = t_slam_create(&wh->tracking.xfctx, &config, &wh->tracking.slam, &sinks); 1506 if (create_status != 0) { 1507 return NULL; 1508 } 1509 1510 int start_status = t_slam_start(wh->tracking.slam); 1511 if (start_status != 0) { 1512 return NULL; 1513 } 1514 1515 WMR_DEBUG(wh, "WMR HMD SLAM tracker successfully started"); 1516#endif 1517 1518 return sinks; 1519} 1520 1521#ifdef XRT_BUILD_DRIVER_HANDTRACKING 1522static enum t_camera_orientation 1523wmr_hmd_guess_camera_orientation(struct wmr_hmd *wh) 1524{ 1525 struct xrt_quat Q_ht0_me = wh->config.sensors.transforms.P_ht0_me.orientation; 1526 struct xrt_vec2 swing = {0}; 1527 float twist = 0; 1528 math_quat_to_swing_twist(&Q_ht0_me, &swing, &twist); 1529 WMR_DEBUG(wh, "HT0 twist value is %f", twist); 1530 1531 float abstwist = fabsf(twist); 1532 1533 // Bottom quadrant 1534 if (abstwist < M_PI / 4) { 1535 WMR_DEBUG(wh, "I think this headset has CAMERA_ORIENTATION_0 front cameras!"); 1536 return CAMERA_ORIENTATION_0; 1537 } 1538 1539 // Top quadrant 1540 if (abstwist > 3 * M_PI / 4) { 1541 WMR_DEBUG(wh, "I think this headset has CAMERA_ORIENTATION_180 front cameras!"); 1542 return CAMERA_ORIENTATION_180; 1543 } 1544 1545 // Right quadrant 1546 if (twist < 0) { 1547 WMR_DEBUG(wh, "I think this headset has CAMERA_ORIENTATION_90 front cameras!"); 1548 return CAMERA_ORIENTATION_90; 1549 } 1550 1551 // Left quadrant 1552 WMR_DEBUG(wh, "I think this headset has CAMERA_ORIENTATION_270 front cameras!"); 1553 return CAMERA_ORIENTATION_270; 1554} 1555#endif 1556 1557static int 1558wmr_hmd_hand_track(struct wmr_hmd *wh, 1559 struct t_stereo_camera_calibration *stereo_calib, 1560 struct xrt_hand_masks_sink *masks_sink, 1561 struct xrt_slam_sinks **out_sinks, 1562 struct xrt_device **out_device) 1563{ 1564 DRV_TRACE_MARKER(); 1565 1566 struct xrt_slam_sinks *sinks = NULL; 1567 struct xrt_device *device = NULL; 1568 1569#ifdef XRT_BUILD_DRIVER_HANDTRACKING 1570 1571 struct t_camera_extra_info extra_camera_info = {0}; 1572 1573 enum t_camera_orientation ori_guess = CAMERA_ORIENTATION_0; 1574 1575 if (wh->hmd_desc->hmd_type == WMR_HEADSET_GENERIC || // 1576 wh->hmd_desc->hmd_type == WMR_HEADSET_REVERB_G2) { 1577 ori_guess = wmr_hmd_guess_camera_orientation(wh); 1578 } 1579 1580 for (int i = 0; i < 2; i++) { 1581 extra_camera_info.views[i].camera_orientation = ori_guess; 1582 extra_camera_info.views[i].boundary_type = HT_IMAGE_BOUNDARY_CIRCLE; 1583 float w = wh->config.cams[i].roi.extent.w; 1584 float h = wh->config.cams[i].roi.extent.h; 1585 float cx = wh->config.cams[i].distortion6KT.params.cx * w; 1586 float cy = wh->config.cams[i].distortion6KT.params.cy * h; 1587 float rpmax = wh->config.cams[i].distortion6KT.params.metric_radius; 1588 struct xrt_vec3 p3d = {rpmax, 0, 1}; // Right-most border of the metric_radius circle in the Z=1 plane 1589 struct xrt_vec2 p2d = wmr_hmd_camera_project(wh, p3d); 1590 float radius = (p2d.x - cx) / w; 1591 extra_camera_info.views[i].boundary.circle.normalized_center = (struct xrt_vec2){cx / w, cy / h}; 1592 extra_camera_info.views[i].boundary.circle.normalized_radius = radius; 1593 } 1594 1595 struct t_hand_tracking_create_info create_info = {.cams_info = extra_camera_info, .masks_sink = masks_sink}; 1596 1597 int create_status = ht_device_create(&wh->tracking.xfctx, // 1598 stereo_calib, // 1599 create_info, // 1600 &sinks, // 1601 &device); 1602 if (create_status != 0) { 1603 return create_status; 1604 } 1605 1606 device = multi_create_tracking_override(XRT_TRACKING_OVERRIDE_ATTACHED, device, &wh->base, 1607 XRT_INPUT_GENERIC_HEAD_POSE, &wh->config.sensors.transforms.P_ht0_me); 1608 1609 WMR_DEBUG(wh, "WMR HMD hand tracker successfully created"); 1610#endif 1611 1612 *out_sinks = sinks; 1613 *out_device = device; 1614 1615 return 0; 1616} 1617 1618static void 1619wmr_hmd_setup_ui(struct wmr_hmd *wh) 1620{ 1621 u_var_add_root(wh, "WMR HMD", true); 1622 1623 u_var_add_gui_header(wh, NULL, "Tracking"); 1624 if (wh->tracking.slam_enabled) { 1625 wh->gui.switch_tracker_btn.cb = wmr_hmd_switch_hmd_tracker; 1626 wh->gui.switch_tracker_btn.ptr = wh; 1627 u_var_add_button(wh, &wh->gui.switch_tracker_btn, "Switch to 3DoF Tracking"); 1628 } 1629 u_var_add_pose(wh, &wh->pose, "Tracked Pose"); 1630 u_var_add_pose(wh, &wh->offset, "Pose Offset"); 1631 u_var_add_bool(wh, &wh->average_imus, "Average IMU samples"); 1632 u_var_add_draggable_f32(wh, &wh->tracked_offset_ms, "Timecode offset(ms)"); 1633 1634 u_var_add_gui_header(wh, NULL, "3DoF Tracking"); 1635 m_imu_3dof_add_vars(&wh->fusion.i3dof, wh, ""); 1636 1637 u_var_add_gui_header(wh, NULL, "SLAM Tracking"); 1638 u_var_add_ro_text(wh, wh->gui.slam_status, "Tracker status"); 1639 u_var_add_bool(wh, &wh->tracking.imu2me, "Correct IMU pose to middle of eyes"); 1640 1641 u_var_add_gui_header(wh, NULL, "Hand Tracking"); 1642 u_var_add_ro_text(wh, wh->gui.hand_status, "Tracker status"); 1643 1644 u_var_add_gui_header(wh, NULL, "Hololens Sensors' Companion device"); 1645 u_var_add_u8(wh, &wh->proximity_sensor, "HMD Proximity"); 1646 u_var_add_u16(wh, &wh->raw_ipd, "HMD IPD"); 1647 1648 if (wh->hmd_desc->screen_enable_func) { 1649 // Enabling/disabling the HMD screen at runtime is supported. Add button to debug GUI. 1650 wh->gui.hmd_screen_enable_btn.cb = wmr_hmd_screen_enable_toggle; 1651 wh->gui.hmd_screen_enable_btn.ptr = wh; 1652 u_var_add_button(wh, &wh->gui.hmd_screen_enable_btn, "HMD Screen [On/Off]"); 1653 } 1654 1655 u_var_add_gui_header(wh, NULL, "Misc"); 1656 u_var_add_log_level(wh, &wh->log_level, "log_level"); 1657} 1658 1659/*! 1660 * Procedure to setup trackers: 3dof, SLAM and hand tracking. 1661 * 1662 * Determines which trackers to initialize and starts them. 1663 * Fills @p out_sinks to stream raw data to for tracking. 1664 * In the case of hand tracking being enabled, it returns a hand tracker device in @p out_handtracker. 1665 * 1666 * @param wh the wmr headset device 1667 * @param out_sinks sinks to stream video/IMU data to for tracking 1668 * @param out_handtracker a newly created hand tracker device 1669 * @return true on success, false when an unexpected state is reached. 1670 */ 1671static bool 1672wmr_hmd_setup_trackers(struct wmr_hmd *wh, struct xrt_slam_sinks *out_sinks, struct xrt_device **out_handtracker) 1673{ 1674 // We always have at least 3dof HMD tracking 1675 bool dof3_enabled = true; 1676 1677 // Decide whether to initialize the SLAM tracker 1678 bool slam_wanted = debug_get_bool_option_wmr_slam(); 1679#ifdef XRT_FEATURE_SLAM 1680 bool slam_supported = true; 1681#else 1682 bool slam_supported = false; 1683#endif 1684 bool slam_enabled = slam_supported && slam_wanted; 1685 1686 // Decide whether to initialize the hand tracker 1687 bool hand_wanted = debug_get_bool_option_wmr_handtracking(); 1688#ifdef XRT_BUILD_DRIVER_HANDTRACKING 1689 bool hand_supported = true; 1690#else 1691 bool hand_supported = false; 1692#endif 1693 bool hand_enabled = hand_supported && hand_wanted; 1694 1695 wh->base.supported.orientation_tracking = dof3_enabled || slam_enabled; 1696 wh->base.supported.position_tracking = slam_enabled; 1697 wh->base.supported.hand_tracking = false; // out_handtracker will handle it 1698 1699 wh->tracking.slam_enabled = slam_enabled; 1700 wh->tracking.hand_enabled = hand_enabled; 1701 wh->tracking.imu2me = true; 1702 1703 wh->slam_over_3dof = slam_enabled; // We prefer SLAM over 3dof tracking if possible 1704 1705 const char *slam_status = wh->tracking.slam_enabled ? "Enabled" 1706 : !slam_wanted ? "Disabled by the user (envvar set to false)" 1707 : !slam_supported ? "Unavailable (not built)" 1708 : NULL; 1709 1710 const char *hand_status = wh->tracking.hand_enabled ? "Enabled" 1711 : !hand_wanted ? "Disabled by the user (envvar set to false)" 1712 : !hand_supported ? "Unavailable (not built)" 1713 : NULL; 1714 1715 assert(slam_status != NULL && hand_status != NULL); 1716 1717 (void)snprintf(wh->gui.slam_status, sizeof(wh->gui.slam_status), "%s", slam_status); 1718 (void)snprintf(wh->gui.hand_status, sizeof(wh->gui.hand_status), "%s", hand_status); 1719 1720 struct t_stereo_camera_calibration *stereo_calib = wmr_hmd_create_stereo_camera_calib(wh); 1721 wmr_hmd_fill_slam_calibration(wh); 1722 1723 // Initialize 3DoF tracker 1724 m_imu_3dof_init(&wh->fusion.i3dof, M_IMU_3DOF_USE_GRAVITY_DUR_20MS); 1725 1726 // Initialize SLAM tracker 1727 struct xrt_slam_sinks *slam_sinks = NULL; 1728 if (wh->tracking.slam_enabled) { 1729 slam_sinks = wmr_hmd_slam_track(wh); 1730 if (slam_sinks == NULL) { 1731 WMR_WARN(wh, "Unable to setup the SLAM tracker"); 1732 return false; 1733 } 1734 } 1735 1736 // Initialize hand tracker 1737 struct xrt_slam_sinks *hand_sinks = NULL; 1738 struct xrt_device *hand_device = NULL; 1739 struct xrt_hand_masks_sink *masks_sink = slam_sinks ? slam_sinks->hand_masks : NULL; 1740 if (wh->tracking.hand_enabled) { 1741 int hand_status = wmr_hmd_hand_track(wh, stereo_calib, masks_sink, &hand_sinks, &hand_device); 1742 if (hand_status != 0 || hand_sinks == NULL || hand_device == NULL) { 1743 WMR_WARN(wh, "Unable to setup the hand tracker"); 1744 return false; 1745 } 1746 } 1747 1748 t_stereo_camera_calibration_reference(&stereo_calib, NULL); 1749 1750 // Setup sinks depending on tracking configuration 1751 struct xrt_slam_sinks entry_sinks = {0}; 1752 if (slam_enabled && hand_enabled) { 1753 struct xrt_frame_sink *entry_cam0_sink = NULL; 1754 struct xrt_frame_sink *entry_cam1_sink = NULL; 1755 1756 u_sink_split_create(&wh->tracking.xfctx, slam_sinks->cams[0], hand_sinks->cams[0], &entry_cam0_sink); 1757 u_sink_split_create(&wh->tracking.xfctx, slam_sinks->cams[1], hand_sinks->cams[1], &entry_cam1_sink); 1758 1759 entry_sinks = *slam_sinks; 1760 entry_sinks.cams[0] = entry_cam0_sink; 1761 entry_sinks.cams[1] = entry_cam1_sink; 1762 } else if (slam_enabled) { 1763 entry_sinks = *slam_sinks; 1764 } else if (hand_enabled) { 1765 entry_sinks = *hand_sinks; 1766 } else { 1767 entry_sinks = (struct xrt_slam_sinks){0}; 1768 } 1769 1770 *out_sinks = entry_sinks; 1771 *out_handtracker = hand_device; 1772 return true; 1773} 1774 1775static bool 1776wmr_hmd_request_controller_status(struct wmr_hmd *wh) 1777{ 1778 DRV_TRACE_MARKER(); 1779 unsigned char cmd[64] = {WMR_MS_HOLOLENS_MSG_BT_CONTROL, WMR_MS_HOLOLENS_MSG_CONTROLLER_STATUS}; 1780 return wmr_hmd_send_controller_packet(wh, cmd, sizeof(cmd)); 1781} 1782 1783static xrt_result_t 1784compute_distortion_wmr(struct xrt_device *xdev, uint32_t view, float u, float v, struct xrt_uv_triplet *out_result) 1785{ 1786 struct wmr_hmd *wh = wmr_hmd(xdev); 1787 1788 u_compute_distortion_poly_3k(&wh->config.eye_params[view].poly_3k, view, u, v, out_result); 1789 1790 return XRT_SUCCESS; 1791} 1792 1793void 1794wmr_hmd_create(enum wmr_headset_type hmd_type, 1795 struct os_hid_device *hid_holo, 1796 struct os_hid_device *hid_ctrl, 1797 struct xrt_prober_device *dev_holo, 1798 enum u_logging_level log_level, 1799 struct xrt_device **out_hmd, 1800 struct xrt_device **out_handtracker, 1801 struct xrt_device **out_left_controller, 1802 struct xrt_device **out_right_controller) 1803{ 1804 DRV_TRACE_MARKER(); 1805 1806 enum u_device_alloc_flags flags = 1807 (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE); 1808 int ret = 0; 1809 int i; 1810 int eye; 1811 1812 struct wmr_hmd *wh = U_DEVICE_ALLOCATE(struct wmr_hmd, flags, 1, 0); 1813 if (!wh) { 1814 return; 1815 } 1816 1817 // Populate the base members. 1818 wh->base.update_inputs = u_device_noop_update_inputs; 1819 wh->base.get_tracked_pose = wmr_hmd_get_tracked_pose; 1820 wh->base.get_view_poses = u_device_get_view_poses; 1821 wh->base.destroy = wmr_hmd_destroy; 1822 wh->base.name = XRT_DEVICE_GENERIC_HMD; 1823 wh->base.device_type = XRT_DEVICE_TYPE_HMD; 1824 wh->log_level = log_level; 1825 1826 wh->hid_hololens_sensors_dev = hid_holo; 1827 wh->hid_control_dev = hid_ctrl; 1828 1829 // Mutex before thread. 1830 ret = os_mutex_init(&wh->fusion.mutex); 1831 if (ret != 0) { 1832 WMR_ERROR(wh, "Failed to init fusion mutex!"); 1833 wmr_hmd_destroy(&wh->base); 1834 wh = NULL; 1835 return; 1836 } 1837 1838 ret = os_mutex_init(&wh->hid_lock); 1839 if (ret != 0) { 1840 WMR_ERROR(wh, "Failed to init HID mutex!"); 1841 wmr_hmd_destroy(&wh->base); 1842 wh = NULL; 1843 return; 1844 } 1845 1846 ret = os_mutex_init(&wh->controller_status_lock); 1847 if (ret != 0) { 1848 WMR_ERROR(wh, "Failed to init Controller status mutex!"); 1849 wmr_hmd_destroy(&wh->base); 1850 wh = NULL; 1851 return; 1852 } 1853 1854 ret = os_cond_init(&wh->controller_status_cond); 1855 if (ret != 0) { 1856 WMR_ERROR(wh, "Failed to init Controller status cond!"); 1857 wmr_hmd_destroy(&wh->base); 1858 wh = NULL; 1859 return; 1860 } 1861 1862 // Thread and other state. 1863 ret = os_thread_helper_init(&wh->oth); 1864 if (ret != 0) { 1865 WMR_ERROR(wh, "Failed to init threading!"); 1866 wmr_hmd_destroy(&wh->base); 1867 wh = NULL; 1868 return; 1869 } 1870 1871 // Setup input. 1872 wh->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE; 1873 1874 // Read config file from HMD 1875 if (wmr_read_config(wh) < 0) { 1876 WMR_ERROR(wh, "Failed to load headset configuration!"); 1877 wmr_hmd_destroy(&wh->base); 1878 wh = NULL; 1879 return; 1880 } 1881 1882 wh->pose = (struct xrt_pose)XRT_POSE_IDENTITY; 1883 wh->offset = (struct xrt_pose)XRT_POSE_IDENTITY; 1884 wh->average_imus = true; 1885 wh->tracked_offset_ms = (struct u_var_draggable_f32){ 1886 .val = 0.0, 1887 .min = -40.0, 1888 .step = 0.1, 1889 .max = +120.0, 1890 }; 1891 1892 /* Now that we have the config loaded, iterate the map of known headsets and see if we have 1893 * an entry for this specific headset (otherwise the generic entry will be used) 1894 */ 1895 for (i = 0; i < headset_map_n; i++) { 1896 const struct wmr_headset_descriptor *cur = &headset_map[i]; 1897 1898 if (hmd_type == cur->hmd_type) { 1899 wh->hmd_desc = cur; 1900 if (hmd_type != WMR_HEADSET_GENERIC) 1901 break; /* Stop checking if we have a specific match, or keep going for the GENERIC 1902 catch-all type */ 1903 } 1904 1905 if (cur->dev_id_str && strncmp(wh->config_hdr.name, cur->dev_id_str, 64) == 0) { 1906 hmd_type = cur->hmd_type; 1907 wh->hmd_desc = cur; 1908 break; 1909 } 1910 } 1911 assert(wh->hmd_desc != NULL); /* Each supported device MUST have a manually created entry in our headset_map */ 1912 1913 WMR_INFO(wh, "Found WMR headset type: %s", wh->hmd_desc->debug_name); 1914 1915 wmr_config_precompute_transforms(&wh->config.sensors, wh->config.eye_params); 1916 1917 struct u_extents_2d exts; 1918 exts.w_pixels = (uint32_t)wh->config.eye_params[0].display_size.x; 1919 exts.h_pixels = (uint32_t)wh->config.eye_params[0].display_size.y; 1920 u_extents_2d_split_side_by_side(&wh->base, &exts); 1921 1922 // Fill in blend mode - just opqaue, unless we get Hololens support one day. 1923 size_t idx = 0; 1924 wh->base.hmd->blend_modes[idx++] = XRT_BLEND_MODE_OPAQUE; 1925 wh->base.hmd->blend_mode_count = idx; 1926 1927 wh->config.eye_params[0].poly_3k.y_offset = debug_get_num_option_left_view_y_offset(); 1928 wh->config.eye_params[1].poly_3k.y_offset = debug_get_num_option_right_view_y_offset(); 1929 1930 // Distortion information, fills in xdev->compute_distortion(). 1931 for (eye = 0; eye < 2; eye++) { 1932 struct xrt_fov *fov = &wh->base.hmd->distortion.fov[eye]; 1933 struct u_poly_3k_eye_values *poly_3k = &wh->config.eye_params[eye].poly_3k; 1934 1935 u_compute_distortion_bounds_poly_3k(&poly_3k->inv_affine_xform, poly_3k->channels, eye, fov, 1936 &poly_3k->tex_x_range, &poly_3k->tex_y_range); 1937 1938 WMR_INFO(wh, "FoV eye %d angles left %f right %f down %f up %f", eye, fov->angle_left, fov->angle_right, 1939 fov->angle_down, fov->angle_up); 1940 1941 WMR_INFO(wh, "Render texture range %f, %f to %f, %f", poly_3k->tex_x_range.x, poly_3k->tex_y_range.x, 1942 poly_3k->tex_x_range.y, poly_3k->tex_y_range.y); 1943 } 1944 1945 wh->base.hmd->distortion.models = XRT_DISTORTION_MODEL_COMPUTE; 1946 wh->base.hmd->distortion.preferred = XRT_DISTORTION_MODEL_COMPUTE; 1947 wh->base.compute_distortion = compute_distortion_wmr; 1948 u_distortion_mesh_fill_in_compute(&wh->base); 1949 1950 // Set HMD Scanout direction and time 1951 if (wh->hmd_desc->hmd_type == WMR_HEADSET_SAMSUNG_800ZAA || 1952 wh->hmd_desc->hmd_type == WMR_HEADSET_SAMSUNG_XE700X3AI) { 1953 1954 wh->base.hmd->screens[0].scanout_direction = XRT_SCANOUT_DIRECTION_TOP_TO_BOTTOM; 1955 wh->base.hmd->screens[0].scanout_time_ns = 1956 wh->base.hmd->screens[0].nominal_frame_interval_ns * 1600.0 / 1624.0; 1957 } else { 1958 wh->base.hmd->screens[0].scanout_direction = XRT_SCANOUT_DIRECTION_NONE; 1959 wh->base.hmd->screens[0].scanout_time_ns = 0; 1960 } 1961 1962 // Set initial HMD screen power state. 1963 wh->hmd_screen_enable = true; 1964 1965 /* We're set up. Activate the HMD and turn on the IMU */ 1966 if (wh->hmd_desc->init_func && wh->hmd_desc->init_func(wh) != 0) { 1967 WMR_ERROR(wh, "Activation of HMD failed"); 1968 wmr_hmd_destroy(&wh->base); 1969 wh = NULL; 1970 return; 1971 } 1972 1973 // Switch on IMU on the HMD. 1974 hololens_sensors_enable_imu(wh); 1975 1976 // Switch on data streams on the HMD (only cameras for now as IMU is not yet integrated into wmr_source) 1977 wh->tracking.source = wmr_source_create(&wh->tracking.xfctx, dev_holo, wh->config); 1978 1979 struct xrt_slam_sinks sinks = {0}; 1980 struct xrt_device *hand_device = NULL; 1981 bool success = wmr_hmd_setup_trackers(wh, &sinks, &hand_device); 1982 if (!success) { 1983 wmr_hmd_destroy(&wh->base); 1984 wh = NULL; 1985 return; 1986 } 1987 1988 // Stream data source into sinks (if populated) 1989 bool stream_started = xrt_fs_slam_stream_start(wh->tracking.source, &sinks); 1990 if (!stream_started) { 1991 //! @todo Could reach this due to !XRT_HAVE_LIBUSB but the HMD should keep working 1992 WMR_WARN(wh, "Failed to start WMR source"); 1993 wmr_hmd_destroy(&wh->base); 1994 wh = NULL; 1995 return; 1996 } 1997 1998 // Hand over hololens sensor device to reading thread. 1999 ret = os_thread_helper_start(&wh->oth, wmr_run_thread, wh); 2000 if (ret != 0) { 2001 WMR_ERROR(wh, "Failed to start thread!"); 2002 wmr_hmd_destroy(&wh->base); 2003 wh = NULL; 2004 return; 2005 } 2006 2007 /* Send controller status request to check for online controllers 2008 * and wait 250ms for the reports for Reverb G2 and Odyssey+ */ 2009 if (wh->hmd_desc->hmd_type == WMR_HEADSET_REVERB_G2 || wh->hmd_desc->hmd_type == WMR_HEADSET_SAMSUNG_800ZAA) { 2010 bool have_controller_status = false; 2011 2012 os_mutex_lock(&wh->controller_status_lock); 2013 if (wmr_hmd_request_controller_status(wh)) { 2014 /* @todo: Add a timed version of os_cond_wait and a timeout? */ 2015 /* This will be signalled from the reader thread */ 2016 while (!wh->have_left_controller_status && !wh->have_right_controller_status) { 2017 os_cond_wait(&wh->controller_status_cond, &wh->controller_status_lock); 2018 } 2019 have_controller_status = true; 2020 } 2021 os_mutex_unlock(&wh->controller_status_lock); 2022 2023 if (!have_controller_status) { 2024 WMR_WARN(wh, "Failed to request controller status from HMD"); 2025 } 2026 } 2027 2028 wmr_hmd_setup_ui(wh); 2029 2030 *out_hmd = &wh->base; 2031 *out_handtracker = hand_device; 2032 2033 os_mutex_lock(&wh->controller_status_lock); 2034 if (wh->controller[0] != NULL) { 2035 *out_left_controller = wmr_hmd_controller_connection_get_controller(wh->controller[0]); 2036 } else { 2037 *out_left_controller = NULL; 2038 } 2039 2040 if (wh->controller[1] != NULL) { 2041 *out_right_controller = wmr_hmd_controller_connection_get_controller(wh->controller[1]); 2042 } else { 2043 *out_right_controller = NULL; 2044 } 2045 os_mutex_unlock(&wh->controller_status_lock); 2046} 2047 2048bool 2049wmr_hmd_send_controller_packet(struct wmr_hmd *hmd, const uint8_t *buffer, uint32_t buf_size) 2050{ 2051 os_mutex_lock(&hmd->hid_lock); 2052 int ret = os_hid_write(hmd->hid_hololens_sensors_dev, buffer, buf_size); 2053 os_mutex_unlock(&hmd->hid_lock); 2054 2055 return ret != -1 && (uint32_t)(ret) == buf_size; 2056} 2057 2058/* Called from WMR controller implementation only during fw reads. @todo: Refactor 2059 * controller firmware reads to happen from a state machine and not require this blocking method */ 2060int 2061wmr_hmd_read_sync_from_controller(struct wmr_hmd *hmd, uint8_t *buffer, uint32_t buf_size, int timeout_ms) 2062{ 2063 os_mutex_lock(&hmd->hid_lock); 2064 int res = os_hid_read(hmd->hid_hololens_sensors_dev, buffer, buf_size, timeout_ms); 2065 os_mutex_unlock(&hmd->hid_lock); 2066 2067 return res; 2068}