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}