The open source OpenXR runtime
at main 546 lines 15 kB view raw
1// Copyright 2016-2019, Philipp Zabel 2// Copyright 2020, Collabora, Ltd. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Vive Lighthouse Watchman implementation 7 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 8 * @ingroup drv_vive 9 */ 10// IWYU pragma: no_include <asm/int-ll64.h> 11// IWYU pragma: no_include <linux/byteorder/little_endian.h> 12 13#include <stdint.h> 14#include <string.h> 15#include <zlib.h> 16#include <stdio.h> 17 18#include "math/m_api.h" 19 20#include "xrt/xrt_byte_order.h" 21 22#include "util/u_debug.h" 23#include "util/u_logging.h" 24 25#include "vive_lighthouse.h" 26 27static enum u_logging_level log_level; 28 29#define LH_TRACE(...) U_LOG_IFL_T(log_level, __VA_ARGS__) 30#define LH_DEBUG(...) U_LOG_IFL_D(log_level, __VA_ARGS__) 31#define LH_INFO(...) U_LOG_IFL_I(log_level, __VA_ARGS__) 32#define LH_WARN(...) U_LOG_IFL_W(log_level, __VA_ARGS__) 33#define LH_ERROR(...) U_LOG_IFL_E(log_level, __VA_ARGS__) 34 35DEBUG_GET_ONCE_LOG_OPTION(vive_log, "VIVE_LOG", U_LOGGING_WARN) 36 37struct lighthouse_ootx_report 38{ 39 __le16 version; 40 __le32 serial; 41 __le16 phase[2]; 42 __le16 tilt[2]; 43 __u8 reset_count; 44 __u8 model_id; 45 __le16 curve[2]; 46 __s8 gravity[3]; 47 __le16 gibphase[2]; 48 __le16 gibmag[2]; 49} __attribute__((packed)); 50 51static unsigned int watchman_id; 52 53float 54_f16_to_float(uint16_t f16) 55{ 56 unsigned int sign = f16 >> 15; 57 unsigned int exponent = (f16 >> 10) & 0x1f; 58 unsigned int mantissa = f16 & 0x3ff; 59 union { 60 float f32; 61 uint32_t u32; 62 } u; 63 64 if (exponent == 0) { 65 if (!mantissa) { 66 /* zero */ 67 u.u32 = sign << 31; 68 } else { 69 /* subnormal */ 70 exponent = 127 - 14; 71 mantissa <<= 23 - 10; 72 /* 73 * convert to normal representation: 74 * shift up mantissa and drop MSB 75 */ 76 while (!(mantissa & (1 << 23))) { 77 mantissa <<= 1; 78 exponent--; 79 } 80 mantissa &= 0x7fffffu; 81 u.u32 = (sign << 31) | (exponent << 23) | mantissa; 82 } 83 } else if (exponent < 31) { 84 /* normal */ 85 exponent += 127 - 15; 86 mantissa <<= 23 - 10; 87 u.u32 = (sign << 31) | (exponent << 23) | mantissa; 88 } else if (mantissa == 0) { 89 /* infinite */ 90 u.u32 = (sign << 31) | (255 << 23); 91 } else { 92 /* NaN */ 93 u.u32 = 0x7fffffffu; 94 } 95 return u.f32; 96} 97 98static inline float 99__le16_to_float(__le16 le16) 100{ 101 return _f16_to_float(__le16_to_cpu(le16)); 102} 103 104static inline bool 105pulse_in_this_sync_window(int32_t dt, uint16_t duration) 106{ 107 return dt > -duration && (dt + duration) < (6500 + 250); 108} 109 110static inline bool 111pulse_in_next_sync_window(int32_t dt, uint16_t duration) 112{ 113 int32_t dt_end = dt + duration; 114 115 /* 116 * Allow 2000 pulses (40 µs) deviation from the expected interval 117 * between bases, and 1000 pulses (20 µs) for a single base. 118 */ 119 return (dt > (20000 - 2000) && (dt_end) < (20000 + 6500 + 2000)) || 120 (dt > (380000 - 2000) && (dt_end) < (380000 + 6500 + 2000)) || 121 (dt > (400000 - 1000) && (dt_end) < (400000 + 6500 + 1000)); 122} 123 124static inline bool 125pulse_in_sweep_window(int32_t dt, uint16_t duration) 126{ 127 /* 128 * The J axis (horizontal) sweep starts 71111 ticks after the sync 129 * pulse start (32°) and ends at 346667 ticks (156°). 130 * The K axis (vertical) sweep starts at 55555 ticks (23°) and ends 131 * at 331111 ticks (149°). 132 */ 133 return dt > (55555 - 1000) && (dt + duration) < (346667 + 1000); 134} 135 136static void 137_handle_ootx_frame(struct lighthouse_base *base) 138{ 139 struct lighthouse_ootx_report *report = (void *)(base->ootx + 2); 140 uint16_t len = (__le16)*base->ootx; 141 uint32_t crc = crc32(0L, Z_NULL, 0); 142 bool serial_changed = false; 143 uint32_t ootx_crc; 144 uint16_t version; 145 int ootx_version; 146 struct xrt_vec3 gravity; 147 int i; 148 149 if (len != 33) { 150 LH_WARN("Lighthouse Base %X: unexpected OOTX payload length: %d", base->serial, len); 151 return; 152 } 153 154 ootx_crc = (__le32) * ((__le32 *)(base->ootx + 36)); /* (len+3)/4*4 */ 155 156 crc = crc32(crc, base->ootx + 2, 33); 157 if (ootx_crc != crc) { 158 LH_ERROR("Lighthouse Base %X: CRC error: %08x != %08x", base->serial, crc, ootx_crc); 159 return; 160 } 161 162 version = __le16_to_cpu(report->version); 163 ootx_version = version & 0x3f; 164 if (ootx_version != 6) { 165 LH_ERROR("Lighthouse Base %X: unexpected OOTX frame version: %d", base->serial, ootx_version); 166 return; 167 } 168 169 base->firmware_version = version >> 6; 170 171 if (base->serial != __le32_to_cpu(report->serial)) { 172 base->serial = __le32_to_cpu(report->serial); 173 serial_changed = true; 174 } 175 176 for (i = 0; i < 2; i++) { 177 struct lighthouse_rotor_calibration *rotor; 178 179 rotor = &base->calibration.rotor[i]; 180 rotor->tilt = __le16_to_float(report->tilt[i]); 181 rotor->phase = __le16_to_float(report->phase[i]); 182 rotor->curve = __le16_to_float(report->curve[i]); 183 rotor->gibphase = __le16_to_float(report->gibphase[i]); 184 rotor->gibmag = __le16_to_float(report->gibmag[i]); 185 } 186 187 base->model_id = report->model_id; 188 189 if (serial_changed) { 190 LH_INFO( 191 "Lighthouse Base %X: firmware version: %d, model id: %d, " 192 "channel: %c", 193 base->serial, base->firmware_version, base->model_id, base->channel); 194 195 for (i = 0; i < 2; i++) { 196 struct lighthouse_rotor_calibration *rotor; 197 198 rotor = &base->calibration.rotor[i]; 199 200 LH_INFO( 201 "Lighthouse Base %X: rotor %d: [ %12.9f %12.9f " 202 "%12.9f %12.9f %12.9f ]", 203 base->serial, i, rotor->tilt, rotor->phase, rotor->curve, rotor->gibphase, rotor->gibmag); 204 } 205 } 206 207 gravity.x = report->gravity[0]; 208 gravity.y = report->gravity[1]; 209 gravity.z = report->gravity[2]; 210 math_vec3_normalize(&gravity); 211 if (gravity.x != base->gravity.x || gravity.y != base->gravity.y || gravity.z != base->gravity.z) { 212 base->gravity = gravity; 213 LH_INFO("Lighthouse Base %X: gravity: [ %9.6f %9.6f %9.6f ]", base->serial, gravity.x, gravity.y, 214 gravity.z); 215 } 216 217 if (base->reset_count != report->reset_count) { 218 base->reset_count = report->reset_count; 219 LH_INFO("Lighthouse Base %X: reset count: %d", base->serial, base->reset_count); 220 } 221} 222 223static void 224lighthouse_base_reset(struct lighthouse_base *base) 225{ 226 base->data_sync = 0; 227 base->data_word = -1; 228 base->data_bit = 0; 229 memset(base->ootx, 0, sizeof(base->ootx)); 230} 231 232static void 233_handle_ootx_data_word(struct lighthouse_watchman *watchman, struct lighthouse_base *base) 234{ 235 uint16_t len = (__le16)*base->ootx; 236 237 /* After 4 OOTX words we have received the base station serial number */ 238 if (base->data_word == 4) { 239 struct lighthouse_ootx_report *report = (void *)(base->ootx + 2); 240 uint16_t ootx_version = __le16_to_cpu(report->version) & 0x3f; 241 uint32_t serial = __le32_to_cpu(report->serial); 242 243 if (len != 33) { 244 LH_WARN("%s: unexpected OOTX frame length %d", watchman->name, len); 245 return; 246 } 247 248 if (ootx_version == 6 && serial != base->serial) { 249 LH_DEBUG("%s: spotted Lighthouse Base %X", watchman->name, serial); 250 } 251 } 252 if (len == 33 && base->data_word == 20) { /* (len + 3)/4 * 2 + 2 */ 253 _handle_ootx_frame(base); 254 } 255} 256 257static void 258lighthouse_base_handle_ootx_data_bit(struct lighthouse_watchman *watchman, struct lighthouse_base *base, bool data) 259{ 260 if (base->data_word >= (int)sizeof(base->ootx) / 2) { 261 base->data_word = -1; 262 } else if (base->data_word >= 0) { 263 if (base->data_bit == 16) { 264 /* Sync bit */ 265 base->data_bit = 0; 266 if (data) { 267 base->data_word++; 268 _handle_ootx_data_word(watchman, base); 269 } else { 270 LH_WARN("%s: Missed a sync bit, restarting", watchman->name); 271 /* Missing sync bit, restart */ 272 base->data_word = -1; 273 } 274 } else if (base->data_bit < 16) { 275 /* 276 * Each 16-bit payload word contains two bytes, 277 * transmitted MSB-first. 278 */ 279 if (data) { 280 int idx = 2 * base->data_word + (base->data_bit >> 3); 281 282 base->ootx[idx] |= 0x80 >> (base->data_bit % 8); 283 } 284 base->data_bit++; 285 } 286 } 287 288 /* Preamble detection */ 289 if (data) { 290 if (base->data_sync > 16) { 291 /* Preamble detected, restart bit capture */ 292 memset(base->ootx, 0, sizeof(base->ootx)); 293 base->data_word = 0; 294 base->data_bit = 0; 295 } 296 base->data_sync = 0; 297 } else { 298 base->data_sync++; 299 } 300} 301 302static void 303lighthouse_base_handle_frame(struct lighthouse_watchman *watchman, 304 struct lighthouse_base *base, 305 uint32_t sync_timestamp) 306{ 307 struct lighthouse_frame *frame = &base->frame[base->active_rotor]; 308 309 (void)watchman; 310 311 if (!frame->sweep_ids) 312 return; 313 314 frame->frame_duration = sync_timestamp - frame->sync_timestamp; 315 316 /* 317 * If a single base station is running in 'B' mode, skipped frames 318 * will still contain old data. 319 */ 320 if (frame->frame_duration > 1000000) 321 return; 322 323 // telemetry_send_lighthouse_frame(watchman->id, frame); 324} 325 326/* 327 * The pulse length encodes three bits. The skip bit indicates whether the 328 * emitting base will enable the sweeping laser in the next sweep window. 329 * The data bit is collected to eventually assemble the OOTX frame. The rotor 330 * bit indicates whether the next sweep will be horizontal (0) or vertical (1): 331 * 332 * duration 3000 3500 4000 4500 5000 5500 6000 6500 (in 48 MHz ticks) 333 * skip 0 0 0 0 1 1 1 1 334 * data 0 0 1 1 0 0 1 1 335 * rotor 0 1 0 1 0 1 0 1 336 */ 337#define SKIP_BIT 4 338#define DATA_BIT 2 339#define ROTOR_BIT 1 340 341static void 342_handle_sync_pulse(struct lighthouse_watchman *watchman, struct lighthouse_pulse *sync) 343{ 344 struct lighthouse_base *base; 345 unsigned char channel; 346 int32_t dt; 347 unsigned int code; 348 349 if (!sync->duration) 350 return; 351 352 if (sync->duration < 2750 || sync->duration > 6750) { 353 LH_WARN("%s: Unknown pulse length: %d", watchman->name, sync->duration); 354 return; 355 } 356 code = (sync->duration - 2750) / 500; 357 358 dt = sync->timestamp - watchman->last_timestamp; 359 360 /* 48 MHz / 120 Hz = 400000 cycles per sync pulse */ 361 if (dt > (400000 - 1000) && dt < (400000 + 1000)) { 362 /* Observing a single base station, channel A (or B, actually) 363 */ 364 channel = 'A'; 365 } else if (dt > (380000 - 1000) && dt < (380000 + 1000)) { 366 /* Observing two base stations, this is channel B */ 367 channel = 'B'; 368 } else if (dt > (20000 - 1000) && dt < (20000 + 1000)) { 369 /* Observing two base stations, this is channel C */ 370 channel = 'C'; 371 } else { 372 if (dt > -1000 && dt < 1000) { 373 /* 374 * Ignore, this means we prematurely finished 375 * assembling the last sync pulse. 376 */ 377 } else { 378 /* Irregular sync pulse */ 379 if (watchman->last_timestamp) 380 LH_WARN( 381 "%s: Irregular sync pulse: %08x -> %08x " 382 "(%+d)", 383 watchman->name, watchman->last_timestamp, sync->timestamp, dt); 384 lighthouse_base_reset(&watchman->base[0]); 385 lighthouse_base_reset(&watchman->base[1]); 386 } 387 388 watchman->last_timestamp = sync->timestamp; 389 return; 390 } 391 392 base = &watchman->base[channel == 'C']; 393 base->channel = channel; 394 base->last_sync_timestamp = sync->timestamp; 395 lighthouse_base_handle_ootx_data_bit(watchman, base, (code & DATA_BIT)); 396 lighthouse_base_handle_frame(watchman, base, sync->timestamp); 397 398 base->active_rotor = (code & ROTOR_BIT); 399 if (!(code & SKIP_BIT)) { 400 struct lighthouse_frame *frame = &base->frame[code & ROTOR_BIT]; 401 402 watchman->active_base = base; 403 frame->sync_timestamp = sync->timestamp; 404 frame->sync_duration = sync->duration; 405 frame->sweep_ids = 0; 406 } 407 408 watchman->last_timestamp = sync->timestamp; 409} 410 411static void 412_handle_sweep_pulse(struct lighthouse_watchman *watchman, uint8_t id, uint32_t timestamp, uint16_t duration) 413{ 414 struct lighthouse_base *base = watchman->active_base; 415 struct lighthouse_frame *frame; 416 int32_t offset; 417 418 (void)id; 419 420 if (!base) { 421 LH_WARN("%s: sweep without sync", watchman->name); 422 return; 423 } 424 425 frame = &base->frame[base->active_rotor]; 426 427 offset = timestamp - base->last_sync_timestamp; 428 429 /* Ignore short sync pulses or sweeps without a corresponding sync */ 430 if (offset > 379000) 431 return; 432 433 if (!pulse_in_sweep_window(offset, duration)) { 434 LH_WARN( 435 "%s: sweep offset out of range: rotor %u offset %u " 436 "duration %u", 437 watchman->name, base->active_rotor, offset, duration); 438 return; 439 } 440 441 if (frame->sweep_ids & (1 << id)) { 442 LH_WARN("%s: sensor %u triggered twice per frame, assuming reflection", watchman->name, id); 443 return; 444 } 445 446 frame->sweep_duration[id] = duration; 447 frame->sweep_offset[id] = offset; 448 frame->sweep_ids |= (1 << id); 449} 450 451static void 452accumulate_sync_pulse(struct lighthouse_watchman *watchman, uint8_t id, uint32_t timestamp, uint16_t duration) 453{ 454 int32_t dt = timestamp - watchman->last_sync.timestamp; 455 456 if (dt > watchman->last_sync.duration || watchman->last_sync.duration == 0) { 457 watchman->seen_by = 1 << id; 458 watchman->last_sync.timestamp = timestamp; 459 watchman->last_sync.duration = duration; 460 watchman->last_sync.id = id; 461 } else { 462 watchman->seen_by |= 1 << id; 463 if (timestamp < watchman->last_sync.timestamp) { 464 watchman->last_sync.duration += watchman->last_sync.timestamp - timestamp; 465 watchman->last_sync.timestamp = timestamp; 466 } 467 if (duration > watchman->last_sync.duration) 468 watchman->last_sync.duration = duration; 469 watchman->last_sync.duration = duration; 470 } 471} 472 473void 474lighthouse_watchman_handle_pulse(struct lighthouse_watchman *watchman, 475 uint8_t id, 476 uint16_t duration, 477 uint32_t timestamp) 478{ 479 int32_t dt; 480 481 dt = timestamp - watchman->last_sync.timestamp; 482 483 if (watchman->sync_lock) { 484 if (watchman->seen_by && dt > watchman->last_sync.duration) { 485 _handle_sync_pulse(watchman, &watchman->last_sync); 486 watchman->seen_by = 0; 487 } 488 489 if (pulse_in_this_sync_window(dt, duration) || pulse_in_next_sync_window(dt, duration)) { 490 accumulate_sync_pulse(watchman, id, timestamp, duration); 491 } else if (pulse_in_sweep_window(dt, duration)) { 492 _handle_sweep_pulse(watchman, id, timestamp, duration); 493 } else { 494 /* 495 * Spurious pulse - this could be due to a reflection or 496 * misdetected sync. If dt > period, drop the sync lock. 497 * Maybe we should ignore a single missed sync. 498 */ 499 if (dt > 407500) { 500 watchman->sync_lock = false; 501 LH_WARN("%s: late pulse, lost sync", watchman->name); 502 } else { 503 LH_WARN("%s: spurious pulse: %08x (%02x %d %u)", watchman->name, timestamp, id, dt, 504 duration); 505 } 506 watchman->seen_by = 0; 507 } 508 } else { 509 /* 510 * If we've not locked onto the periodic sync signals, try to 511 * treat all pulses within the right duration range as potential 512 * sync pulses. 513 * This is still a bit naive. If the sensors are moved too 514 * close to the lighthouse base station, sweep pulse durations 515 * may fall into this range and sweeps may be misdetected as 516 * sync floods. 517 */ 518 if (duration >= 2750 && duration <= 6750) { 519 /* 520 * Decide we've locked on if the pulse falls into any 521 * of the expected time windows from the last 522 * accumulated sync pulse. 523 */ 524 if (pulse_in_next_sync_window(dt, duration)) { 525 LH_WARN("%s: sync locked", watchman->name); 526 watchman->sync_lock = true; 527 } 528 529 accumulate_sync_pulse(watchman, id, timestamp, duration); 530 } else { 531 /* Assume this is a sweep, ignore it until we lock */ 532 } 533 } 534} 535 536void 537lighthouse_watchman_init(struct lighthouse_watchman *watchman, const char *name) 538{ 539 watchman->id = watchman_id++; 540 watchman->name = name; 541 watchman->seen_by = 0; 542 watchman->last_timestamp = 0; 543 watchman->last_sync.timestamp = 0; 544 watchman->last_sync.duration = 0; 545 log_level = debug_get_log_option_vive_log(); 546}