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}