The open source OpenXR runtime
at main 378 lines 13 kB view raw
1// Copyright 2021, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief WMR camera and IMU data source. 6 * @author Mateo de Mayo <mateo.demayo@collabora.com> 7 * @ingroup drv_wmr 8 */ 9#include "wmr_source.h" 10#include "wmr_camera.h" 11#include "wmr_config.h" 12#include "wmr_protocol.h" 13 14#include "math/m_api.h" 15#include "math/m_clock_tracking.h" 16#include "math/m_filter_fifo.h" 17#include "util/u_debug.h" 18#include "util/u_sink.h" 19#include "util/u_var.h" 20#include "util/u_trace_marker.h" 21#include "xrt/xrt_tracking.h" 22#include "xrt/xrt_frameserver.h" 23 24#include <assert.h> 25#include <stdio.h> 26 27#define WMR_SOURCE_STR "WMR Source" 28 29#define WMR_TRACE(w, ...) U_LOG_IFL_T(w->log_level, __VA_ARGS__) 30#define WMR_DEBUG(w, ...) U_LOG_IFL_D(w->log_level, __VA_ARGS__) 31#define WMR_INFO(w, ...) U_LOG_IFL_I(w->log_level, __VA_ARGS__) 32#define WMR_WARN(w, ...) U_LOG_IFL_W(w->log_level, __VA_ARGS__) 33#define WMR_ERROR(w, ...) U_LOG_IFL_E(w->log_level, __VA_ARGS__) 34#define WMR_ASSERT(predicate, ...) \ 35 do { \ 36 bool p = predicate; \ 37 if (!p) { \ 38 U_LOG(U_LOGGING_ERROR, __VA_ARGS__); \ 39 assert(false && "WMR_ASSERT failed: " #predicate); \ 40 exit(EXIT_FAILURE); \ 41 } \ 42 } while (false); 43#define WMR_ASSERT_(predicate) WMR_ASSERT(predicate, "Assertion failed " #predicate) 44 45DEBUG_GET_ONCE_LOG_OPTION(wmr_log, "WMR_LOG", U_LOGGING_INFO) 46 47/*! 48 * Handles all the data sources from the WMR driver 49 * 50 * @todo Currently only properly handling tracking cameras, move IMU and other sources here 51 * @implements xrt_fs 52 * @implements xrt_frame_node 53 */ 54struct wmr_source 55{ 56 struct xrt_fs xfs; 57 struct xrt_frame_node node; 58 enum u_logging_level log_level; //!< Log level 59 60 struct wmr_hmd_config config; 61 struct wmr_camera *camera; 62 63 // Sinks (head tracking) 64 struct xrt_frame_sink cam_sinks[WMR_MAX_CAMERAS]; //!< Intermediate sinks for camera frames 65 struct xrt_imu_sink imu_sink; //!< Intermediate sink for IMU samples 66 struct xrt_slam_sinks in_sinks; //!< Pointers to intermediate sinks 67 struct xrt_slam_sinks out_sinks; //!< Pointers to downstream sinks 68 69 // UI Sinks (head tracking) 70 struct u_sink_debug ui_cam_sinks[WMR_MAX_CAMERAS]; //!< Sink to display camera frames in UI 71 struct m_ff_vec3_f32 *gyro_ff; //!< Queue of gyroscope data to display in UI 72 struct m_ff_vec3_f32 *accel_ff; //!< Queue of accelerometer data to display in UI 73 74 bool is_running; //!< Whether the device is streaming 75 bool first_imu_received; //!< Don't send frames until first IMU sample 76 timepoint_ns last_imu_ns; //!< Last timepoint received. 77 time_duration_ns hw2mono; //!< Estimated offset from IMU to monotonic clock 78 time_duration_ns cam_hw2mono; //!< Caches hw2mono for use in the full frame bundle 79}; 80 81/* 82 * 83 * Sinks functionality 84 * 85 */ 86 87#define DEFINE_RECEIVE_CAM(cam_id) \ 88 static void receive_cam##cam_id(struct xrt_frame_sink *sink, struct xrt_frame *xf) \ 89 { \ 90 struct wmr_source *ws = container_of(sink, struct wmr_source, cam_sinks[cam_id]); \ 91 if (cam_id == 0) { \ 92 ws->cam_hw2mono = ws->hw2mono; \ 93 } \ 94 xf->timestamp += ws->cam_hw2mono; \ 95 WMR_TRACE(ws, "cam" #cam_id " img t=%" PRId64 " source_t=%" PRId64, xf->timestamp, \ 96 xf->source_timestamp); \ 97 u_sink_debug_push_frame(&ws->ui_cam_sinks[cam_id], xf); \ 98 if (ws->out_sinks.cams[cam_id] && ws->first_imu_received) { \ 99 xrt_sink_push_frame(ws->out_sinks.cams[cam_id], xf); \ 100 } \ 101 } 102 103DEFINE_RECEIVE_CAM(0) 104DEFINE_RECEIVE_CAM(1) 105DEFINE_RECEIVE_CAM(2) 106DEFINE_RECEIVE_CAM(3) 107 108//! Define a function for each WMR_MAX_CAMERAS and reference it in this array 109void (*receive_cam[WMR_MAX_CAMERAS])(struct xrt_frame_sink *, struct xrt_frame *) = { 110 receive_cam0, // 111 receive_cam1, // 112 receive_cam2, // 113 receive_cam3, // 114}; 115 116static void 117receive_imu_sample(struct xrt_imu_sink *sink, struct xrt_imu_sample *s) 118{ 119 struct wmr_source *ws = container_of(sink, struct wmr_source, imu_sink); 120 121 // Convert hardware timestamp into monotonic clock. Update offset estimate hw2mono. 122 // Note this is only done with IMU samples as they have the smallest USB transmission time. 123 const float IMU_FREQ = 250.f; //!< @todo use 1000 if "average_imus" is false 124 timepoint_ns now_hw = s->timestamp_ns; 125 timepoint_ns now_mono = (timepoint_ns)os_monotonic_get_ns(); 126 timepoint_ns ts = m_clock_offset_a2b(IMU_FREQ, now_hw, now_mono, &ws->hw2mono); 127 128 /* 129 * Check if the timepoint does time travel, we get one or two 130 * old samples when the device has not been cleanly shut down. 131 */ 132 if (ws->last_imu_ns > ts) { 133 WMR_WARN(ws, "Received sample from the past, new: %" PRIu64 ", last: %" PRIu64 ", diff: %" PRIu64, ts, 134 s->timestamp_ns, ts - s->timestamp_ns); 135 return; 136 } 137 138 ws->first_imu_received = true; 139 ws->last_imu_ns = ts; 140 s->timestamp_ns = ts; 141 142 struct xrt_vec3_f64 a = s->accel_m_s2; 143 struct xrt_vec3_f64 w = s->gyro_rad_secs; 144 WMR_TRACE(ws, "imu t=%" PRId64 " a=(%f %f %f) w=(%f %f %f)", ts, a.x, a.y, a.z, w.x, w.y, w.z); 145 146 // Push to debug UI 147 struct xrt_vec3 gyro = {(float)w.x, (float)w.y, (float)w.z}; 148 struct xrt_vec3 accel = {(float)a.x, (float)a.y, (float)a.z}; 149 m_ff_vec3_f32_push(ws->gyro_ff, &gyro, ts); 150 m_ff_vec3_f32_push(ws->accel_ff, &accel, ts); 151 152 if (ws->out_sinks.imu) { 153 xrt_sink_push_imu(ws->out_sinks.imu, s); 154 } 155} 156 157 158/* 159 * 160 * Frameserver functionality 161 * 162 */ 163 164static inline struct wmr_source * 165wmr_source_from_xfs(struct xrt_fs *xfs) 166{ 167 struct wmr_source *ws = container_of(xfs, struct wmr_source, xfs); 168 return ws; 169} 170 171static bool 172wmr_source_enumerate_modes(struct xrt_fs *xfs, struct xrt_fs_mode **out_modes, uint32_t *out_count) 173{ 174 WMR_ASSERT(false, "Not implemented"); 175 return false; 176} 177 178static bool 179wmr_source_configure_capture(struct xrt_fs *xfs, struct xrt_fs_capture_parameters *cp) 180{ 181 WMR_ASSERT(false, "Not implemented"); 182 return false; 183} 184 185static bool 186wmr_source_stream_stop(struct xrt_fs *xfs) 187{ 188 DRV_TRACE_MARKER(); 189 190 struct wmr_source *ws = wmr_source_from_xfs(xfs); 191 192 bool stopped = wmr_camera_stop(ws->camera); 193 if (!stopped) { 194 WMR_ERROR(ws, "Unable to stop WMR cameras"); 195 WMR_ASSERT_(false); 196 } 197 198 return stopped; 199} 200 201static bool 202wmr_source_is_running(struct xrt_fs *xfs) 203{ 204 DRV_TRACE_MARKER(); 205 206 struct wmr_source *ws = wmr_source_from_xfs(xfs); 207 return ws->is_running; 208} 209 210static bool 211wmr_source_stream_start(struct xrt_fs *xfs, 212 struct xrt_frame_sink *xs, 213 enum xrt_fs_capture_type capture_type, 214 uint32_t descriptor_index) 215{ 216 DRV_TRACE_MARKER(); 217 218 struct wmr_source *ws = wmr_source_from_xfs(xfs); 219 220 if (xs == NULL && capture_type == XRT_FS_CAPTURE_TYPE_TRACKING) { 221 WMR_INFO(ws, "Starting WMR stream in tracking mode"); 222 } else if (xs != NULL && capture_type == XRT_FS_CAPTURE_TYPE_CALIBRATION) { 223 WMR_INFO(ws, "Starting WMR stream in calibration mode, will stream only cam0 frames"); 224 ws->out_sinks.cam_count = 1; 225 ws->out_sinks.cams[0] = xs; 226 } else { 227 WMR_ASSERT(false, "Unsupported stream configuration xs=%p capture_type=%d", (void *)xs, capture_type); 228 return false; 229 } 230 231 bool started = wmr_camera_start(ws->camera); 232 if (!started) { 233 WMR_ERROR(ws, "Unable to start WMR cameras"); 234 WMR_ASSERT_(false); 235 } 236 237 ws->is_running = started; 238 return ws->is_running; 239} 240 241static bool 242wmr_source_slam_stream_start(struct xrt_fs *xfs, struct xrt_slam_sinks *sinks) 243{ 244 DRV_TRACE_MARKER(); 245 246 struct wmr_source *ws = wmr_source_from_xfs(xfs); 247 if (sinks != NULL) { 248 ws->out_sinks = *sinks; 249 } 250 return wmr_source_stream_start(xfs, NULL, XRT_FS_CAPTURE_TYPE_TRACKING, 0); 251} 252 253 254/* 255 * 256 * Frame node functionality 257 * 258 */ 259 260static void 261wmr_source_node_break_apart(struct xrt_frame_node *node) 262{ 263 DRV_TRACE_MARKER(); 264 265 struct wmr_source *ws = container_of(node, struct wmr_source, node); 266 wmr_source_stream_stop(&ws->xfs); 267} 268 269static void 270wmr_source_node_destroy(struct xrt_frame_node *node) 271{ 272 DRV_TRACE_MARKER(); 273 274 struct wmr_source *ws = container_of(node, struct wmr_source, node); 275 WMR_DEBUG(ws, "Destroying WMR source"); 276 for (int i = 0; i < ws->config.tcam_count; i++) { 277 u_sink_debug_destroy(&ws->ui_cam_sinks[i]); 278 } 279 m_ff_vec3_f32_free(&ws->gyro_ff); 280 m_ff_vec3_f32_free(&ws->accel_ff); 281 u_var_remove_root(ws); 282 if (ws->camera != NULL) { // It could be null if XRT_HAVE_LIBUSB is not defined 283 wmr_camera_free(ws->camera); 284 } 285 free(ws); 286} 287 288 289/* 290 * 291 * Exported functions 292 * 293 */ 294 295//! Create and open the frame server for IMU/camera streaming. 296struct xrt_fs * 297wmr_source_create(struct xrt_frame_context *xfctx, struct xrt_prober_device *dev_holo, struct wmr_hmd_config cfg) 298{ 299 DRV_TRACE_MARKER(); 300 301 struct wmr_source *ws = U_TYPED_CALLOC(struct wmr_source); 302 ws->log_level = debug_get_log_option_wmr_log(); 303 304 // Setup xrt_fs 305 struct xrt_fs *xfs = &ws->xfs; 306 xfs->enumerate_modes = wmr_source_enumerate_modes; 307 xfs->configure_capture = wmr_source_configure_capture; 308 xfs->stream_start = wmr_source_stream_start; 309 xfs->slam_stream_start = wmr_source_slam_stream_start; 310 xfs->stream_stop = wmr_source_stream_stop; 311 xfs->is_running = wmr_source_is_running; 312 (void)snprintf(xfs->name, sizeof(xfs->name), WMR_SOURCE_STR); 313 (void)snprintf(xfs->product, sizeof(xfs->product), WMR_SOURCE_STR " Product"); 314 (void)snprintf(xfs->manufacturer, sizeof(xfs->manufacturer), WMR_SOURCE_STR " Manufacturer"); 315 (void)snprintf(xfs->serial, sizeof(xfs->serial), WMR_SOURCE_STR " Serial"); 316 xfs->source_id = *((uint64_t *)"WMR_SRC\0"); 317 318 // Setup sinks 319 for (int i = 0; i < WMR_MAX_CAMERAS; i++) { 320 ws->cam_sinks[i].push_frame = receive_cam[i]; 321 } 322 ws->imu_sink.push_imu = receive_imu_sample; 323 324 ws->in_sinks.cam_count = cfg.tcam_count; 325 for (int i = 0; i < cfg.tcam_count; i++) { 326 ws->in_sinks.cams[i] = &ws->cam_sinks[i]; 327 } 328 ws->in_sinks.imu = &ws->imu_sink; 329 330 struct wmr_camera_open_config options = { 331 .dev_holo = dev_holo, 332 .tcam_confs = cfg.tcams, 333 .tcam_sinks = ws->in_sinks.cams, 334 .tcam_count = cfg.tcam_count, 335 .slam_cam_count = cfg.slam_cam_count, 336 .log_level = ws->log_level, 337 }; 338 339 ws->camera = wmr_camera_open(&options); 340 ws->config = cfg; 341 342 // Setup UI 343 for (int i = 0; i < cfg.tcam_count; i++) { 344 u_sink_debug_init(&ws->ui_cam_sinks[i]); 345 } 346 m_ff_vec3_f32_alloc(&ws->gyro_ff, 1000); 347 m_ff_vec3_f32_alloc(&ws->accel_ff, 1000); 348 u_var_add_root(ws, WMR_SOURCE_STR, false); 349 u_var_add_log_level(ws, &ws->log_level, "Log Level"); 350 u_var_add_ro_ff_vec3_f32(ws, ws->gyro_ff, "Gyroscope"); 351 u_var_add_ro_ff_vec3_f32(ws, ws->accel_ff, "Accelerometer"); 352 for (int i = 0; i < cfg.tcam_count; i++) { 353 char label[] = "Camera NNNNNNNNNNN"; 354 (void)snprintf(label, sizeof(label), "Camera %d", i); 355 u_var_add_sink_debug(ws, &ws->ui_cam_sinks[i], label); 356 } 357 358 // Setup node 359 struct xrt_frame_node *xfn = &ws->node; 360 xfn->break_apart = wmr_source_node_break_apart; 361 xfn->destroy = wmr_source_node_destroy; 362 xrt_frame_context_add(xfctx, &ws->node); 363 364 WMR_DEBUG(ws, "WMR Source created"); 365 366 return xfs; 367} 368 369void 370wmr_source_push_imu_packet(struct xrt_fs *xfs, timepoint_ns t, struct xrt_vec3 accel, struct xrt_vec3 gyro) 371{ 372 DRV_TRACE_MARKER(); 373 struct wmr_source *ws = wmr_source_from_xfs(xfs); 374 struct xrt_vec3_f64 accel_f64 = {accel.x, accel.y, accel.z}; 375 struct xrt_vec3_f64 gyro_f64 = {gyro.x, gyro.y, gyro.z}; 376 struct xrt_imu_sample sample = {.timestamp_ns = t, .accel_m_s2 = accel_f64, .gyro_rad_secs = gyro_f64}; 377 xrt_sink_push_imu(&ws->imu_sink, &sample); 378}