use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone)]
pub struct IncrementalEncoder<S: ControlScalar> {
counts_per_rev: i32,
count: i32,
prev_count: i32,
velocity: S,
vel_alpha: S,
}
impl<S: ControlScalar> IncrementalEncoder<S> {
pub fn new(counts_per_rev: i32) -> Self {
Self {
counts_per_rev,
count: 0,
prev_count: 0,
velocity: S::ZERO,
vel_alpha: S::from_f64(0.1), }
}
pub fn with_velocity_filter(mut self, alpha: S) -> Self {
self.vel_alpha = alpha;
self
}
pub fn update(&mut self, new_count: i32, dt: S) {
self.count = new_count;
if dt > S::ZERO {
let delta = self.count - self.prev_count;
let cpr = S::from_f64(self.counts_per_rev as f64);
let raw_vel = S::from_f64(delta as f64) / (cpr * dt);
self.velocity = self.vel_alpha * self.velocity + (S::ONE - self.vel_alpha) * raw_vel;
}
self.prev_count = self.count;
}
pub fn pulse(&mut self, direction: i32, dt: S) {
self.update(self.count + direction, dt);
}
pub fn position_rev(&self) -> S {
S::from_f64(self.count as f64) / S::from_f64(self.counts_per_rev as f64)
}
pub fn position_rad(&self) -> S {
self.position_rev() * S::TWO * S::PI
}
pub fn position_deg(&self) -> S {
self.position_rev() * S::from_f64(360.0)
}
pub fn velocity_rev_s(&self) -> S {
self.velocity
}
pub fn velocity_rad_s(&self) -> S {
self.velocity * S::TWO * S::PI
}
pub fn count(&self) -> i32 {
self.count
}
pub fn reset(&mut self) {
self.count = 0;
self.prev_count = 0;
self.velocity = S::ZERO;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn position_zero_at_start() {
let enc = IncrementalEncoder::<f64>::new(1000);
assert_eq!(enc.count(), 0);
assert_eq!(enc.position_rev(), 0.0);
}
#[test]
fn one_revolution() {
let mut enc = IncrementalEncoder::<f64>::new(1000);
enc.update(1000, 0.01);
assert!((enc.position_rev() - 1.0).abs() < 1e-10);
assert!((enc.position_deg() - 360.0).abs() < 1e-10);
}
#[test]
fn velocity_estimation() {
let mut enc = IncrementalEncoder::<f64>::new(1000).with_velocity_filter(0.0);
enc.update(0, 0.001);
enc.update(500, 0.5);
assert!(
(enc.velocity_rev_s() - 1.0).abs() < 0.01,
"vel={}",
enc.velocity_rev_s()
);
}
#[test]
fn pulse_increments() {
let mut enc = IncrementalEncoder::<f64>::new(200);
enc.pulse(1, 0.001);
enc.pulse(1, 0.001);
enc.pulse(1, 0.001);
assert_eq!(enc.count(), 3);
}
#[test]
fn negative_direction() {
let mut enc = IncrementalEncoder::<f64>::new(1000);
enc.update(-500, 0.1);
assert!(enc.position_rev() < 0.0);
assert!(enc.velocity_rev_s() < 0.0);
}
#[test]
fn reset_clears() {
let mut enc = IncrementalEncoder::<f64>::new(1000);
enc.update(5000, 1.0);
enc.reset();
assert_eq!(enc.count(), 0);
assert_eq!(enc.velocity_rev_s(), 0.0);
}
}