autd3_driver/firmware/fpga/
pulse_width.rs1use getset::CopyGetters;
2use num::Zero;
3
4#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, CopyGetters)]
5pub struct PulseWidth<T: Copy, const BITS: usize> {
7 #[getset(get_copy = "pub")]
8 pulse_width: T,
10}
11
12impl<T, const BITS: usize> PulseWidth<T, BITS>
13where
14 T: Copy + TryFrom<usize> + Zero + PartialOrd,
15{
16 pub fn new(pulse_width: T) -> Option<Self> {
18 let period: T = T::try_from(1 << BITS).ok()?;
19 if pulse_width < T::zero() || period <= pulse_width {
20 return None;
21 }
22 Some(Self { pulse_width })
23 }
24
25 #[must_use]
27 pub fn from_duty(duty: f32) -> Option<Self> {
28 if !(0.0..=1.0).contains(&duty) {
29 return None;
30 } else {
31 duty
32 };
33 Self::new(T::try_from(((1 << BITS) as f32 * duty).round() as usize).ok()?)
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 const BITS: usize = 9;
42
43 #[rstest::rstest]
44 #[case(Some(0), 0)]
45 #[case(Some(256), 256)]
46 #[case(Some(511), 511)]
47 #[case(None, 512)]
48 #[test]
49 fn test_pulse_width_new(#[case] expected: Option<u16>, #[case] pulse_width: u16) {
50 let pulse_width = PulseWidth::<u16, BITS>::new(pulse_width);
51 assert_eq!(expected, pulse_width.map(|p| p.pulse_width()));
52 }
53
54 #[rstest::rstest]
55 #[case(Some(0), 0.0)]
56 #[case(Some(256), 0.5)]
57 #[case(Some(511), 511.0 / 512.0)]
58 #[case(None, -0.5)]
59 #[case(None, 1.0)]
60 #[case(None, 1.5)]
61 #[test]
62 fn test_pulse_width_from_duty(#[case] expected: Option<u16>, #[case] duty: f32) {
63 let pulse_width = PulseWidth::<u16, BITS>::from_duty(duty);
64 assert_eq!(expected, pulse_width.map(|p| p.pulse_width()));
65 }
66}