use super::limits::StepLimits;
use super::motor::MotorConfig;
use super::units::{DegreesPerSec, DegreesPerSecSquared};
#[derive(Debug, Clone)]
pub struct MechanicalConstraints {
pub steps_per_revolution: u32,
pub steps_per_degree: f32,
pub max_velocity_steps_per_sec: f32,
pub max_acceleration_steps_per_sec2: f32,
pub min_step_interval_ns: u32,
pub limits: Option<StepLimits>,
pub max_velocity: DegreesPerSec,
pub max_acceleration: DegreesPerSecSquared,
}
impl MechanicalConstraints {
pub fn from_config(config: &MotorConfig) -> Self {
let steps_per_revolution = (config.steps_per_revolution as f32
* config.microsteps.value() as f32
* config.gear_ratio) as u32;
let steps_per_degree = steps_per_revolution as f32 / 360.0;
let max_velocity_steps_per_sec = config.max_velocity.0 * steps_per_degree;
let max_acceleration_steps_per_sec2 = config.max_acceleration.0 * steps_per_degree;
let min_step_interval_ns = if max_velocity_steps_per_sec > 0.0 {
(1_000_000_000.0 / max_velocity_steps_per_sec) as u32
} else {
u32::MAX
};
let limits = config
.limits
.as_ref()
.map(|l| StepLimits::from_soft_limits(l, steps_per_degree));
Self {
steps_per_revolution,
steps_per_degree,
max_velocity_steps_per_sec,
max_acceleration_steps_per_sec2,
min_step_interval_ns,
limits,
max_velocity: config.max_velocity,
max_acceleration: config.max_acceleration,
}
}
#[inline]
pub fn degrees_to_steps(&self, degrees: f32) -> i64 {
(degrees * self.steps_per_degree) as i64
}
#[inline]
pub fn steps_to_degrees(&self, steps: i64) -> f32 {
steps as f32 / self.steps_per_degree
}
#[inline]
pub fn velocity_to_steps(&self, deg_per_sec: f32) -> f32 {
deg_per_sec * self.steps_per_degree
}
#[inline]
pub fn acceleration_to_steps(&self, deg_per_sec2: f32) -> f32 {
deg_per_sec2 * self.steps_per_degree
}
#[inline]
pub fn velocity_to_interval_ns(&self, velocity_steps_per_sec: f32) -> u32 {
if velocity_steps_per_sec > 0.0 {
(1_000_000_000.0 / velocity_steps_per_sec) as u32
} else {
u32::MAX
}
}
pub fn check_limits(&self, steps: i64) -> Option<i64> {
match &self.limits {
Some(limits) => limits.apply(steps),
None => Some(steps), }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::units::Microsteps;
fn make_test_config() -> MotorConfig {
MotorConfig {
name: heapless::String::try_from("test").unwrap(),
steps_per_revolution: 200,
microsteps: Microsteps::SIXTEENTH,
gear_ratio: 1.0,
max_velocity: DegreesPerSec(360.0),
max_acceleration: DegreesPerSecSquared(720.0),
invert_direction: false,
limits: None,
backlash_compensation: None,
}
}
#[test]
fn test_steps_per_revolution() {
let config = make_test_config();
let constraints = MechanicalConstraints::from_config(&config);
assert_eq!(constraints.steps_per_revolution, 3200);
}
#[test]
fn test_steps_per_degree() {
let config = make_test_config();
let constraints = MechanicalConstraints::from_config(&config);
assert!((constraints.steps_per_degree - 8.889).abs() < 0.01);
}
#[test]
fn test_velocity_conversion() {
let config = make_test_config();
let constraints = MechanicalConstraints::from_config(&config);
assert!((constraints.max_velocity_steps_per_sec - 3200.0).abs() < 1.0);
}
}