ar_drivers/
lib.rs

1// Copyright (C) 2023, Alex Badics
2// This file is part of ar-drivers-rs
3// Licensed under the MIT license. See LICENSE file in the project root for details.
4
5#![warn(missing_docs)]
6//! This crate contains a simplified driver for Rokid Air and Mad Gaze Glow AR glasses.
7//! It supports getting basic sensor data and setting up the display.
8//!
9//! Example usage (in a thread, probably):
10//! ```ignore
11//! let mut glasses = any_glasses().unwrap();
12//! loop {
13//!     match glasses.read_event().unwrap() {
14//!         GlassesEvent::AccGyro {accelerometer, gyroscope} => ...,
15//!         GlassesEvent::Magnetometer(data) => ...,
16//!         GlassesEvent::KeyPress(data) => ...,
17//!         _ => {}
18//!     }
19//! }
20//! ```
21//!
22//! As opposed to e.g. Rokid's own API, this is all that you get, since this is what comes
23//! out of the hardware. To get quaternions, you should probably use a lib that implements
24//! Madgwicks algorithm or a proper EKF. One good choice is the `eskf` crate.
25//!
26//! ## Feature flags
27//!
28//! Support for individual AR glasses types ca be enabled with the following features:
29//!
30//! * `mad_gaze`: Mad Gaze Glow
31//! * `nreal`: Nreal Light
32//! * `rokid`: Rokid Air
33//!
34//! All of them are enabled by default, which may bring in some unwanted dependencies if you
35//! only want to support a specific type.
36
37use nalgebra::{Isometry3, Matrix3, UnitQuaternion, Vector2, Vector3};
38
39#[cfg(feature = "grawoow")]
40pub mod grawoow;
41#[cfg(feature = "mad_gaze")]
42pub mod mad_gaze;
43#[cfg(feature = "nreal")]
44pub mod nreal_air;
45#[cfg(feature = "nreal")]
46pub mod nreal_light;
47#[cfg(feature = "rokid")]
48pub mod rokid;
49mod util;
50
51/// Possible errors resulting from `ar-drivers` API calls
52#[derive(Debug)]
53pub enum Error {
54    /// A standard IO error happened. See [`std::io::Error`] for specifics
55    IoError(std::io::Error),
56    /// An rusb error happened. See [`rusb::Error`] for specifics
57    #[cfg(feature = "rusb")]
58    UsbError(rusb::Error),
59    /// A hidapi error happened. See [`hidapi::HidError`] for specifics
60    #[cfg(feature = "hidapi")]
61    HidError(hidapi::HidError),
62    /// A serialport error happened. See [`serialport::Error`] for specifics
63    #[cfg(feature = "serialport")]
64    SerialPortError(serialport::Error),
65    /// No glasses were found.
66    NotFound,
67    /// Packet sending or reception timed out. Note that this is not the only
68    /// timeout error that is sent (e.g. UsbError can contain a timeout), and
69    /// also this is usually a fatal one.
70    PacketTimeout,
71    /// Other fatal error, usually a problem with the library itself, or
72    /// a device support issue. File a bug if you encounter this.
73    Other(&'static str),
74}
75
76type Result<T> = std::result::Result<T, Error>;
77
78impl std::error::Error for Error {
79    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
80        match self {
81            Error::IoError(e) => Some(e),
82            #[cfg(feature = "rusb")]
83            Error::UsbError(e) => Some(e),
84            #[cfg(feature = "hidapi")]
85            Error::HidError(e) => Some(e),
86            #[cfg(feature = "serialport")]
87            Error::SerialPortError(e) => Some(e),
88            _ => None,
89        }
90    }
91}
92
93impl std::fmt::Display for Error {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        f.write_str(match self {
96            Error::IoError(_) => "I/O error",
97            #[cfg(feature = "rusb")]
98            Error::UsbError(_) => "Libusb error",
99            #[cfg(feature = "hidapi")]
100            Error::HidError(_) => "Hidapi error",
101            #[cfg(feature = "serialport")]
102            Error::SerialPortError(_) => "Serial error",
103            Error::NotFound => "Glasses not found",
104            Error::PacketTimeout => "Packet timeout",
105            Error::Other(s) => s,
106        })
107    }
108}
109
110/// AR glasses sensor event, got from [`ARGlasses::read_event`]
111///
112/// Coordinate system is "RUB": Positive X is Right, Positive Y is Up, Positive Z is backwards.
113/// This is the same as the Android sensor coordinate system.
114#[derive(Debug, Clone)]
115pub enum GlassesEvent {
116    /// Synchronized accelerometer and gyroscope data.
117    AccGyro {
118        /// Accelerometer data in m^2/s.
119        ///
120        /// Remember that while gravitational acceleration is "down", the acceleration
121        /// the device "feels" is the one opposite from that, so the normal reading
122        /// when the device is upright is (0, 9.81, 0)
123        accelerometer: Vector3<f32>,
124        /// Gyroscope data. Right handed rotation in rad/sec,
125        /// e.g. turning left is positive y axis.
126        gyroscope: Vector3<f32>,
127        /// Timestamp, in device time, in microseconds
128        timestamp: u64,
129    },
130    /// Magnetometer data.
131    Magnetometer {
132        /// Direction of magnetic north (more or less). Unit is uT.
133        magnetometer: Vector3<f32>,
134        /// Timestamp, in device time, in microseconds
135        timestamp: u64,
136    },
137    /// A key was pressed (sent once per press)
138    /// The number is a key ID, starting from 0.
139    KeyPress(u8),
140
141    /// Proximity sensor senses the user, i.e. the glasses were put on
142    /// Sent once per event.
143    ProximityNear,
144
145    /// Proximity sensor senses the user, i.e. the glasses were taken off.
146    /// Sent once per event.
147    ProximityFar,
148    /// Ambient light level. Unit is vendor-specific
149    AmbientLight(u16),
150    /// V-sync happened on the device
151    VSync,
152}
153
154/// Display mode used by [`ARGlasses::set_display_mode`]
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum DisplayMode {
157    /// Picture should be same for both eyes (simple full HD mode)
158    SameOnBoth,
159    /// Set display to 3840*1080 or 3840x1200,
160    /// where the left half is the left eye, the right half is the right eye
161    Stereo,
162    /// Set display to half-SBS mode, which presents itself as 1920*1080 resolution,
163    /// but actually scales down everything to 960x540,then upscales to 3840x1080
164    HalfSBS,
165    /// Set display to mirrored high refresh rate mode (typically 120Hz)
166    HighRefreshRate,
167    /// Set display to high refresh rate SBS mode
168    HighRefreshRateSBS,
169}
170
171/// Display side used by [`ARGlasses::view_matrix`]
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum Side {
174    /// Left display
175    Left,
176    /// Right display
177    Right,
178}
179
180/// Common interface for AR implemented glasses
181pub trait ARGlasses: Send {
182    /// Get the serial number of the glasses
183    fn serial(&mut self) -> Result<String>;
184    /// Get a single sensor event. Blocks.
185    fn read_event(&mut self) -> Result<GlassesEvent>;
186    /// Get the display mode of the glasses. See [`DisplayMode`]
187    fn get_display_mode(&mut self) -> Result<DisplayMode>;
188    /// Set the display mode of the glasses. See [`DisplayMode`]
189    fn set_display_mode(&mut self, display_mode: DisplayMode) -> Result<()>;
190    /// Field of view of the display along the horizontal axis, in radians
191    fn display_fov(&self) -> f32;
192    /// Transformation from IMU frame to display frame, at the specified
193    /// IPD (interpupillary distance). The `ipd` parameter is in meters.
194    /// A typical value is 0.07.
195    fn imu_to_display_matrix(&self, side: Side, ipd: f32) -> Isometry3<f64>;
196    /// Name of the device
197    fn name(&self) -> &'static str;
198    /// Get built-in camera descriptors
199    fn cameras(&self) -> Result<Vec<CameraDescriptor>> {
200        Ok(Vec::new())
201    }
202    /// The additional delay (in usecs) of the glasses' display from getting the data
203    /// on DisplayPort. This is not really an absolute value, but more of
204    /// a relative measure between different glasses.
205    /// In the future this may depend on the current display mode.
206    fn display_delay(&self) -> u64;
207}
208
209/// Represents one built-in camera
210///
211/// Warning: Experimental. May change between any versions.
212#[derive(Debug, Clone)]
213pub struct CameraDescriptor {
214    /// The unique name for the type of the camera.
215    pub name: &'static str,
216    /// The width and height in pixels for the calibration data
217    pub resolution: Vector2<f64>,
218    /// The intrinsic matrix of the camera
219    pub intrinsic_matrix: Matrix3<f64>,
220    /// Distortion coefficients: k1, k2, p1, p2, k3
221    pub distortion: [f64; 5],
222    /// Additional rectification matrix for stereo cameras
223    pub stereo_rotation: UnitQuaternion<f64>,
224    /// Transformation from the IMU frame to the camera frame
225    pub imu_to_camera: Isometry3<f64>,
226}
227
228/// Convenience function to detect and connect to any of the supported glasses
229#[cfg(not(target_os = "android"))]
230pub fn any_glasses() -> Result<Box<dyn ARGlasses>> {
231    #[cfg(feature = "rokid")]
232    if let Ok(glasses) = rokid::RokidAir::new() {
233        return Ok(Box::new(glasses));
234    };
235    #[cfg(feature = "nreal")]
236    if let Ok(glasses) = nreal_air::NrealAir::new() {
237        return Ok(Box::new(glasses));
238    };
239    #[cfg(feature = "nreal")]
240    if let Ok(glasses) = nreal_light::NrealLight::new() {
241        return Ok(Box::new(glasses));
242    };
243    #[cfg(feature = "grawoow")]
244    if let Ok(glasses) = grawoow::GrawoowG530::new() {
245        return Ok(Box::new(glasses));
246    };
247    #[cfg(feature = "mad_gaze")]
248    if let Ok(glasses) = mad_gaze::MadGazeGlow::new() {
249        return Ok(Box::new(glasses));
250    };
251    Err(Error::NotFound)
252}
253
254impl From<std::io::Error> for Error {
255    fn from(e: std::io::Error) -> Self {
256        Error::IoError(e)
257    }
258}
259
260#[cfg(feature = "rusb")]
261impl From<rusb::Error> for Error {
262    fn from(e: rusb::Error) -> Self {
263        Error::UsbError(e)
264    }
265}
266
267#[cfg(feature = "hidapi")]
268impl From<hidapi::HidError> for Error {
269    fn from(e: hidapi::HidError) -> Self {
270        Error::HidError(e)
271    }
272}
273
274#[cfg(feature = "serialport")]
275impl From<serialport::Error> for Error {
276    fn from(e: serialport::Error) -> Self {
277        Error::SerialPortError(e)
278    }
279}
280
281impl From<&'static str> for Error {
282    fn from(e: &'static str) -> Self {
283        Error::Other(e)
284    }
285}