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#[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 pub const ZERO: Self = Self(0);
21 pub const PI: Self = Self(0x80);
23
24 #[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}