use cu29::units::si::angle::{degree, radian};
use cu29::units::si::f32::Angle;
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Units {
#[default]
Raw,
Deg,
Rad,
Normalize,
}
impl FromStr for Units {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"raw" => Ok(Self::Raw),
"deg" => Ok(Self::Deg),
"rad" => Ok(Self::Rad),
"normalize" | "norm" => Ok(Self::Normalize),
_ => Err(()),
}
}
}
pub const DEFAULT_TICKS_PER_REV: u32 = 4096;
impl Units {
#[inline]
pub fn from_raw(self, raw: u16, center: f32, param: f32) -> f32 {
match self {
Self::Raw => raw as f32,
Self::Deg => {
let deg = (raw as f32 - center) * 360.0 / param;
Angle::new::<degree>(deg).get::<degree>()
}
Self::Rad => {
let rad = (raw as f32 - center) * core::f32::consts::TAU / param;
Angle::new::<radian>(rad).get::<radian>()
}
Self::Normalize => {
if param <= 0.0 {
0.0
} else {
((raw as f32 - center) / param).clamp(-1.0, 1.0)
}
}
}
}
#[inline]
pub fn to_raw(self, value: f32, center: f32, param: f32) -> u16 {
let raw = match self {
Self::Raw => value,
Self::Deg => {
let deg = Angle::new::<degree>(value).get::<degree>();
deg * param / 360.0 + center
}
Self::Rad => {
let rad = Angle::new::<radian>(value).get::<radian>();
rad * param / core::f32::consts::TAU + center
}
Self::Normalize => center + value.clamp(-1.0, 1.0) * param,
};
raw.round().clamp(0.0, 65535.0) as u16
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServoCalibration {
pub id: u8,
pub min: u16,
pub max: u16,
}
impl ServoCalibration {
pub fn center(&self) -> f32 {
(self.min as f32 + self.max as f32) / 2.0
}
pub fn range(&self) -> u16 {
self.max.saturating_sub(self.min)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CalibrationData {
pub servos: Vec<ServoCalibration>,
}
impl CalibrationData {
pub fn load(path: &Path) -> std::io::Result<Self> {
let contents = std::fs::read_to_string(path)?;
serde_json::from_str(&contents)
.map_err(|e| std::io::Error::other(format!("bad calibration JSON: {e}")))
}
pub fn save(&self, path: &Path) -> std::io::Result<()> {
let json = serde_json::to_string_pretty(self)?;
std::fs::write(path, json)
}
pub fn center_for(&self, id: u8) -> Option<f32> {
self.servos.iter().find(|s| s.id == id).map(|s| s.center())
}
pub fn half_range_for(&self, id: u8) -> Option<f32> {
self.servos
.iter()
.find(|s| s.id == id)
.map(|s| (s.max as f32 - s.min as f32) / 2.0)
}
}