1use crate::motor::MotorKind;
2use ph_curves::{MonotonicCurveLut256, Rounding};
3
4#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6pub enum LoopMode {
7 Once,
9 Forever,
11 Count(u32),
13}
14
15#[derive(Copy, Clone, Debug)]
17pub struct Ramp<C = MonotonicCurveLut256> {
18 pub duration_ms: u32,
20 pub from: u16,
22 pub to: u16,
24 pub curve: C,
26 pub step: u16,
28 pub rounding: Rounding,
30 pub min_dt_ms: u32,
32 pub lra_frequency_hz: Option<u16>,
34 pub lra_frequency_hz_to: Option<u16>,
39}
40
41impl<C> Ramp<C> {
42 pub const fn new(duration_ms: u32, from: u16, to: u16, curve: C) -> Self {
47 Self {
48 duration_ms: normalize_duration(duration_ms),
49 from,
50 to,
51 curve,
52 step: 1,
53 rounding: Rounding::Nearest,
54 min_dt_ms: 0,
55 lra_frequency_hz: None,
56 lra_frequency_hz_to: None,
57 }
58 }
59
60 pub const fn with_quantization(mut self, step: u16, rounding: Rounding) -> Self {
64 self.step = normalize_step(step);
65 self.rounding = rounding;
66 self
67 }
68
69 pub const fn with_min_dt(mut self, min_dt_ms: u32) -> Self {
71 self.min_dt_ms = min_dt_ms;
72 self
73 }
74
75 pub const fn with_lra_frequency(mut self, frequency_hz: u16) -> Self {
77 self.lra_frequency_hz = Some(frequency_hz);
78 self
79 }
80}
81
82#[derive(Copy, Clone, Debug)]
84pub enum Instruction<C = MonotonicCurveLut256> {
85 Ramp(Ramp<C>),
87 Hold {
89 duration_ms: u32,
91 level: u16,
93 lra_frequency_hz: Option<u16>,
95 },
96 Pause {
98 duration_ms: u32,
100 },
101}
102
103impl<C> Instruction<C> {
104 pub const fn ramp(duration_ms: u32, from: u16, to: u16, curve: C) -> Self {
106 Self::Ramp(Ramp::new(duration_ms, from, to, curve))
107 }
108
109 pub const fn hold(duration_ms: u32, level: u16) -> Self {
111 Self::Hold {
112 duration_ms: normalize_duration(duration_ms),
113 level,
114 lra_frequency_hz: None,
115 }
116 }
117
118 pub const fn hold_with_lra_frequency(duration_ms: u32, level: u16, frequency_hz: u16) -> Self {
122 Self::Hold {
123 duration_ms: normalize_duration(duration_ms),
124 level,
125 lra_frequency_hz: Some(frequency_hz),
126 }
127 }
128
129 pub const fn pause(duration_ms: u32) -> Self {
131 Self::Pause {
132 duration_ms: normalize_duration(duration_ms),
133 }
134 }
135
136 pub const fn duration_ms(&self) -> u32 {
138 match self {
139 Self::Ramp(ramp) => ramp.duration_ms,
140 Self::Hold { duration_ms, .. } | Self::Pause { duration_ms } => *duration_ms,
141 }
142 }
143}
144
145#[derive(Copy, Clone, Debug)]
147pub struct Program<'a, C = MonotonicCurveLut256> {
148 motor: MotorKind,
149 instructions: &'a [Instruction<C>],
150 loop_mode: LoopMode,
151}
152
153impl<'a, C> Program<'a, C> {
154 pub const fn new(motor: MotorKind, instructions: &'a [Instruction<C>]) -> Self {
156 Self {
157 motor,
158 instructions,
159 loop_mode: LoopMode::Once,
160 }
161 }
162
163 pub const fn with_loop_mode(mut self, loop_mode: LoopMode) -> Self {
165 self.loop_mode = loop_mode;
166 self
167 }
168
169 pub const fn repeat_forever(self) -> Self {
171 self.with_loop_mode(LoopMode::Forever)
172 }
173
174 pub const fn motor(&self) -> MotorKind {
176 self.motor
177 }
178
179 pub const fn loop_mode(&self) -> LoopMode {
181 self.loop_mode
182 }
183
184 pub const fn instructions(&self) -> &'a [Instruction<C>] {
186 self.instructions
187 }
188
189 pub const fn is_empty(&self) -> bool {
191 self.instructions.is_empty()
192 }
193
194 pub fn total_duration_ms(&self) -> u32 {
196 let mut total = 0u32;
197 let mut index = 0usize;
198
199 while index < self.instructions.len() {
200 total = total.saturating_add(self.instructions[index].duration_ms());
201 index += 1;
202 }
203
204 total
205 }
206}
207
208pub(crate) const fn normalize_step(step: u16) -> u16 {
209 if step == 0 { 1 } else { step }
210}
211
212const fn normalize_duration(duration_ms: u32) -> u32 {
213 if duration_ms == 0 { 1 } else { duration_ms }
214}