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::AddAssign for Phase {
55    fn add_assign(&mut self, rhs: Phase) {
56        self.0 = self.0.wrapping_add(rhs.0);
57    }
58}
59
60impl core::ops::Sub<Phase> for Phase {
61    type Output = Phase;
62
63    fn sub(self, rhs: Phase) -> Self::Output {
64        Phase(self.0.wrapping_sub(rhs.0))
65    }
66}
67
68impl core::ops::SubAssign for Phase {
69    fn sub_assign(&mut self, rhs: Phase) {
70        self.0 = self.0.wrapping_sub(rhs.0);
71    }
72}
73
74impl core::ops::Mul<u8> for Phase {
75    type Output = Phase;
76
77    fn mul(self, rhs: u8) -> Self::Output {
78        Phase(self.0.wrapping_mul(rhs))
79    }
80}
81
82impl core::ops::Mul<Phase> for u8 {
83    type Output = Phase;
84
85    fn mul(self, rhs: Phase) -> Self::Output {
86        Phase(self.wrapping_mul(rhs.0))
87    }
88}
89
90impl core::ops::Div<u8> for Phase {
91    type Output = Phase;
92
93    fn div(self, rhs: u8) -> Self::Output {
94        Phase(self.0.wrapping_div(rhs))
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[rstest::rstest]
103    #[case(Phase(0x02), Phase(0x01), Phase(0x01))]
104    #[case(Phase(0xFE), Phase(0x7F), Phase(0x7F))]
105    #[case(Phase(0x7E), Phase(0x7F), Phase(0xFF))]
106    fn add(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) {
107        assert_eq!(expected, lhs + rhs);
108    }
109
110    #[rstest::rstest]
111    #[case(Phase(0x02), Phase(0x01), Phase(0x01))]
112    #[case(Phase(0xFE), Phase(0x7F), Phase(0x7F))]
113    #[case(Phase(0x7E), Phase(0x7F), Phase(0xFF))]
114    fn add_assign(#[case] expected: Phase, #[case] mut lhs: Phase, #[case] rhs: Phase) {
115        lhs += rhs;
116        assert_eq!(expected, lhs);
117    }
118
119    #[rstest::rstest]
120    #[case(Phase::ZERO, Phase(0x01), Phase(0x01))]
121    #[case(Phase(0x01), Phase(0x02), Phase(0x01))]
122    #[case(Phase(0x80), Phase(0x7F), Phase(0xFF))]
123    fn sub(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) {
124        assert_eq!(expected, lhs - rhs);
125    }
126
127    #[rstest::rstest]
128    #[case(Phase::ZERO, Phase(0x01), Phase(0x01))]
129    #[case(Phase(0x01), Phase(0x02), Phase(0x01))]
130    #[case(Phase(0x80), Phase(0x7F), Phase(0xFF))]
131    fn sub_assign(#[case] expected: Phase, #[case] mut lhs: Phase, #[case] rhs: Phase) {
132        lhs -= rhs;
133        assert_eq!(expected, lhs);
134    }
135
136    #[rstest::rstest]
137    #[case(Phase(0x02), Phase(0x01), 2)]
138    #[case(Phase(0xFE), Phase(0x7F), 2)]
139    #[case(Phase::ZERO, Phase(0x80), 2)]
140    fn mul(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) {
141        assert_eq!(expected, lhs * rhs);
142        assert_eq!(expected, rhs * lhs);
143    }
144
145    #[rstest::rstest]
146    #[case(Phase(0x01), Phase(0x02), 2)]
147    #[case(Phase(0x7F), Phase(0xFE), 2)]
148    #[case(Phase::ZERO, Phase(0x01), 2)]
149    fn div(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) {
150        assert_eq!(expected, lhs / rhs);
151    }
152
153    #[rstest::rstest]
154    #[case(0.0, 0)]
155    #[case(2.0 * PI / 256.0 * 128.0, 128)]
156    #[case(2.0 * PI / 256.0 * 255.0, 255)]
157    fn radian(#[case] expect: f32, #[case] value: u8) {
158        approx::assert_abs_diff_eq!(expect, Phase(value).radian());
159    }
160
161    #[rstest::rstest]
162    #[case(Phase(0x00), Complex::new(1.0, 0.0))]
163    #[case(Phase(0x40), Complex::new(0.0, 1.0))]
164    #[case(Phase(0x80), Complex::new(-1.0, 0.0))]
165    #[case(Phase(0xC0), Complex::new(0.0, -1.0))]
166    fn from_complex(#[case] expect: Phase, #[case] value: Complex) {
167        assert_eq!(expect, Phase::from(value));
168    }
169
170    #[test]
171    fn dbg() {
172        assert_eq!(format!("{:?}", Phase::ZERO), "0x00");
173        assert_eq!(format!("{:?}", Phase(0x01)), "0x01");
174        assert_eq!(format!("{:?}", Phase(0xFF)), "0xFF");
175    }
176}