autd3_core/firmware/
pulse_width.rs1use crate::firmware::ULTRASOUND_PERIOD_COUNT_BITS;
2
3const PERIOD: u16 = 1 << ULTRASOUND_PERIOD_COUNT_BITS;
4
5#[repr(C)]
6#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
7enum PulseWidthInner {
8 Duty(f32),
9 Raw(u16),
10}
11
12#[repr(C)]
13#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
14pub struct PulseWidth {
16 inner: PulseWidthInner,
17}
18
19#[derive(Debug, PartialEq, Copy, Clone)]
20pub enum PulseWidthError {
22 PulseWidthOutOfRange(u16),
24 DutyRatioOutOfRange(f32),
26}
27
28impl core::error::Error for PulseWidthError {}
29
30impl core::fmt::Display for PulseWidthError {
31 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
32 match self {
33 PulseWidthError::PulseWidthOutOfRange(pulse_width) => {
34 write!(
35 f,
36 "Pulse width ({}) is out of range [0, {})",
37 pulse_width, PERIOD
38 )
39 }
40 PulseWidthError::DutyRatioOutOfRange(duty) => {
41 write!(f, "Duty ratio ({}) is out of range [0, 1)", duty)
42 }
43 }
44 }
45}
46impl PulseWidth {
47 #[must_use]
51 pub const fn new(pulse_width: u16) -> Self {
52 Self {
53 inner: PulseWidthInner::Raw(pulse_width),
54 }
55 }
56
57 pub fn from_duty(duty: f32) -> Self {
59 Self {
60 inner: PulseWidthInner::Duty(duty),
61 }
62 }
63
64 pub fn pulse_width(self) -> Result<u16, PulseWidthError> {
66 let pulse_width = match self.inner {
67 PulseWidthInner::Duty(duty) => {
68 if !(0.0..1.0).contains(&duty) {
69 return Err(PulseWidthError::DutyRatioOutOfRange(duty));
70 }
71 (duty * PERIOD as f32).round() as u16
72 }
73 PulseWidthInner::Raw(raw) => raw,
74 };
75 if pulse_width >= PERIOD {
76 return Err(PulseWidthError::PulseWidthOutOfRange(pulse_width));
77 }
78 Ok(pulse_width)
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[rstest::rstest]
87 #[case(Ok(0), 0)]
88 #[case(Ok(256), 256)]
89 #[case(Ok(511), 511)]
90 #[case(Err(PulseWidthError::PulseWidthOutOfRange(512)), 512)]
91 #[test]
92 fn pulse_width_new(#[case] expected: Result<u16, PulseWidthError>, #[case] pulse_width: u16) {
93 assert_eq!(expected, PulseWidth::new(pulse_width).pulse_width());
94 }
95
96 #[rstest::rstest]
97 #[case(Ok(0), 0.0)]
98 #[case(Ok(256), 0.5)]
99 #[case(Ok(511), 511.0 / 512.0)]
100 #[case(Err(PulseWidthError::DutyRatioOutOfRange(-0.5)), -0.5)]
101 #[case(Err(PulseWidthError::DutyRatioOutOfRange(1.0)), 1.0)]
102 #[case(Err(PulseWidthError::DutyRatioOutOfRange(1.5)), 1.5)]
103 #[test]
104 fn pulse_width_from_duty(#[case] expected: Result<u16, PulseWidthError>, #[case] duty: f32) {
105 assert_eq!(expected, PulseWidth::from_duty(duty).pulse_width());
106 }
107
108 #[rstest::rstest]
109 #[case(
110 "Pulse width (512) is out of range [0, 512)",
111 PulseWidthError::PulseWidthOutOfRange(512)
112 )]
113 #[case(
114 "Duty ratio (1.5) is out of range [0, 1)",
115 PulseWidthError::DutyRatioOutOfRange(1.5)
116 )]
117 fn display(#[case] expected: &str, #[case] error: PulseWidthError) {
118 assert_eq!(expected, format!("{}", error));
119 }
120}