autd3_driver/firmware/v10/fpga/
fpga_state.rs

1use autd3_core::{datagram::Segment, link::RxMessage};
2
3use getset::CopyGetters;
4
5const THERMAL_ASSERT_BIT: u8 = 1 << 0;
6const CURRENT_MOD_SEGMENT_BIT: u8 = 1 << 1;
7const CURRENT_STM_SEGMENT_BIT: u8 = 1 << 2;
8const CURRENT_GAIN_SEGMENT_BIT: u8 = 1 << 2;
9const IS_GAIN_MODE_BIT: u8 = 1 << 3;
10const READS_FPGA_STATE_ENABLED: u8 = 1 << 7;
11
12/// FPGA state.
13#[repr(C)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, CopyGetters)]
15pub struct FPGAState {
16    #[doc(hidden)]
17    #[getset(get_copy = "pub")]
18    pub(crate) state: u8,
19}
20
21impl FPGAState {
22    /// `true` if the thermal sensor is asserted.
23    #[must_use]
24    pub const fn is_thermal_assert(&self) -> bool {
25        (self.state & THERMAL_ASSERT_BIT) != 0
26    }
27
28    /// The current Modulation segment.
29    #[must_use]
30    pub const fn current_mod_segment(&self) -> Segment {
31        match self.state & CURRENT_MOD_SEGMENT_BIT {
32            0 => Segment::S0,
33            _ => Segment::S1,
34        }
35    }
36
37    /// The current STM segment. `None` if the current mode is not STM.
38    #[must_use]
39    pub const fn current_stm_segment(&self) -> Option<Segment> {
40        if !self.is_stm_mode() {
41            return None;
42        }
43        match self.state & CURRENT_STM_SEGMENT_BIT {
44            0 => Some(Segment::S0),
45            _ => Some(Segment::S1),
46        }
47    }
48
49    /// The current Gain segment. `None` if the current mode is not Gain.
50    #[must_use]
51    pub const fn current_gain_segment(&self) -> Option<Segment> {
52        if !self.is_gain_mode() {
53            return None;
54        }
55        match self.state & CURRENT_GAIN_SEGMENT_BIT {
56            0 => Some(Segment::S0),
57            _ => Some(Segment::S1),
58        }
59    }
60
61    /// `true` if the current mode is Gain.
62    #[must_use]
63    pub const fn is_gain_mode(&self) -> bool {
64        (self.state & IS_GAIN_MODE_BIT) != 0
65    }
66
67    /// `true` if the current mode is STM.
68    #[must_use]
69    pub const fn is_stm_mode(&self) -> bool {
70        !self.is_gain_mode()
71    }
72
73    #[doc(hidden)]
74    #[must_use]
75    pub fn from_rx(msg: &RxMessage) -> Option<Self> {
76        if msg.data() & READS_FPGA_STATE_ENABLED != 0 {
77            Some(FPGAState { state: msg.data() })
78        } else {
79            None
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use std::mem::size_of;
87
88    use super::*;
89
90    #[test]
91    fn size() {
92        assert_eq!(1, size_of::<FPGAState>());
93        assert_eq!(0, std::mem::offset_of!(FPGAState, state));
94    }
95
96    #[rstest::rstest]
97    #[test]
98    #[case(false, 0b0)]
99    #[case(true, 0b1)]
100    fn is_thermal_assert(#[case] expected: bool, #[case] state: u8) {
101        assert_eq!(expected, FPGAState { state }.is_thermal_assert());
102    }
103
104    #[rstest::rstest]
105    #[test]
106    #[case(Segment::S0, 0b00)]
107    #[case(Segment::S1, 0b10)]
108    fn current_mod_segment(#[case] expected: Segment, #[case] state: u8) {
109        assert_eq!(expected, FPGAState { state }.current_mod_segment());
110    }
111
112    #[rstest::rstest]
113    #[test]
114    #[case(false, 0b0000)]
115    #[case(true, 0b1000)]
116    fn is_gain_mode(#[case] expected: bool, #[case] state: u8) {
117        assert_eq!(expected, FPGAState { state }.is_gain_mode());
118    }
119
120    #[rstest::rstest]
121    #[test]
122    #[case(false, 0b1000)]
123    #[case(true, 0b0000)]
124    fn is_stm_mode(#[case] expected: bool, #[case] state: u8) {
125        assert_eq!(expected, FPGAState { state }.is_stm_mode());
126    }
127
128    #[rstest::rstest]
129    #[test]
130    #[case(None, 0b1000)]
131    #[case(Some(Segment::S0), 0b0000)]
132    #[case(Some(Segment::S1), 0b0100)]
133    fn current_stm_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
134        assert_eq!(expected, FPGAState { state }.current_stm_segment());
135    }
136
137    #[rstest::rstest]
138    #[test]
139    #[case(None, 0b0000)]
140    #[case(Some(Segment::S0), 0b1000)]
141    #[case(Some(Segment::S1), 0b1100)]
142    fn current_gain_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
143        assert_eq!(expected, FPGAState { state }.current_gain_segment());
144    }
145}