autd3_core/gain/
phase.rs

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