use core::ops::{Add, Mul, Sub};
use serde::Deserialize;
use crate::error::ConfigError;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deserialize)]
#[serde(transparent)]
pub struct Degrees(pub f32);
impl Degrees {
#[inline]
pub const fn new(value: f32) -> Self {
Self(value)
}
#[inline]
pub const fn value(self) -> f32 {
self.0
}
#[inline]
pub fn to_radians(self) -> f32 {
self.0.to_radians()
}
#[inline]
pub fn from_radians(radians: f32) -> Self {
Self(radians.to_degrees())
}
}
impl Add for Degrees {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Sub for Degrees {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deserialize)]
#[serde(transparent)]
pub struct DegreesPerSec(pub f32);
impl DegreesPerSec {
#[inline]
pub const fn new(value: f32) -> Self {
Self(value)
}
#[inline]
pub const fn value(self) -> f32 {
self.0
}
}
impl Mul<f32> for DegreesPerSec {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self(self.0 * rhs)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deserialize)]
#[serde(transparent)]
pub struct DegreesPerSecSquared(pub f32);
impl DegreesPerSecSquared {
#[inline]
pub const fn new(value: f32) -> Self {
Self(value)
}
#[inline]
pub const fn value(self) -> f32 {
self.0
}
}
impl Mul<f32> for DegreesPerSecSquared {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self(self.0 * rhs)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Steps(pub i64);
impl Steps {
#[inline]
pub const fn new(value: i64) -> Self {
Self(value)
}
#[inline]
pub const fn value(self) -> i64 {
self.0
}
#[inline]
pub fn abs(self) -> u64 {
self.0.unsigned_abs()
}
#[inline]
pub fn to_degrees(self, steps_per_degree: f32) -> Degrees {
Degrees(self.0 as f32 / steps_per_degree)
}
#[inline]
pub fn from_degrees(degrees: Degrees, steps_per_degree: f32) -> Self {
Self((degrees.0 * steps_per_degree) as i64)
}
}
impl Add for Steps {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Sub for Steps {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Microsteps(u16);
impl Microsteps {
pub const FULL: Self = Self(1);
pub const HALF: Self = Self(2);
pub const QUARTER: Self = Self(4);
pub const EIGHTH: Self = Self(8);
pub const SIXTEENTH: Self = Self(16);
pub const THIRTY_SECOND: Self = Self(32);
pub const SIXTY_FOURTH: Self = Self(64);
pub const ONE_TWENTY_EIGHTH: Self = Self(128);
pub const TWO_FIFTY_SIXTH: Self = Self(256);
const VALID_VALUES: [u16; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256];
pub fn new(value: u16) -> Result<Self, ConfigError> {
if Self::VALID_VALUES.contains(&value) {
Ok(Self(value))
} else {
Err(ConfigError::InvalidMicrosteps(value))
}
}
#[inline]
pub const fn value(self) -> u16 {
self.0
}
#[inline]
pub fn is_valid(value: u16) -> bool {
Self::VALID_VALUES.contains(&value)
}
}
impl Default for Microsteps {
fn default() -> Self {
Self::FULL
}
}
impl TryFrom<u16> for Microsteps {
type Error = ConfigError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl<'de> Deserialize<'de> for Microsteps {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use core::fmt::Write;
let value = u16::deserialize(deserializer)?;
Microsteps::new(value).map_err(|e| {
let mut buf = heapless::String::<128>::new();
let _ = write!(buf, "{}", e);
serde::de::Error::custom(buf.as_str())
})
}
}
pub trait UnitExt {
fn degrees(self) -> Degrees;
fn degrees_per_sec(self) -> DegreesPerSec;
fn degrees_per_sec_squared(self) -> DegreesPerSecSquared;
}
impl UnitExt for f32 {
#[inline]
fn degrees(self) -> Degrees {
Degrees(self)
}
#[inline]
fn degrees_per_sec(self) -> DegreesPerSec {
DegreesPerSec(self)
}
#[inline]
fn degrees_per_sec_squared(self) -> DegreesPerSecSquared {
DegreesPerSecSquared(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_microsteps_valid_values() {
for &v in &Microsteps::VALID_VALUES {
assert!(Microsteps::new(v).is_ok());
}
}
#[test]
fn test_microsteps_invalid_values() {
assert!(Microsteps::new(0).is_err());
assert!(Microsteps::new(3).is_err());
assert!(Microsteps::new(17).is_err());
assert!(Microsteps::new(512).is_err());
}
#[test]
fn test_degrees_conversion() {
let d = Degrees::new(180.0);
assert!((d.to_radians() - core::f32::consts::PI).abs() < 0.0001);
}
#[test]
fn test_steps_to_degrees() {
let steps = Steps::new(3200);
let steps_per_degree = 3200.0 / 360.0;
let degrees = steps.to_degrees(steps_per_degree);
assert!((degrees.value() - 360.0).abs() < 0.01);
}
}