use std::{collections::VecDeque, time::Duration};
use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
use rusb::{request_type, DeviceHandle, GlobalContext};
use crate::{
util::get_interface_for_endpoint, ARGlasses, DisplayMode, Error, GlassesEvent, Result, Side,
};
pub struct RokidAir {
device_handle: DeviceHandle<GlobalContext>,
last_accelerometer: Option<(Vector3<f32>, u64)>,
last_gyroscope: Option<(Vector3<f32>, u64)>,
previous_key_states: u8,
proxy_sensor_was_far: bool,
pending_events: VecDeque<GlassesEvent>,
model: RokidModel,
}
enum RokidModel {
Air,
Max,
}
const INTERRUPT_IN_ENDPOINT: u8 = 0x82;
const TIMEOUT: Duration = Duration::from_millis(250);
impl ARGlasses for RokidAir {
fn serial(&mut self) -> Result<String> {
let mut result = [0u8; 0x40];
self.device_handle.read_control(
request_type(
rusb::Direction::In,
rusb::RequestType::Vendor,
rusb::Recipient::Device,
),
0x81,
0x100,
0,
&mut result,
TIMEOUT,
)?;
Ok(
String::from_utf8(result.iter().copied().take_while(|c| *c != 0).collect())
.map_err(|_| "Invalid serial string")?,
)
}
fn read_event(&mut self) -> Result<GlassesEvent> {
while self.pending_events.is_empty() {
let mut packet_data = [0u8; 0x40];
self.device_handle
.read_interrupt(INTERRUPT_IN_ENDPOINT, &mut packet_data, TIMEOUT)?;
match packet_data[0] {
2 => {
let packet: &MiscPacket = bytemuck::cast_ref(&packet_data);
self.handle_key_press(packet.keys_pressed);
self.handle_proxy_sensor(packet.proxy_sensor);
}
4 => {
let packet: &SensorPacket = bytemuck::cast_ref(&packet_data);
let sensor_data =
Vector3::from_data(nalgebra::ArrayStorage([packet.vector; 1]));
match packet.sensor_type {
1 => self.last_accelerometer = Some((sensor_data, packet.timestamp)),
2 => self.last_gyroscope = Some((sensor_data, packet.timestamp)),
3 => self.pending_events.push_back(GlassesEvent::Magnetometer {
magnetometer: sensor_data,
timestamp: packet.timestamp,
}),
_ => (),
}
if let (Some((accelerometer, acc_ts)), Some((gyroscope, gyro_ts))) =
(self.last_accelerometer, self.last_gyroscope)
{
if acc_ts == gyro_ts {
self.last_gyroscope = None;
self.last_accelerometer = None;
self.pending_events.push_back(GlassesEvent::AccGyro {
accelerometer,
gyroscope,
timestamp: acc_ts,
});
}
}
}
17 => {
let packet: &CombinedPacket = bytemuck::cast_ref(&packet_data);
let timestamp = packet.timestamp / 1000;
self.pending_events.push_back(GlassesEvent::AccGyro {
accelerometer: Vector3::from_data(nalgebra::ArrayStorage(
[packet.accelerometer; 1],
)),
gyroscope: Vector3::from_data(nalgebra::ArrayStorage(
[packet.gyroscope; 1],
)),
timestamp,
});
self.pending_events.push_back(GlassesEvent::Magnetometer {
magnetometer: Vector3::from_data(nalgebra::ArrayStorage(
[packet.magnetometer; 1],
)),
timestamp,
});
self.handle_key_press(packet.keys_pressed);
self.handle_proxy_sensor(packet.proxy_sensor);
}
_ => {}
}
}
Ok(self.pending_events.pop_front().unwrap())
}
fn get_display_mode(&mut self) -> Result<DisplayMode> {
let mut result = [0; 0x40];
self.device_handle.read_control(
request_type(
rusb::Direction::In,
rusb::RequestType::Vendor,
rusb::Recipient::Device,
),
0x81,
0x0,
0x1,
&mut result,
TIMEOUT,
)?;
match result[1] {
0 => Ok(DisplayMode::SameOnBoth),
1 => Ok(DisplayMode::Stereo),
2 => Ok(DisplayMode::HalfSBS),
4 => Ok(DisplayMode::HighRefreshRateSBS),
_ => Ok(DisplayMode::HighRefreshRate),
}
}
fn set_display_mode(&mut self, display_mode: DisplayMode) -> Result<()> {
let display_mode = match display_mode {
DisplayMode::SameOnBoth => 0,
DisplayMode::Stereo => 1,
DisplayMode::HighRefreshRate => 3,
DisplayMode::HighRefreshRateSBS => 4,
_ => return Err(Error::Other("Display mode not supported")),
};
self.device_handle.write_control(
request_type(
rusb::Direction::Out,
rusb::RequestType::Vendor,
rusb::Recipient::Device,
),
0x1,
display_mode,
0x1,
&[0u8; 1],
TIMEOUT,
)?;
Ok(())
}
fn display_fov(&self) -> f32 {
match self.model {
RokidModel::Air => {
20f32.to_radians()
}
RokidModel::Max => {
23f32.to_radians()
}
}
}
fn imu_to_display_matrix(&self, side: Side, ipd: f32) -> Isometry3<f64> {
let tilt = match self.model {
RokidModel::Air => 0.022,
RokidModel::Max => 0.07,
};
let ipd = ipd as f64
* match side {
Side::Left => -0.5,
Side::Right => 0.5,
};
Translation3::new(ipd, 0.0, 0.0) * UnitQuaternion::from_euler_angles(tilt, 0.0, 0.0)
}
fn name(&self) -> &'static str {
match self.model {
RokidModel::Air => "Rokid Air",
RokidModel::Max => "Rokid Max",
}
}
fn display_delay(&self) -> u64 {
match self.model {
RokidModel::Air => 15000,
RokidModel::Max => 13000,
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct MiscPacket {
packet_type: u8,
seq: u32,
_unknown_0: [u8; 42],
keys_pressed: u8,
_unknown_1: [u8; 3],
proxy_sensor: u8,
_unknown_2: [u8; 12],
}
unsafe impl bytemuck::Zeroable for MiscPacket {}
unsafe impl bytemuck::Pod for MiscPacket {}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct SensorPacket {
packet_type: u8,
sensor_type: u8,
seq: u32,
_unknown_0: [u8; 3],
timestamp: u64,
_unknown_1: [u8; 4],
vector: [f32; 3],
_unknown_2: [u8; 31],
}
unsafe impl bytemuck::Zeroable for SensorPacket {}
unsafe impl bytemuck::Pod for SensorPacket {}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct CombinedPacket {
packet_type: u8,
timestamp: u64,
accelerometer: [f32; 3],
gyroscope: [f32; 3],
magnetometer: [f32; 3],
keys_pressed: u8,
proxy_sensor: u8,
_unknown_0: u8,
vsync_timestamp: u64,
_unknown_1: [u8; 3],
display_brightness: u8,
volume: u8,
_unknown_2: [u8; 3],
}
unsafe impl bytemuck::Zeroable for CombinedPacket {}
unsafe impl bytemuck::Pod for CombinedPacket {}
impl RokidAir {
pub const VID: u16 = 0x04d2;
pub const PID: u16 = 0x162f;
#[cfg(target_os = "android")]
pub fn new(fd: isize) -> Result<Self> {
use rusb::UsbContext;
unsafe { rusb::ffi::libusb_set_option(std::ptr::null_mut(), 2) };
let device_handle = unsafe { GlobalContext::default().open_device_with_fd(fd as i32) }?;
Self::new_common(device_handle)
}
#[cfg(not(target_os = "android"))]
pub fn new() -> Result<Self> {
use crate::util::get_device_vid_pid;
Self::new_common(get_device_vid_pid(Self::VID, Self::PID)?.open()?)
}
fn new_common(mut device_handle: DeviceHandle<GlobalContext>) -> Result<Self> {
device_handle.set_auto_detach_kernel_driver(true)?;
device_handle.claim_interface(
get_interface_for_endpoint(&device_handle.device(), INTERRUPT_IN_ENDPOINT).ok_or_else(
|| Error::Other("Could not find endpoint, wrong USB structure (probably)"),
)?,
)?;
let product_string = device_handle
.read_product_string_ascii(&device_handle.device().device_descriptor()?)?;
let result = Self {
device_handle,
last_accelerometer: None,
last_gyroscope: None,
previous_key_states: 0,
proxy_sensor_was_far: false,
model: if product_string.contains("Max") {
RokidModel::Max
} else {
RokidModel::Air
},
pending_events: Default::default(),
};
Ok(result)
}
fn handle_key_press(&mut self, keys_pressed: u8) {
let new_presses = keys_pressed & !self.previous_key_states;
for bit in 0..8 {
if new_presses & (1 << bit) != 0 {
self.pending_events.push_back(GlassesEvent::KeyPress(bit))
}
}
self.previous_key_states = keys_pressed;
}
fn handle_proxy_sensor(&mut self, value: u8) {
let proxy_sensor_is_far = value != 0;
let send_proxy_event = proxy_sensor_is_far != self.proxy_sensor_was_far;
self.proxy_sensor_was_far = proxy_sensor_is_far;
if send_proxy_event {
self.pending_events.push_back(if proxy_sensor_is_far {
GlassesEvent::ProximityFar
} else {
GlassesEvent::ProximityNear
});
}
}
}