autd3_core/firmware/
phase.rs

1use core::f32::consts::PI;
2
3use crate::{
4    common::{Angle, rad},
5    geometry::Complex,
6};
7
8/// The phase of the ultrasound.
9#[derive(Clone, Copy, PartialEq, Eq, Default)]
10#[repr(C)]
11pub struct Phase(pub u8);
12
13impl core::fmt::Debug for Phase {
14    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15        write!(f, "0x{:02X}", self.0)
16    }
17}
18
19impl Phase {
20    /// A phase of zero.
21    pub const ZERO: Self = Self(0);
22    /// A phase of π.
23    pub const PI: Self = Self(0x80);
24
25    /// Converts the phase into a radian.
26    #[must_use]
27    pub const fn radian(&self) -> f32 {
28        self.0 as f32 / 256.0 * 2.0 * PI
29    }
30}
31
32impl From<Angle> for Phase {
33    fn from(v: Angle) -> Self {
34        let p = v.radian() / (2.0 * PI) * 256.0;
35        let p = p.round();
36        Self(((p as i32) & 0xFF) as _)
37    }
38}
39
40impl From<Complex> for Phase {
41    fn from(v: Complex) -> Self {
42        Self::from(v.arg() * rad)
43    }
44}
45
46impl core::ops::Add<Phase> for Phase {
47    type Output = Phase;
48
49    fn add(self, rhs: Phase) -> Self::Output {
50        Phase(self.0.wrapping_add(rhs.0))
51    }
52}
53
54impl core::ops::Sub<Phase> for Phase {
55    type Output = Phase;
56
57    fn sub(self, rhs: Phase) -> Self::Output {
58        Phase(self.0.wrapping_sub(rhs.0))
59    }
60}
61
62impl core::ops::Mul<u8> for Phase {
63    type Output = Phase;
64
65    fn mul(self, rhs: u8) -> Self::Output {
66        Phase(self.0.wrapping_mul(rhs))
67    }
68}
69
70impl core::ops::Div<u8> for Phase {
71    type Output = Phase;
72
73    fn div(self, rhs: u8) -> Self::Output {
74        Phase(self.0.wrapping_div(rhs))
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[rstest::rstest]
83    #[case(Phase(0x02), Phase(0x01), Phase(0x01))]
84    #[case(Phase(0xFE), Phase(0x7F), Phase(0x7F))]
85    #[case(Phase(0x7E), Phase(0x7F), Phase(0xFF))]
86    fn add(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) {
87        assert_eq!(expected, lhs + rhs);
88    }
89
90    #[rstest::rstest]
91    #[case(Phase::ZERO, Phase(0x01), Phase(0x01))]
92    #[case(Phase(0x01), Phase(0x02), Phase(0x01))]
93    #[case(Phase(0x80), Phase(0x7F), Phase(0xFF))]
94    fn sub(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) {
95        assert_eq!(expected, lhs - rhs);
96    }
97
98    #[rstest::rstest]
99    #[case(Phase(0x02), Phase(0x01), 2)]
100    #[case(Phase(0xFE), Phase(0x7F), 2)]
101    #[case(Phase::ZERO, Phase(0x80), 2)]
102    fn mul(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) {
103        assert_eq!(expected, lhs * rhs);
104    }
105
106    #[rstest::rstest]
107    #[case(Phase(0x01), Phase(0x02), 2)]
108    #[case(Phase(0x7F), Phase(0xFE), 2)]
109    #[case(Phase::ZERO, Phase(0x01), 2)]
110    fn div(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) {
111        assert_eq!(expected, lhs / rhs);
112    }
113
114    #[rstest::rstest]
115    #[case(0.0, 0)]
116    #[case(2.0 * PI / 256.0 * 128.0, 128)]
117    #[case(2.0 * PI / 256.0 * 255.0, 255)]
118    fn radian(#[case] expect: f32, #[case] value: u8) {
119        approx::assert_abs_diff_eq!(expect, Phase(value).radian());
120    }
121
122    #[rstest::rstest]
123    #[case(Phase(0x00), Complex::new(1.0, 0.0))]
124    #[case(Phase(0x40), Complex::new(0.0, 1.0))]
125    #[case(Phase(0x80), Complex::new(-1.0, 0.0))]
126    #[case(Phase(0xC0), Complex::new(0.0, -1.0))]
127    fn from_complex(#[case] expect: Phase, #[case] value: Complex) {
128        assert_eq!(expect, Phase::from(value));
129    }
130
131    #[test]
132    fn dbg() {
133        assert_eq!(format!("{:?}", Phase::ZERO), "0x00");
134        assert_eq!(format!("{:?}", Phase(0x01)), "0x01");
135        assert_eq!(format!("{:?}", Phase(0xFF)), "0xFF");
136    }
137}