use std::time::{Duration, Instant};
use byteorder::{LittleEndian, ReadBytesExt};
use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
use rusb::{request_type, DeviceHandle, GlobalContext};
use tinyjson::JsonValue;
use crate::{
util::get_interface_for_endpoint, ARGlasses, DisplayMode, Error, GlassesEvent, Result, Side,
};
pub struct GrawoowG530 {
mcu_handle: DeviceHandle<GlobalContext>,
ov580_handle: DeviceHandle<GlobalContext>,
config_json: JsonValue,
gyro_bias: Vector3<f32>,
accelerometer_bias: Vector3<f32>,
start: Instant,
}
const OV580_ENDPOINT: u8 = 0x89;
const OV580_TIMEOUT: Duration = Duration::from_millis(250);
const MCU_TIMEOUT: Duration = Duration::from_millis(1000);
impl ARGlasses for GrawoowG530 {
fn serial(&mut self) -> Result<String> {
Ok(String::from_utf8(self.command(0x8005, &[])?).map_err(|_| "Invalid serial string")?)
}
fn read_event(&mut self) -> Result<GlassesEvent> {
let mut packet_data = [0u8; 0x80];
self.ov580_handle
.read_interrupt(OV580_ENDPOINT, &mut packet_data, OV580_TIMEOUT)?;
self.parse_imu_packet(&packet_data)
}
fn get_display_mode(&mut self) -> Result<DisplayMode> {
let result = self.command(0x8007, &[])?;
if result.first() == Some(&1) {
Ok(DisplayMode::Stereo)
} else {
Ok(DisplayMode::SameOnBoth)
}
}
fn set_display_mode(&mut self, display_mode: DisplayMode) -> Result<()> {
let display_mode = match display_mode {
DisplayMode::SameOnBoth => 0,
DisplayMode::Stereo => 1,
_ => return Err(Error::Other("Display mode not supported")),
};
self.command(0x8008, &[display_mode])?;
Ok(())
}
fn display_fov(&self) -> f32 {
22f32.to_radians()
}
fn imu_to_display_matrix(&self, side: Side, ipd: f32) -> Isometry3<f64> {
let side_multiplier = match side {
Side::Left => -0.5,
Side::Right => 0.5,
};
Translation3::new(ipd as f64 * side_multiplier, 0.0, 0.0)
* UnitQuaternion::from_euler_angles(
Self::DISPLAY_TILT,
Self::DISPLAY_DIVERGENCE * side_multiplier,
0.0,
)
}
fn name(&self) -> &'static str {
"Grawoow G530"
}
fn display_delay(&self) -> u64 {
15000
}
}
impl GrawoowG530 {
pub const MCU_VID: u16 = 0x1ff7;
pub const MCU_PID: u16 = 0x0ff4;
pub const OV580_VID: u16 = 0x05a9;
pub const OV580_PID: u16 = 0x0f87;
const DISPLAY_TILT: f64 = -0.11;
const DISPLAY_DIVERGENCE: f64 = 0.02;
#[cfg(target_os = "android")]
pub fn new(mcu_fd: isize, ov580_fd: isize) -> Result<Self> {
use rusb::UsbContext;
unsafe { rusb::ffi::libusb_set_option(std::ptr::null_mut(), 2) };
Self::new_common(
unsafe { GlobalContext::default().open_device_with_fd(mcu_fd as i32) }?,
unsafe { GlobalContext::default().open_device_with_fd(ov580_fd as i32) }?,
)
}
#[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::MCU_VID, Self::MCU_PID)?.open()?,
get_device_vid_pid(Self::OV580_VID, Self::OV580_PID)?.open()?,
)
}
fn new_common(
mut mcu_handle: DeviceHandle<GlobalContext>,
mut ov580_handle: DeviceHandle<GlobalContext>,
) -> Result<Self> {
mcu_handle.set_auto_detach_kernel_driver(true)?;
ov580_handle.set_auto_detach_kernel_driver(true)?;
mcu_handle.claim_interface(0)?;
ov580_handle.claim_interface(
get_interface_for_endpoint(&ov580_handle.device(), OV580_ENDPOINT).ok_or_else(
|| Error::Other("Could not find endpoint, wrong USB structure (probably)"),
)?,
)?;
let mut result = Self {
mcu_handle,
ov580_handle,
config_json: tinyjson::JsonValue::Null,
gyro_bias: Default::default(),
accelerometer_bias: Default::default(),
start: Instant::now(),
};
result.read_calibration()?;
Ok(result)
}
fn read_calibration(&mut self) -> Result<()> {
let mut calib_string = Vec::new();
for _ in 0..100 {
let part = self.command(
0x800a,
&[
0,
0,
0,
(calib_string.len() >> 8) as u8,
calib_string.len() as u8,
],
)?;
if part.len() <= 6 {
break;
}
calib_string.extend_from_slice(&part[6..]);
}
self.config_json = String::from_utf8(calib_string)
.map_err(|_| Error::Other("Invalid glasses config format (no start token)"))?
.parse()
.map_err(|_| Error::Other("Invalid glasses config format (JSON parse error)"))?;
let json = &self.config_json["imu"][0]["RM_acc"];
self.accelerometer_bias = Vector3::new(
*json[9].get::<f64>().unwrap() as f32,
*json[10].get::<f64>().unwrap() as f32,
*json[11].get::<f64>().unwrap() as f32,
);
let json = &self.config_json["imu"][0]["RM_gyro"];
self.gyro_bias = Vector3::new(
*json[9].get::<f64>().unwrap() as f32,
*json[10].get::<f64>().unwrap() as f32,
*json[11].get::<f64>().unwrap() as f32,
);
Ok(())
}
fn command(&self, cmd_id: u16, additional_data: &[u8]) -> Result<Vec<u8>> {
self.send_command_request(cmd_id, additional_data)?;
self.recv_command_result(cmd_id)
}
fn send_command_request(&self, cmd_id: u16, additional_data: &[u8]) -> Result<()> {
let mut control_data = vec![
0xaa,
0xbb,
(cmd_id >> 8) as u8,
cmd_id as u8,
0,
additional_data.len() as u8,
];
control_data.extend_from_slice(additional_data);
let checksum: u32 = control_data[2..].iter().map(|x| *x as u32).sum();
control_data.push(checksum as u8);
let control_data = &control_data[..6 + additional_data.len() + 1];
self.mcu_handle.write_control(
request_type(
rusb::Direction::Out,
rusb::RequestType::Class,
rusb::Recipient::Interface,
),
9,
0x201,
0,
control_data,
MCU_TIMEOUT,
)?;
Ok(())
}
fn recv_command_result(&self, cmd_id: u16) -> Result<Vec<u8>> {
let mut result = [0; 0x100];
self.mcu_handle.read_control(
request_type(
rusb::Direction::In,
rusb::RequestType::Class,
rusb::Recipient::Interface,
),
1,
0x102,
0,
&mut result,
MCU_TIMEOUT,
)?;
if result[0] != 0xaa
|| result[1] != 0xbb
|| result[2] != (cmd_id >> 8) as u8
|| result[3] != (cmd_id & 0xff) as u8
|| result[4] != 0
{
return Err(Error::Other("Protocol error"));
}
let len = result[5] as usize;
Ok(result[6..(6 + len)].into())
}
fn parse_imu_packet(&self, data: &[u8]) -> Result<GlassesEvent> {
const GYRO_MUL: f32 = std::f32::consts::PI / 180.0 / 16.4;
const ACC_MUL: f32 = 9.81 / 16384.0;
let mut reader = std::io::Cursor::new(&data);
reader.set_position(0x3c);
let gyro_x = reader.read_i32::<LittleEndian>()? as f32;
let gyro_y = reader.read_i32::<LittleEndian>()? as f32;
let gyro_z = reader.read_i32::<LittleEndian>()? as f32;
let gyroscope = Vector3::new(
-(gyro_y * GYRO_MUL - self.gyro_bias.y),
-(gyro_z * GYRO_MUL - self.gyro_bias.z),
gyro_x * GYRO_MUL - self.gyro_bias.x,
);
reader.set_position(0x58);
let acc_x = reader.read_i32::<LittleEndian>()? as f32;
let acc_y = reader.read_i32::<LittleEndian>()? as f32;
let acc_z = reader.read_i32::<LittleEndian>()? as f32;
let accelerometer = Vector3::new(
-(acc_y * ACC_MUL - self.accelerometer_bias.y),
-(acc_z * ACC_MUL - self.accelerometer_bias.z),
acc_x * ACC_MUL - self.accelerometer_bias.x,
);
Ok(GlassesEvent::AccGyro {
accelerometer,
gyroscope,
timestamp: self.start.elapsed().as_micros() as u64,
})
}
}