autd3_driver/firmware/fpga/
pulse_width.rs

1use getset::CopyGetters;
2use num::Zero;
3
4#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, CopyGetters)]
5/// The pulse width.
6pub struct PulseWidth<T: Copy, const BITS: usize> {
7    #[getset(get_copy = "pub")]
8    /// The pulse width in period of 2^`BITS`.
9    pulse_width: T,
10}
11
12impl<T, const BITS: usize> PulseWidth<T, BITS>
13where
14    T: Copy + TryFrom<usize> + Zero + PartialOrd,
15{
16    /// Creates a new [`PulseWidth`].
17    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    /// Creates a new [`PulseWidth`] from duty ratio.
26    #[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}