use core::fmt;
use alloc::format;
use uom::si::{
acceleration::meter_per_second_squared,
angle::degree,
angular_velocity::degree_per_second,
f64::{Acceleration, Angle, AngularVelocity},
};
use crate::{
bindings,
error::{get_errno, Error},
rtos::DataSource,
};
pub struct InertialSensor {
port: u8,
}
impl InertialSensor {
pub unsafe fn new(port: u8) -> InertialSensor {
InertialSensor { port }
}
pub fn calibrate(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_reset(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn get_rotation(&self) -> Result<Angle, InertialSensorError> {
match unsafe { bindings::imu_get_rotation(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(Angle::new::<degree>(x)),
}
}
pub fn get_heading(&self) -> Result<Angle, InertialSensorError> {
match unsafe { bindings::imu_get_heading(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(Angle::new::<degree>(x)),
}
}
pub fn get_quaternion(&self) -> Result<InertialSensorQuaternion, InertialSensorError> {
match unsafe { bindings::imu_get_quaternion(self.port) } {
x if x.x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(InertialSensorQuaternion {
x: x.x,
y: x.y,
z: x.z,
w: x.w,
}),
}
}
pub fn get_euler(&self) -> Result<InertialSensorEuler, InertialSensorError> {
match unsafe { bindings::imu_get_euler(self.port) } {
x if x.pitch == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(InertialSensorEuler {
pitch: Angle::new::<degree>(x.pitch),
roll: Angle::new::<degree>(x.roll),
yaw: Angle::new::<degree>(x.yaw),
}),
}
}
pub fn get_pitch(&self) -> Result<Angle, InertialSensorError> {
match unsafe { bindings::imu_get_pitch(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(Angle::new::<degree>(x)),
}
}
pub fn get_roll(&self) -> Result<Angle, InertialSensorError> {
match unsafe { bindings::imu_get_roll(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(Angle::new::<degree>(x)),
}
}
pub fn get_yaw(&self) -> Result<Angle, InertialSensorError> {
match unsafe { bindings::imu_get_yaw(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(Angle::new::<degree>(x)),
}
}
pub fn get_gyro_rate(&self) -> Result<InertialSensorRawRate, InertialSensorError> {
match unsafe { bindings::imu_get_gyro_rate(self.port) } {
x if x.x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(InertialSensorRawRate {
x: AngularVelocity::new::<degree_per_second>(x.x),
y: AngularVelocity::new::<degree_per_second>(x.y),
z: AngularVelocity::new::<degree_per_second>(x.z),
}),
}
}
pub fn get_accel(&self) -> Result<InertialSensorRawAccel, InertialSensorError> {
match unsafe { bindings::imu_get_accel(self.port) } {
x if x.x == bindings::PROS_ERR_F_ => Err(InertialSensorError::from_errno()),
x => Ok(InertialSensorRawAccel {
x: Acceleration::new::<meter_per_second_squared>(x.x),
y: Acceleration::new::<meter_per_second_squared>(x.y),
z: Acceleration::new::<meter_per_second_squared>(x.z),
}),
}
}
pub fn get_status(&self) -> Result<InertialSensorStatus, InertialSensorError> {
let status = unsafe { bindings::imu_get_status(self.port) };
if status == bindings::imu_status_e_E_IMU_STATUS_ERROR {
Err(InertialSensorError::from_errno())
} else {
Ok(InertialSensorStatus(status))
}
}
pub fn reset_heading(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare_heading(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn reset_rotation(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare_rotation(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn reset_pitch(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare_pitch(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn reset_roll(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare_roll(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn reset_yaw(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare_yaw(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn reset_euler(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare_euler(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn reset(&mut self) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_tare(self.port) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_zero_euler(
&mut self,
euler: InertialSensorEuler,
) -> Result<(), InertialSensorError> {
match unsafe {
bindings::imu_set_euler(
self.port,
bindings::euler_s_t {
pitch: euler.pitch.get::<degree>(),
roll: euler.roll.get::<degree>(),
yaw: euler.yaw.get::<degree>(),
},
)
} {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_rotation(&mut self, rotation: Angle) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_set_rotation(self.port, rotation.get::<degree>()) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_heading(&mut self, heading: Angle) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_set_heading(self.port, heading.get::<degree>()) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_pitch(&mut self, pitch: Angle) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_set_pitch(self.port, pitch.get::<degree>()) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_roll(&mut self, roll: Angle) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_set_roll(self.port, roll.get::<degree>()) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_yaw(&mut self, yaw: Angle) -> Result<(), InertialSensorError> {
match unsafe { bindings::imu_set_yaw(self.port, yaw.get::<degree>()) } {
bindings::PROS_ERR_ => Err(InertialSensorError::from_errno()),
_ => Ok(()),
}
}
}
impl DataSource for InertialSensor {
type Data = InertialSensorData;
type Error = InertialSensorError;
fn read(&self) -> Result<Self::Data, Self::Error> {
Ok(InertialSensorData {
status: self.get_status()?,
quaternion: self.get_quaternion()?,
euler: self.get_euler()?,
gyro_rate: self.get_gyro_rate()?,
accel: self.get_accel()?,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InertialSensorData {
pub status: InertialSensorStatus,
pub quaternion: InertialSensorQuaternion,
pub euler: InertialSensorEuler,
pub gyro_rate: InertialSensorRawRate,
pub accel: InertialSensorRawAccel,
}
#[derive(Debug)]
pub enum InertialSensorError {
PortOutOfRange,
PortNotInertialSensor,
SensorAlreadyCalibrating,
UnknownStatusCode(u32),
Unknown(i32),
}
impl InertialSensorError {
fn from_errno() -> Self {
match get_errno() {
libc::ENXIO => Self::PortOutOfRange,
libc::ENODEV => Self::PortNotInertialSensor,
libc::EAGAIN => Self::SensorAlreadyCalibrating,
x => Self::Unknown(x),
}
}
}
impl From<InertialSensorError> for Error {
fn from(err: InertialSensorError) -> Self {
match err {
InertialSensorError::PortOutOfRange => Error::Custom("port out of range".into()),
InertialSensorError::PortNotInertialSensor => {
Error::Custom("port not a inertial sensor".into())
}
InertialSensorError::SensorAlreadyCalibrating => {
Error::Custom("sensor already calibrating".into())
}
InertialSensorError::UnknownStatusCode(n) => {
Error::Custom(format!("sensor returned unknown status code {}", n))
}
InertialSensorError::Unknown(n) => Error::System(n),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InertialSensorRawRate {
pub x: AngularVelocity,
pub y: AngularVelocity,
pub z: AngularVelocity,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct InertialSensorRawAccel {
pub x: Acceleration,
pub y: Acceleration,
pub z: Acceleration,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InertialSensorQuaternion {
pub x: f64,
pub y: f64,
pub z: f64,
pub w: f64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InertialSensorEuler {
pub pitch: Angle,
pub roll: Angle,
pub yaw: Angle,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct InertialSensorStatus(bindings::imu_status_e);
impl InertialSensorStatus {
#[inline]
pub fn into_raw(self) -> bindings::imu_status_e {
self.0
}
#[inline]
pub fn is_calibrating(self) -> bool {
self.0 & bindings::imu_status_e_E_IMU_STATUS_CALIBRATING != 0
}
}
impl fmt::Debug for InertialSensorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InertialSensorStatus")
.field("is_calibrating", &self.is_calibrating())
.finish()
}
}