use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct MicroStepDriver<S: ControlScalar> {
full_steps_per_rev: u32,
micro_per_full: u32,
peak_current: S,
electrical_angle: S,
micro_count: i64,
}
impl<S: ControlScalar> MicroStepDriver<S> {
pub fn new(full_steps_per_rev: u32, micro_per_full: u32, peak_current: S) -> Self {
Self {
full_steps_per_rev,
micro_per_full,
peak_current,
electrical_angle: S::ZERO,
micro_count: 0,
}
}
pub fn step(&mut self, micro_steps: i32) {
self.micro_count += i64::from(micro_steps);
let total_micro_per_rev =
S::from_f64((self.full_steps_per_rev * self.micro_per_full) as f64);
let delta = S::TWO * S::PI / total_micro_per_rev;
self.electrical_angle += delta * S::from_f64(f64::from(micro_steps));
}
pub fn coil_currents(&self) -> (S, S) {
let ia = self.peak_current * self.electrical_angle.cos();
let ib = self.peak_current * self.electrical_angle.sin();
(ia, ib)
}
pub fn position_microsteps(&self) -> i64 {
self.micro_count
}
pub fn position_full_steps(&self) -> i64 {
let m = self.micro_per_full as i64;
if m == 0 {
return 0;
}
self.micro_count / m
}
pub fn electrical_angle(&self) -> S {
self.electrical_angle
}
pub fn position_rad(&self) -> S {
let total = S::from_f64((self.full_steps_per_rev * self.micro_per_full) as f64);
S::TWO * S::PI * S::from_f64(self.micro_count as f64) / total
}
pub fn reset(&mut self) {
self.micro_count = 0;
self.electrical_angle = S::ZERO;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initial_currents_at_zero_angle() {
let d = MicroStepDriver::<f64>::new(200, 16, 1.0);
let (ia, ib) = d.coil_currents();
assert!((ia - 1.0).abs() < 1e-10); assert!(ib.abs() < 1e-10); }
#[test]
fn currents_bounded_by_peak() {
let mut d = MicroStepDriver::<f64>::new(200, 16, 0.8);
for step in 0..1000 {
d.step(if step % 2 == 0 { 1 } else { -1 });
let (ia, ib) = d.coil_currents();
assert!(ia.abs() <= 0.8 + 1e-10);
assert!(ib.abs() <= 0.8 + 1e-10);
}
}
#[test]
fn position_tracking() {
let mut d = MicroStepDriver::<f64>::new(200, 16, 1.0);
d.step(32); assert_eq!(d.position_microsteps(), 32);
assert_eq!(d.position_full_steps(), 2);
}
#[test]
fn reset_zeroes_state() {
let mut d = MicroStepDriver::<f64>::new(200, 16, 1.0);
d.step(50);
d.reset();
assert_eq!(d.position_microsteps(), 0);
let (ia, ib) = d.coil_currents();
assert!((ia - 1.0).abs() < 1e-10);
assert!(ib.abs() < 1e-10);
}
#[test]
fn one_full_step_quarter_cycle() {
let mut d = MicroStepDriver::<f64>::new(200, 4, 1.0);
d.step(4);
let (ia, ib) = d.coil_currents();
let theta = d.electrical_angle();
let expected_theta = 2.0 * core::f64::consts::PI / 200.0; assert!((theta - expected_theta).abs() < 1e-10);
assert!((ia - theta.cos()).abs() < 1e-10);
assert!((ib - theta.sin()).abs() < 1e-10);
}
}