realsense_rust/sensor.rs
1//! Type for abstracting over the concept of a Sensor
2//!
3//! The words device and sensor are overloaded in this industry, and librealsense2 makes no
4//! attempts to fix that. While the words device and sensor are often used colloquially, here
5//! `Device` refers to a whole Realsense package, such as a D435i, L515, etc. `Sensor` refers to
6//! "sub-devices"; rather, the individual sensing components that one can stream from. A sensor may
7//! be an IMU, which has accelerometer and gyroscope streams, or a camera of some kind, which has
8//! streams related to different types of images.
9//!
10//! The hierarchy is effectively:
11//!
12//! [`Device`] |-> [`Sensor`] |-> [`StreamProfile`]
13
14use crate::{
15 base::Rs2Roi,
16 check_rs2_error,
17 device::{Device, DeviceConstructionError},
18 kind::{
19 OptionSetError, Rs2CameraInfo, Rs2Exception, Rs2Extension, Rs2Option, Rs2OptionRange,
20 SENSOR_EXTENSIONS,
21 },
22 stream_profile::StreamProfile,
23};
24use anyhow::Result;
25use realsense_sys as sys;
26use std::{
27 convert::{From, TryInto},
28 ffi::CStr,
29 mem::MaybeUninit,
30 ptr::NonNull,
31};
32use thiserror::Error;
33
34/// Type describing errors that can occur when trying to construct a sensor.
35///
36/// Follows the standard pattern of errors where the enum variant describes what the low-level code
37/// was attempting to do while the string carried alongside describes the underlying error message
38/// from any C++ exceptions that occur.
39#[derive(Error, Debug)]
40pub enum SensorConstructionError {
41 /// Could not get the correct sensor from the sensor list.
42 #[error("Could not get correct sensor from sensor list. Type: {0}; Reason: {1}")]
43 CouldNotGetSensorFromList(Rs2Exception, String),
44}
45
46/// Type describing errors that can occur when trying to set the region of interest of a sensor.
47///
48/// Follows the standard pattern of errors where the enum variant describes what the low-level code
49/// was attempting to do while the string carried alongside describes the underlying error message
50/// from any C++ exceptions that occur.
51#[derive(Error, Debug)]
52pub enum RoiSetError {
53 /// Could not set region of interest for sensor.
54 #[error("Could not set region of interest for sensor. Type: {0}; Reason: {1}")]
55 CouldNotSetRoi(Rs2Exception, String),
56}
57
58/// Type for holding sensor-related data.
59///
60/// A sensor in librealsense2 corresponds to a physical component on the unit in some way, shape,
61/// or form. These may or may not correspond to multiple streams. e.g. an IMU on the device may
62/// correspond to accelerometer and gyroscope streams, or an IR camera sensor on the device may
63/// correspond to depth & video streams.
64///
65/// Sensors are constructed one of two ways:
66///
67/// 1. From the device's [sensor list](crate::device::Device::sensors)
68/// 2. By getting the sensor that [corresponds to a given frame](crate::frame::FrameEx::sensor)
69pub struct Sensor {
70 /// The underlying non-null sensor pointer.
71 ///
72 /// This should not be deleted unless the sensor was constructed via `rs2_create_sensor`
73 sensor_ptr: NonNull<sys::rs2_sensor>,
74 /// Boolean used for telling us if we should drop the sensor pointer or not.
75 should_drop: bool,
76}
77
78impl Drop for Sensor {
79 fn drop(&mut self) {
80 unsafe {
81 if self.should_drop {
82 sys::rs2_delete_sensor(self.sensor_ptr.as_ptr());
83 }
84 }
85 }
86}
87
88unsafe impl Send for Sensor {}
89
90impl std::convert::From<NonNull<sys::rs2_sensor>> for Sensor {
91 /// Attempt to construct a Sensor from a non-null pointer to `rs2_sensor`.
92 fn from(sensor_ptr: NonNull<sys::rs2_sensor>) -> Self {
93 Sensor {
94 sensor_ptr,
95 should_drop: false,
96 }
97 }
98}
99
100impl Sensor {
101 /// Create a sensor from a sensor list and an index
102 ///
103 /// Unlike when you directly acquire a `*mut rs2_sensor` from an API in librealsense2, such as
104 /// when calling `rs2_get_frame_sensor`, you have to drop this pointer at the end (because you
105 /// now own it). When calling `try_from` we don't want to drop in the default case, since our
106 /// `*mut rs2_sensor` may not be owned by us, but by the device / frame / etc.
107 ///
108 /// The main difference then is that this API defaults to using `rs2_create_sensor` vs. a call
109 /// to get a sensor from somewhere else.
110 ///
111 /// This can fail for similar reasons to `try_from`, and is likewise only valid if `index` is
112 /// less than the length of `sensor_list` (see `rs2_get_sensors_count` for how to get that
113 /// length).
114 ///
115 /// Guaranteeing the lifetime / semantics of the sensor is difficult, so this should probably
116 /// not be used outside of this crate. See `crate::device::Device` for where this is used.
117 ///
118 /// # Errors
119 ///
120 /// Returns [`SensorConstructionError::CouldNotGetSensorFromList`] if the index is invalid or
121 /// if the sensor list is invalid in some way.
122 pub(crate) fn try_create(
123 sensor_list: &NonNull<sys::rs2_sensor_list>,
124 index: i32,
125 ) -> Result<Self, SensorConstructionError> {
126 unsafe {
127 let mut err = std::ptr::null_mut::<sys::rs2_error>();
128
129 let sensor_ptr = sys::rs2_create_sensor(sensor_list.as_ptr(), index, &mut err);
130 check_rs2_error!(err, SensorConstructionError::CouldNotGetSensorFromList)?;
131
132 let nonnull_ptr = NonNull::new(sensor_ptr).unwrap();
133 let mut sensor = Sensor::from(nonnull_ptr);
134 sensor.should_drop = true;
135 Ok(sensor)
136 }
137 }
138
139 /// Get the parent device that this sensor corresponds to.
140 ///
141 /// Returns the device that this sensor corresponds to iff that device is still connected and
142 /// the sensor is still valid. Otherwise returns an error.
143 ///
144 /// # Errors
145 ///
146 /// Returns [`DeviceConstructionError::CouldNotCreateDeviceFromSensor`] if the device cannot be
147 /// obtained due to the physical device being disconnected or the internal sensor pointer
148 /// becoming invalid.
149 pub fn device(&self) -> Result<Device, DeviceConstructionError> {
150 unsafe {
151 let mut err = std::ptr::null_mut::<sys::rs2_error>();
152 let device_ptr = sys::rs2_create_device_from_sensor(self.sensor_ptr.as_ptr(), &mut err);
153 check_rs2_error!(err, DeviceConstructionError::CouldNotCreateDeviceFromSensor)?;
154
155 Ok(Device::from(NonNull::new(device_ptr).unwrap()))
156 }
157 }
158
159 /// Get sensor extension.
160 pub fn extension(&self) -> Rs2Extension {
161 let ext = SENSOR_EXTENSIONS
162 .iter()
163 .find(|ext| unsafe {
164 let mut err = std::ptr::null_mut::<sys::rs2_error>();
165 let is_extendable = sys::rs2_is_sensor_extendable_to(
166 self.sensor_ptr.as_ptr(),
167 #[allow(clippy::useless_conversion)]
168 (**ext as i32).try_into().unwrap(),
169 &mut err,
170 );
171
172 if err.as_ref().is_none() {
173 is_extendable != 0
174 } else {
175 sys::rs2_free_error(err);
176 false
177 }
178 })
179 .unwrap();
180 *ext
181 }
182
183 /// Get the value associated with the provided Rs2Option for the sensor.
184 ///
185 /// Returns An `f32` value corresponding to that option within the librealsense2 library, or None
186 /// if the option is not supported.
187 pub fn get_option(&self, option: Rs2Option) -> Option<f32> {
188 if !self.supports_option(option) {
189 return None;
190 }
191
192 unsafe {
193 let mut err = std::ptr::null_mut::<sys::rs2_error>();
194 let val = sys::rs2_get_option(
195 self.sensor_ptr.as_ptr().cast::<sys::rs2_options>(),
196 #[allow(clippy::useless_conversion)]
197 (option as i32).try_into().unwrap(),
198 &mut err,
199 );
200
201 if err.as_ref().is_none() {
202 Some(val)
203 } else {
204 sys::rs2_free_error(err);
205 None
206 }
207 }
208 }
209
210 /// Sets the `value` associated with the provided `option` for the sensor.
211 ///
212 /// Returns null tuple if the option can be successfully set on the sensor, otherwise an error.
213 ///
214 /// # Errors
215 ///
216 /// Returns [`OptionSetError::OptionNotSupported`] if the option is not supported on this
217 /// sensor.
218 ///
219 /// Returns [`OptionSetError::OptionIsReadOnly`] if the option is supported but cannot be set
220 /// on this sensor.
221 ///
222 /// Returns [`OptionSetError::CouldNotSetOption`] if the option is supported and not read-only,
223 /// but could not be set for another reason (invalid value, internal exception, etc.).
224 pub fn set_option(&mut self, option: Rs2Option, value: f32) -> Result<(), OptionSetError> {
225 if !self.supports_option(option) {
226 return Err(OptionSetError::OptionNotSupported);
227 }
228
229 if self.is_option_read_only(option) {
230 return Err(OptionSetError::OptionIsReadOnly);
231 }
232
233 unsafe {
234 let mut err = std::ptr::null_mut::<sys::rs2_error>();
235 sys::rs2_set_option(
236 self.sensor_ptr.as_ptr().cast::<sys::rs2_options>(),
237 #[allow(clippy::useless_conversion)]
238 (option as i32).try_into().unwrap(),
239 value,
240 &mut err,
241 );
242 check_rs2_error!(err, OptionSetError::CouldNotSetOption)?;
243
244 Ok(())
245 }
246 }
247
248 /// Gets the range for a given option.
249 ///
250 /// Returns some option range if the sensor supports the option, else `None`.
251 pub fn get_option_range(&self, option: Rs2Option) -> Option<Rs2OptionRange> {
252 if !self.supports_option(option) {
253 return None;
254 }
255
256 unsafe {
257 let mut err = std::ptr::null_mut::<sys::rs2_error>();
258
259 let mut min = MaybeUninit::uninit();
260 let mut max = MaybeUninit::uninit();
261 let mut step = MaybeUninit::uninit();
262 let mut default = MaybeUninit::uninit();
263
264 sys::rs2_get_option_range(
265 self.sensor_ptr.as_ptr().cast::<sys::rs2_options>(),
266 #[allow(clippy::useless_conversion)]
267 (option as i32).try_into().unwrap(),
268 min.as_mut_ptr(),
269 max.as_mut_ptr(),
270 step.as_mut_ptr(),
271 default.as_mut_ptr(),
272 &mut err,
273 );
274
275 if err.as_ref().is_none() {
276 Some(Rs2OptionRange {
277 min: min.assume_init(),
278 max: max.assume_init(),
279 step: step.assume_init(),
280 default: default.assume_init(),
281 })
282 } else {
283 sys::rs2_free_error(err);
284 None
285 }
286 }
287 }
288
289 /// Predicate for determining if this sensor supports a given option
290 ///
291 /// Returns true iff the option is supported by this sensor.
292 pub fn supports_option(&self, option: Rs2Option) -> bool {
293 unsafe {
294 let mut err = std::ptr::null_mut::<sys::rs2_error>();
295 let val = sys::rs2_supports_option(
296 self.sensor_ptr.as_ptr().cast::<sys::rs2_options>(),
297 #[allow(clippy::useless_conversion)]
298 (option as i32).try_into().unwrap(),
299 &mut err,
300 );
301
302 if err.as_ref().is_none() {
303 val != 0
304 } else {
305 sys::rs2_free_error(err);
306 false
307 }
308 }
309 }
310
311 /// Predicate for determining if the provided option is immutable or not.
312 ///
313 /// Returns true if the option is supported and can be mutated, otherwise false.
314 pub fn is_option_read_only(&self, option: Rs2Option) -> bool {
315 if !self.supports_option(option) {
316 return false;
317 }
318
319 unsafe {
320 let mut err = std::ptr::null_mut::<sys::rs2_error>();
321 let val = sys::rs2_is_option_read_only(
322 self.sensor_ptr.as_ptr().cast::<sys::rs2_options>(),
323 #[allow(clippy::useless_conversion)]
324 (option as i32).try_into().unwrap(),
325 &mut err,
326 );
327
328 if err.as_ref().is_none() {
329 val != 0
330 } else {
331 sys::rs2_free_error(err);
332 false
333 }
334 }
335 }
336
337 /// Get a list of stream profiles associated with this sensor
338 ///
339 /// Returns a vector containing all the stream profiles associated with the sensor. The vector
340 /// will have a length of zero if an error occurs while getting the stream profiles.
341 pub fn stream_profiles(&self) -> Vec<StreamProfile> {
342 let mut profiles = Vec::new();
343 unsafe {
344 let mut err = std::ptr::null_mut::<sys::rs2_error>();
345 let profiles_ptr = sys::rs2_get_stream_profiles(self.sensor_ptr.as_ptr(), &mut err);
346 if err.as_ref().is_some() {
347 sys::rs2_free_error(err);
348 return profiles;
349 }
350
351 let nonnull_profiles_ptr = NonNull::new(profiles_ptr).unwrap();
352 let len = sys::rs2_get_stream_profiles_count(nonnull_profiles_ptr.as_ptr(), &mut err);
353
354 if err.as_ref().is_some() {
355 sys::rs2_free_error(err);
356 sys::rs2_delete_stream_profiles_list(nonnull_profiles_ptr.as_ptr());
357 return profiles;
358 }
359
360 for i in 0..len {
361 match StreamProfile::try_create(&nonnull_profiles_ptr, i) {
362 Ok(s) => {
363 profiles.push(s);
364 }
365 Err(_) => {
366 continue;
367 }
368 }
369 }
370 sys::rs2_delete_stream_profiles_list(nonnull_profiles_ptr.as_ptr());
371 }
372 profiles
373 }
374
375 // fn recommended_processing_blocks(&self) -> Vec<ProcessingBlock>{}
376
377 /// Gets the value associated with the provided camera info key from the sensor.
378 ///
379 /// Returns some value corresponding to the camera info requested if this sensor supports that
380 /// camera info, else `None`.
381 pub fn info(&self, camera_info: Rs2CameraInfo) -> Option<&CStr> {
382 if !self.supports_info(camera_info) {
383 return None;
384 }
385
386 unsafe {
387 let mut err = std::ptr::null_mut::<sys::rs2_error>();
388
389 let val = sys::rs2_get_sensor_info(
390 self.sensor_ptr.as_ptr(),
391 #[allow(clippy::useless_conversion)]
392 (camera_info as i32).try_into().unwrap(),
393 &mut err,
394 );
395
396 if err.as_ref().is_none() {
397 Some(CStr::from_ptr(val))
398 } else {
399 sys::rs2_free_error(err);
400 None
401 }
402 }
403 }
404
405 /// Predicate method for determining if the sensor supports a certain kind of camera info.
406 ///
407 /// Returns true iff the sensor has a value associated with the `camera_info` key.
408 pub fn supports_info(&self, camera_info: Rs2CameraInfo) -> bool {
409 unsafe {
410 let mut err = std::ptr::null_mut::<sys::rs2_error>();
411 let supports_info = sys::rs2_supports_sensor_info(
412 self.sensor_ptr.as_ptr(),
413 #[allow(clippy::useless_conversion)]
414 (camera_info as i32).try_into().unwrap(),
415 &mut err,
416 );
417
418 if err.as_ref().is_none() {
419 supports_info != 0
420 } else {
421 sys::rs2_free_error(err);
422 false
423 }
424 }
425 }
426
427 /// Gets the auto exposure's region of interest for the sensor.
428 ///
429 /// Returns the region of interest for the auto exposure or None
430 /// if this isn't available.
431 pub fn get_region_of_interest(&self) -> Option<Rs2Roi> {
432 unsafe {
433 let mut err = std::ptr::null_mut::<sys::rs2_error>();
434 let mut roi = Rs2Roi {
435 min_x: 0,
436 min_y: 0,
437 max_x: 0,
438 max_y: 0,
439 };
440 sys::rs2_get_region_of_interest(
441 self.sensor_ptr.as_ptr(),
442 &mut roi.min_x,
443 &mut roi.min_y,
444 &mut roi.max_x,
445 &mut roi.max_y,
446 &mut err,
447 );
448 if err.as_ref().is_none() {
449 Some(roi)
450 } else {
451 sys::rs2_free_error(err);
452 None
453 }
454 }
455 }
456
457 /// Sets the auto exposure's region of interest to `roi` for the sensor.
458 ///
459 /// Returns null tuple if the region of interest was set successfully, otherwise an error.
460 ///
461 /// # Errors
462 ///
463 /// Returns [`RoiSetError::CouldNotSetRoi`] if setting the region of interest failed.
464 ///
465 /// # Known issues
466 ///
467 /// This command can fail directly after the pipeline start. This is a bug in librealsense.
468 /// Either wait for the first Frameset to be received or repeat the command in a loop
469 /// with a delay until it succeeds as suggested by Intel.
470 /// Issue at librealsense: https://github.com/IntelRealSense/librealsense/issues/8004
471 pub fn set_region_of_interest(&mut self, roi: Rs2Roi) -> Result<(), RoiSetError> {
472 unsafe {
473 let mut err = std::ptr::null_mut::<sys::rs2_error>();
474 sys::rs2_set_region_of_interest(
475 self.sensor_ptr.as_ptr(),
476 roi.min_x,
477 roi.min_y,
478 roi.max_x,
479 roi.max_y,
480 &mut err,
481 );
482 check_rs2_error!(err, RoiSetError::CouldNotSetRoi)
483 }
484 }
485}