stepper_motion/motor/
position.rs

1//! Position tracking for stepper motors.
2//!
3//! Provides absolute position tracking in steps with unit conversions.
4
5use crate::config::units::{Degrees, Steps};
6
7/// Motor position tracker.
8///
9/// Maintains absolute position in steps and provides unit conversions.
10#[derive(Debug, Clone, Copy, Default)]
11pub struct Position {
12    /// Current position in steps (from origin)
13    steps: Steps,
14    /// Steps per degree for conversions
15    steps_per_degree: f32,
16}
17
18impl Position {
19    /// Create a new position tracker.
20    #[inline]
21    pub fn new(steps_per_degree: f32) -> Self {
22        Self {
23            steps: Steps::default(),
24            steps_per_degree,
25        }
26    }
27
28    /// Create a position tracker at a specific position.
29    #[inline]
30    pub fn at(steps: Steps, steps_per_degree: f32) -> Self {
31        Self {
32            steps,
33            steps_per_degree,
34        }
35    }
36
37    /// Get current position in steps.
38    #[inline]
39    pub fn steps(&self) -> Steps {
40        self.steps
41    }
42
43    /// Get current position in degrees.
44    #[inline]
45    pub fn degrees(&self) -> Degrees {
46        self.steps.to_degrees(self.steps_per_degree)
47    }
48
49    /// Set position in steps.
50    #[inline]
51    pub fn set_steps(&mut self, steps: Steps) {
52        self.steps = steps;
53    }
54
55    /// Set position in degrees.
56    #[inline]
57    pub fn set_degrees(&mut self, degrees: Degrees) {
58        self.steps = Steps::from_degrees(degrees, self.steps_per_degree);
59    }
60
61    /// Move by a number of steps.
62    #[inline]
63    pub fn move_steps(&mut self, delta: i64) {
64        self.steps = Steps(self.steps.0 + delta);
65    }
66
67    /// Move by an amount in degrees.
68    #[inline]
69    pub fn move_degrees(&mut self, delta: Degrees) {
70        let delta_steps = (delta.0 * self.steps_per_degree) as i64;
71        self.move_steps(delta_steps);
72    }
73
74    /// Reset position to origin (0 steps).
75    #[inline]
76    pub fn reset(&mut self) {
77        self.steps = Steps::default();
78    }
79
80    /// Set current position as the new origin.
81    #[inline]
82    pub fn set_origin(&mut self) {
83        self.steps = Steps::default();
84    }
85
86    /// Get steps per degree conversion factor.
87    #[inline]
88    pub fn steps_per_degree(&self) -> f32 {
89        self.steps_per_degree
90    }
91
92    /// Calculate steps needed to reach a target position in degrees.
93    #[inline]
94    pub fn steps_to(&self, target: Degrees) -> i64 {
95        let target_steps = Steps::from_degrees(target, self.steps_per_degree);
96        target_steps.0 - self.steps.0
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_position_tracking() {
106        // 200 steps/rev * 16 microsteps = 3200 steps/rev
107        // 3200 / 360 = 8.889 steps/degree
108        let steps_per_degree = 3200.0 / 360.0;
109        let mut pos = Position::new(steps_per_degree);
110
111        assert_eq!(pos.steps().value(), 0);
112
113        pos.move_degrees(Degrees(90.0));
114        assert!((pos.degrees().value() - 90.0).abs() < 0.1);
115
116        pos.move_degrees(Degrees(90.0));
117        assert!((pos.degrees().value() - 180.0).abs() < 0.1);
118
119        pos.move_degrees(Degrees(-180.0));
120        assert!(pos.degrees().value().abs() < 0.1);
121    }
122
123    #[test]
124    fn test_steps_to_target() {
125        let steps_per_degree = 10.0;
126        let pos = Position::at(Steps(900), steps_per_degree);
127
128        let steps = pos.steps_to(Degrees(180.0));
129        assert_eq!(steps, 900); // 1800 - 900 = 900
130    }
131}