ramp_maker/
flat.rs

1//! Flat motion profile
2//!
3//! See [`Flat`].
4
5use fixed::FixedU32;
6
7use crate::MotionProfile;
8
9/// Flat motion profile
10///
11/// This is the simplest possible motion profile, as it produces just a constant
12/// velocity. Please note that this is of limited use, and should probably be
13/// restricted to testing.
14///
15/// Theoretically, this profile produces infinite acceleration/deceleration at
16/// the beginning and end of the movement. In practice, you might get away with
17/// this, if the velocity and the load on the motor are low enough. Otherwise,
18/// this will definitely produce missed steps.
19///
20/// Create an instance of this struct using [`Flat::new`], then use the API
21/// defined by [`MotionProfile`] (which this struct implements) to generate the
22/// acceleration ramp.
23///
24/// # Unit of Time
25///
26/// This code is agnostic on which unit of time is used. If you provide the
27/// target velocity in steps per second, the unit of the delay returned will be
28/// seconds.
29///
30/// This allows you to pass the target velocity in steps per number of timer
31/// counts for the timer you're using, completely eliminating any conversion
32/// overhead for the delay.
33///
34/// # Type Parameter
35///
36/// The type parameter `Num` defines the type that is used to represent the
37/// target velocity and the delay per step. It is set to a 32-bit fixed-point
38/// number type by default.
39///
40/// This default is appropriate for 32-bit microcontrollers, but it might not
41/// be ideal for 8- or 16-bit microcontrollers, or target platforms where
42/// hardware support for floating point numbers is available. You can override
43/// it with other types from the `fixed` crate, or `f32`/`f64`, for example.
44pub struct Flat<Num = DefaultNum> {
45    delay: Option<Num>,
46    num_steps: u32,
47}
48
49impl<Num> Flat<Num> {
50    /// Create a new instance of `Flat`
51    pub fn new() -> Self {
52        Self {
53            delay: None,
54            num_steps: 0,
55        }
56    }
57}
58
59impl Default for Flat<f32> {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl<Num> MotionProfile for Flat<Num>
66where
67    Num: Copy + num_traits::Zero + num_traits::Inv<Output = Num>,
68{
69    type Velocity = Num;
70    type Delay = Num;
71
72    fn enter_position_mode(
73        &mut self,
74        max_velocity: Self::Velocity,
75        num_steps: u32,
76    ) {
77        self.delay = if max_velocity.is_zero() {
78            None
79        } else {
80            Some(max_velocity.inv())
81        };
82
83        self.num_steps = num_steps;
84    }
85
86    fn next_delay(&mut self) -> Option<Self::Delay> {
87        if self.num_steps == 0 {
88            return None;
89        }
90
91        self.num_steps -= 1;
92
93        self.delay
94    }
95}
96
97/// The default numeric type used by [`Flat`]
98pub type DefaultNum = FixedU32<typenum::U16>;
99
100#[cfg(test)]
101mod tests {
102    use crate::{Flat, MotionProfile as _};
103
104    #[test]
105    fn flat_should_pass_motion_profile_tests() {
106        crate::util::testing::test::<Flat<f32>>();
107    }
108
109    #[test]
110    fn flat_should_produce_constant_velocity() {
111        let mut flat = Flat::new();
112
113        let max_velocity = 1000.0;
114        flat.enter_position_mode(max_velocity, 200);
115
116        for velocity in flat.velocities() {
117            assert_eq!(velocity, max_velocity);
118        }
119    }
120}