autd3_driver/firmware/operation/
segment.rs

1use std::mem::size_of;
2
3use crate::{
4    error::AUTDDriverError,
5    firmware::{
6        fpga::{Segment, TransitionMode},
7        operation::TypeTag,
8    },
9    geometry::Device,
10};
11
12use super::Operation;
13
14use zerocopy::{Immutable, IntoBytes};
15
16/// [`Datagram`] to change the segment.
17///
18/// [`Datagram`]: crate::datagram::Datagram
19#[derive(Debug, Clone, Copy)]
20pub enum SwapSegment {
21    /// Change the [`Gain`] segment.
22    ///
23    /// [`Gain`]: autd3_core::gain::Gain
24    Gain(Segment, TransitionMode),
25    /// Change the [`Modulation`] segment.
26    ///
27    /// [`Modulation`]: autd3_core::modulation::Modulation
28    Modulation(Segment, TransitionMode),
29    /// Change the [`FociSTM`] segment.
30    ///
31    /// [`FociSTM`]: crate::datagram::FociSTM
32    FociSTM(Segment, TransitionMode),
33    /// Change the [`GainSTM`] segment.
34    ///
35    /// [`GainSTM`]: crate::datagram::GainSTM
36    GainSTM(Segment, TransitionMode),
37}
38
39#[repr(C, align(2))]
40#[derive(IntoBytes, Immutable)]
41struct SwapSegmentT {
42    tag: TypeTag,
43    segment: u8,
44}
45
46#[repr(C, align(2))]
47#[derive(IntoBytes, Immutable)]
48struct SwapSegmentTWithTransition {
49    tag: TypeTag,
50    segment: u8,
51    transition_mode: u8,
52    __: [u8; 5],
53    transition_value: u64,
54}
55
56pub struct SwapSegmentOp {
57    is_done: bool,
58    segment: SwapSegment,
59}
60
61impl SwapSegmentOp {
62    pub(crate) fn new(segment: SwapSegment) -> Self {
63        Self {
64            is_done: false,
65            segment,
66        }
67    }
68}
69
70impl Operation for SwapSegmentOp {
71    type Error = AUTDDriverError;
72
73    fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result<usize, Self::Error> {
74        self.is_done = true;
75
76        let tag = match self.segment {
77            SwapSegment::Gain(_, _) => TypeTag::GainSwapSegment,
78            SwapSegment::Modulation(_, _) => TypeTag::ModulationSwapSegment,
79            SwapSegment::FociSTM(_, _) => TypeTag::FociSTMSwapSegment,
80            SwapSegment::GainSTM(_, _) => TypeTag::GainSTMSwapSegment,
81        };
82
83        match self.segment {
84            SwapSegment::Gain(segment, transition) => {
85                if transition != TransitionMode::Immediate {
86                    return Err(AUTDDriverError::InvalidTransitionMode);
87                }
88                super::write_to_tx(
89                    tx,
90                    SwapSegmentT {
91                        tag,
92                        segment: segment as u8,
93                    },
94                );
95
96                Ok(size_of::<SwapSegmentT>())
97            }
98            SwapSegment::Modulation(segment, transition)
99            | SwapSegment::FociSTM(segment, transition)
100            | SwapSegment::GainSTM(segment, transition) => {
101                super::write_to_tx(
102                    tx,
103                    SwapSegmentTWithTransition {
104                        tag,
105                        segment: segment as u8,
106                        transition_mode: transition.mode(),
107                        __: [0; 5],
108                        transition_value: transition.value(),
109                    },
110                );
111                Ok(size_of::<SwapSegmentTWithTransition>())
112            }
113        }
114    }
115
116    fn required_size(&self, _: &Device) -> usize {
117        match self.segment {
118            SwapSegment::Gain(_, _) => size_of::<SwapSegmentT>(),
119            SwapSegment::Modulation(_, _)
120            | SwapSegment::FociSTM(_, _)
121            | SwapSegment::GainSTM(_, _) => size_of::<SwapSegmentTWithTransition>(),
122        }
123    }
124
125    fn is_done(&self) -> bool {
126        self.is_done
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use crate::{ethercat::DcSysTime, firmware::operation::tests::create_device};
133
134    use super::*;
135
136    const NUM_TRANS_IN_UNIT: u8 = 249;
137
138    #[test]
139    fn gain() {
140        const FRAME_SIZE: usize = size_of::<SwapSegmentT>();
141
142        let device = create_device(NUM_TRANS_IN_UNIT);
143        let mut tx = vec![0x00u8; FRAME_SIZE];
144
145        let mut op = SwapSegmentOp::new(SwapSegment::Gain(Segment::S0, TransitionMode::Immediate));
146
147        assert_eq!(size_of::<SwapSegmentT>(), op.required_size(&device));
148        assert_eq!(Ok(size_of::<SwapSegmentT>()), op.pack(&device, &mut tx));
149        assert!(op.is_done());
150        assert_eq!(TypeTag::GainSwapSegment as u8, tx[0]);
151        assert_eq!(Segment::S0 as u8, tx[1]);
152    }
153
154    #[test]
155    fn gain_invalid_transition_mode() {
156        const FRAME_SIZE: usize = size_of::<SwapSegmentT>();
157
158        let device = create_device(NUM_TRANS_IN_UNIT);
159        let mut tx = vec![0x00u8; FRAME_SIZE];
160
161        let mut op = SwapSegmentOp::new(SwapSegment::Gain(Segment::S0, TransitionMode::Ext));
162
163        assert_eq!(
164            Some(AUTDDriverError::InvalidTransitionMode),
165            op.pack(&device, &mut tx).err()
166        );
167    }
168
169    #[test]
170    fn modulation() {
171        const FRAME_SIZE: usize = size_of::<SwapSegmentTWithTransition>();
172
173        let device = create_device(NUM_TRANS_IN_UNIT);
174        let mut tx = vec![0x00u8; FRAME_SIZE];
175
176        let sys_time = DcSysTime::ZERO + std::time::Duration::from_nanos(0x0123456789ABCDEF);
177        let transition_mode = TransitionMode::SysTime(sys_time);
178        let mut op = SwapSegmentOp::new(SwapSegment::Modulation(Segment::S0, transition_mode));
179
180        assert_eq!(
181            size_of::<SwapSegmentTWithTransition>(),
182            op.required_size(&device)
183        );
184        assert_eq!(
185            Ok(size_of::<SwapSegmentTWithTransition>()),
186            op.pack(&device, &mut tx)
187        );
188        assert!(op.is_done());
189        assert_eq!(TypeTag::ModulationSwapSegment as u8, tx[0]);
190        assert_eq!(Segment::S0 as u8, tx[1]);
191        let mode = transition_mode.mode();
192        let value = transition_mode.value();
193        assert_eq!(mode, tx[2]);
194        assert_eq!(value, u64::from_le_bytes(tx[8..].try_into().unwrap()));
195    }
196
197    #[test]
198    fn foci_stm() {
199        const FRAME_SIZE: usize = size_of::<SwapSegmentTWithTransition>();
200
201        let device = create_device(NUM_TRANS_IN_UNIT);
202        let mut tx = vec![0x00u8; FRAME_SIZE];
203
204        let sys_time = DcSysTime::ZERO + std::time::Duration::from_nanos(0x0123456789ABCDEF);
205        let transition_mode = TransitionMode::SysTime(sys_time);
206        let mut op = SwapSegmentOp::new(SwapSegment::FociSTM(Segment::S0, transition_mode));
207
208        assert_eq!(
209            size_of::<SwapSegmentTWithTransition>(),
210            op.required_size(&device)
211        );
212        assert_eq!(
213            Ok(size_of::<SwapSegmentTWithTransition>()),
214            op.pack(&device, &mut tx)
215        );
216        assert!(op.is_done());
217        assert_eq!(TypeTag::FociSTMSwapSegment as u8, tx[0]);
218        assert_eq!(Segment::S0 as u8, tx[1]);
219        let mode = transition_mode.mode();
220        let value = transition_mode.value();
221        assert_eq!(mode, tx[2]);
222        assert_eq!(value, u64::from_le_bytes(tx[8..].try_into().unwrap()));
223    }
224
225    #[test]
226    fn gain_stm() {
227        const FRAME_SIZE: usize = size_of::<SwapSegmentTWithTransition>();
228
229        let device = create_device(NUM_TRANS_IN_UNIT);
230        let mut tx = vec![0x00u8; FRAME_SIZE];
231
232        let sys_time = DcSysTime::ZERO + std::time::Duration::from_nanos(0x0123456789ABCDEF);
233        let transition_mode = TransitionMode::SysTime(sys_time);
234        let mut op = SwapSegmentOp::new(SwapSegment::GainSTM(Segment::S0, transition_mode));
235
236        assert_eq!(
237            size_of::<SwapSegmentTWithTransition>(),
238            op.required_size(&device)
239        );
240        assert_eq!(
241            Ok(size_of::<SwapSegmentTWithTransition>()),
242            op.pack(&device, &mut tx)
243        );
244        assert!(op.is_done());
245        assert_eq!(TypeTag::GainSTMSwapSegment as u8, tx[0]);
246        assert_eq!(Segment::S0 as u8, tx[1]);
247        let mode = transition_mode.mode();
248        let value = transition_mode.value();
249        assert_eq!(mode, tx[2]);
250        assert_eq!(value, u64::from_le_bytes(tx[8..].try_into().unwrap()));
251    }
252}