autd3_emulator/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(missing_docs)]
3#![warn(rustdoc::missing_crate_level_docs)]
4#![warn(rustdoc::unescaped_backticks)]
5
6//! This crate provides a emulator for autd3 that calculates sound field, emulates of firmware, etc.
7
8mod error;
9mod option;
10mod record;
11mod utils;
12
13use bvh::aabb::Aabb;
14pub use error::EmulatorError;
15use getset::Getters;
16pub use option::*;
17#[cfg(feature = "polars")]
18use polars::{df, frame::DataFrame};
19use record::TransducerRecord;
20pub use record::{Instant, InstantRecordOption, Record, Rms, RmsRecordOption};
21
22use std::time::Duration;
23
24use derive_more::{Deref, DerefMut};
25
26use autd3::{
27    Controller,
28    driver::{
29        defined::ULTRASOUND_PERIOD,
30        ethercat::DcSysTime,
31        firmware::{
32            cpu::{RxMessage, TxMessage},
33            fpga::{EmitIntensity, Phase},
34        },
35    },
36};
37use autd3_core::{
38    derive::Device,
39    geometry::{Geometry, Transducer},
40    link::{Link, LinkError},
41};
42use autd3_firmware_emulator::{
43    CPUEmulator,
44    cpu::params::{TAG_CLEAR, TAG_SILENCER},
45    fpga::emulator::SilencerEmulator,
46};
47
48use crate::utils::device::clone_device;
49
50pub(crate) struct RawTransducerRecord {
51    pub pulse_width: Vec<u16>,
52    pub phase: Vec<u8>,
53    pub silencer_phase: SilencerEmulator<Phase>,
54    pub silencer_intensity: SilencerEmulator<EmitIntensity>,
55}
56
57pub(crate) struct RawDeviceRecord {
58    pub records: Vec<RawTransducerRecord>,
59}
60
61pub(crate) struct RawRecord {
62    pub records: Vec<RawDeviceRecord>,
63    pub start: DcSysTime,
64    pub current: DcSysTime,
65}
66
67/// A recorder for the sound field.
68pub struct Recorder {
69    start_time: DcSysTime,
70    is_open: bool,
71    emulators: Vec<CPUEmulator>,
72    geometry: Geometry,
73    record: RawRecord,
74}
75
76impl Recorder {
77    fn new(start_time: DcSysTime) -> Self {
78        Self {
79            start_time,
80            is_open: false,
81            emulators: Vec::new(),
82            geometry: Geometry::new(Vec::new()),
83            record: RawRecord {
84                records: Vec::new(),
85                start: DcSysTime::ZERO,
86                current: DcSysTime::ZERO,
87            },
88        }
89    }
90}
91
92impl Link for Recorder {
93    fn close(&mut self) -> Result<(), LinkError> {
94        self.is_open = false;
95        Ok(())
96    }
97
98    fn send(&mut self, tx: &[TxMessage]) -> Result<(), LinkError> {
99        self.emulators
100            .iter_mut()
101            .zip(self.record.records.iter_mut())
102            .for_each(|(cpu, r)| {
103                cpu.send(tx);
104
105                let should_update_silencer =
106                    |tag: u8| -> bool { matches!(tag, TAG_SILENCER | TAG_CLEAR) };
107                let update_silencer = should_update_silencer(tx[cpu.idx()].payload()[0]);
108                let slot_2_offset = tx[cpu.idx()].header.slot_2_offset as usize;
109                let update_silencer = if slot_2_offset != 0 {
110                    update_silencer
111                        || should_update_silencer(tx[cpu.idx()].payload()[slot_2_offset])
112                } else {
113                    update_silencer
114                };
115                if update_silencer {
116                    r.records.iter_mut().for_each(|tr| {
117                        tr.silencer_phase = cpu
118                            .fpga()
119                            .silencer_emulator_phase_continue_with(tr.silencer_phase);
120                        tr.silencer_intensity = cpu
121                            .fpga()
122                            .silencer_emulator_intensity_continue_with(tr.silencer_intensity);
123                    });
124                }
125            });
126
127        Ok(())
128    }
129
130    fn receive(&mut self, rx: &mut [RxMessage]) -> Result<(), LinkError> {
131        self.emulators.iter_mut().for_each(|cpu| {
132            cpu.update_with_sys_time(self.record.current);
133            rx[cpu.idx()] = cpu.rx();
134        });
135
136        Ok(())
137    }
138
139    fn is_open(&self) -> bool {
140        self.is_open
141    }
142
143    fn open(&mut self, geometry: &Geometry) -> Result<(), LinkError> {
144        self.is_open = true;
145        self.emulators = geometry
146            .iter()
147            .enumerate()
148            .filter(|(_, dev)| dev.enable)
149            .map(|(i, dev)| CPUEmulator::new(i, dev.num_transducers()))
150            .collect::<Vec<_>>();
151        self.record = RawRecord {
152            records: self
153                .emulators
154                .iter()
155                .map(|cpu| RawDeviceRecord {
156                    records: geometry[cpu.idx()]
157                        .iter()
158                        .map(|_| RawTransducerRecord {
159                            pulse_width: Vec::new(),
160                            phase: Vec::new(),
161                            silencer_phase: cpu.fpga().silencer_emulator_phase(0),
162                            silencer_intensity: cpu.fpga().silencer_emulator_intensity(0),
163                        })
164                        .collect(),
165                })
166                .collect(),
167            current: self.start_time,
168            start: self.start_time,
169        };
170        self.geometry = Geometry::new(geometry.devices().map(clone_device).collect());
171        Ok(())
172    }
173}
174
175impl Recorder {
176    /// Progresses by the specified time.
177    pub fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
178        // This function must be public for capi.
179        if tick.is_zero() || tick.as_nanos() % ULTRASOUND_PERIOD.as_nanos() != 0 {
180            return Err(EmulatorError::InvalidTick);
181        }
182        let mut t = self.record.current;
183        let end = t + tick;
184        loop {
185            self.emulators
186                .iter_mut()
187                .zip(self.geometry.iter())
188                .for_each(|(cpu, dev)| {
189                    cpu.update_with_sys_time(t);
190                    let m = cpu.fpga().modulation();
191                    let d = cpu.fpga().drives();
192                    dev.iter().zip(d).for_each(|(tr, d)| {
193                        let tr_record = &mut self.record.records[tr.dev_idx()].records[tr.idx()];
194                        tr_record.pulse_width.push(
195                            cpu.fpga()
196                                .pulse_width_encoder_table_at(
197                                    tr_record
198                                        .silencer_intensity
199                                        .apply((d.intensity.0 as u16 * m as u16 / 255) as u8)
200                                        as _,
201                                )
202                                .pulse_width(),
203                        );
204                        tr_record
205                            .phase
206                            .push(tr_record.silencer_phase.apply(d.phase.0))
207                    });
208                });
209            t = t + ULTRASOUND_PERIOD;
210            if t == end {
211                break;
212            }
213        }
214        self.record.current = end;
215        Ok(())
216    }
217}
218
219/// A emulator for the AUTD devices.
220#[derive(Getters, Deref, DerefMut)]
221pub struct Emulator {
222    #[getset(get = "pub")]
223    #[deref]
224    #[deref_mut]
225    /// The geometry of the devices.
226    geometry: Geometry,
227}
228
229impl Emulator {
230    /// Creates a new emulator.
231    pub fn new<D: Into<Device>, F: IntoIterator<Item = D>>(devices: F) -> Self {
232        Self {
233            geometry: Geometry::new(devices.into_iter().map(|dev| dev.into()).collect()),
234        }
235    }
236
237    /// Records the sound field.
238    ///
239    /// # Example
240    ///
241    /// ```
242    /// # use autd3::prelude::*;
243    /// # use autd3_emulator::*;
244    /// # use std::time::Duration;
245    /// # fn example() -> Result<(), EmulatorError> {
246    /// let emulator = Emulator::new([AUTD3 {
247    ///        pos: Point3::origin(),
248    ///        rot: UnitQuaternion::identity(),
249    ///    }]);
250    /// let record = emulator
251    ///      .record(|autd| {
252    ///          autd.send(Silencer::default())?;
253    ///          autd.send((Sine { freq: 200 * Hz, option: Default::default() }, Uniform { intensity: EmitIntensity(0xFF), phase: Phase::ZERO }))?;
254    ///          autd.tick(Duration::from_millis(10))?;
255    ///          Ok(())
256    ///      })
257    ///      ?;
258    /// # Ok(())
259    /// # }
260    /// ```
261    pub fn record(
262        &self,
263        f: impl FnOnce(&mut Controller<Recorder>) -> Result<(), EmulatorError>,
264    ) -> Result<Record, EmulatorError> {
265        self.record_from(DcSysTime::ZERO, f)
266    }
267
268    fn collect_record(mut recorder: Controller<Recorder>) -> Result<Record, EmulatorError> {
269        let start = recorder.link().record.start;
270        let end = recorder.link().record.current;
271        let devices = {
272            let mut tmp = Vec::new();
273            std::mem::swap(&mut tmp, recorder.geometry_mut());
274            tmp
275        };
276
277        let aabb = devices
278            .iter()
279            .filter(|dev| dev.enable)
280            .fold(Aabb::empty(), |aabb, dev| aabb.join(dev.aabb()));
281
282        let records = recorder
283            .link_mut()
284            .record
285            .records
286            .drain(..)
287            .zip(devices.into_iter().filter(|dev| dev.enable))
288            .flat_map(|(rd, dev)| {
289                rd.records
290                    .into_iter()
291                    .zip(dev)
292                    .map(|(r, tr)| TransducerRecord {
293                        pulse_width: r.pulse_width,
294                        phase: r.phase,
295                        tr,
296                    })
297            })
298            .collect();
299
300        recorder.close()?;
301
302        Ok(Record {
303            records,
304            start,
305            end,
306            aabb,
307        })
308    }
309
310    /// Records the sound field from the specified time.
311    pub fn record_from(
312        &self,
313        start_time: DcSysTime,
314        f: impl FnOnce(&mut Controller<Recorder>) -> Result<(), EmulatorError>,
315    ) -> Result<Record, EmulatorError> {
316        let mut recorder = Controller::open(
317            self.geometry.iter().map(clone_device),
318            Recorder::new(start_time),
319        )?;
320        f(&mut recorder)?;
321        Self::collect_record(recorder)
322    }
323
324    // GRCOV_EXCL_START
325    #[doc(hidden)]
326    // This function is used in capi.
327    pub fn record_from_take(
328        &self,
329        start_time: DcSysTime,
330        f: impl FnOnce(Controller<Recorder>) -> Result<Controller<Recorder>, EmulatorError>,
331    ) -> Result<Record, EmulatorError> {
332        let recorder = Controller::open(
333            self.geometry.iter().map(clone_device),
334            Recorder::new(start_time),
335        )?;
336        let recorder = f(recorder)?;
337        Self::collect_record(recorder)
338    }
339    // GRCOV_EXCL_STOP
340
341    fn transducers(&self) -> impl Iterator<Item = &Transducer> {
342        self.geometry.devices().flat_map(|dev| dev.iter())
343    }
344
345    #[cfg_attr(feature = "inplace", visibility::make(pub))]
346    #[doc(hidden)]
347    fn transducer_table_rows(&self) -> usize {
348        self.geometry.num_transducers()
349    }
350
351    #[cfg_attr(feature = "inplace", visibility::make(pub))]
352    #[doc(hidden)]
353    fn dev_indices_inplace(&self, dev_indices: &mut [u16]) {
354        self.transducers()
355            .zip(dev_indices.iter_mut())
356            .for_each(|(tr, dst)| *dst = tr.dev_idx() as u16);
357    }
358
359    #[cfg_attr(feature = "inplace", visibility::make(pub))]
360    #[doc(hidden)]
361    fn tr_indices_inplace(&self, tr_indices: &mut [u8]) {
362        self.transducers()
363            .zip(tr_indices.iter_mut())
364            .for_each(|(tr, dst)| *dst = tr.idx() as u8);
365    }
366
367    #[cfg_attr(feature = "inplace", visibility::make(pub))]
368    #[doc(hidden)]
369    fn tr_positions_inplace(&self, x: &mut [f32], y: &mut [f32], z: &mut [f32]) {
370        self.transducers()
371            .zip(x.iter_mut())
372            .zip(y.iter_mut())
373            .zip(z.iter_mut())
374            .for_each(|(((tr, x), y), z)| {
375                *x = tr.position().x;
376                *y = tr.position().y;
377                *z = tr.position().z;
378            });
379    }
380
381    #[cfg_attr(feature = "inplace", visibility::make(pub))]
382    #[doc(hidden)]
383    fn tr_dir_inplace(&self, x: &mut [f32], y: &mut [f32], z: &mut [f32]) {
384        self.transducers()
385            .zip(x.iter_mut())
386            .zip(y.iter_mut())
387            .zip(z.iter_mut())
388            .for_each(|(((tr, x), y), z)| {
389                *x = self.geometry[tr.dev_idx()].axial_direction().x;
390                *y = self.geometry[tr.dev_idx()].axial_direction().y;
391                *z = self.geometry[tr.dev_idx()].axial_direction().z;
392            });
393    }
394
395    #[cfg(feature = "polars")]
396    /// Returns properties of transducers.
397    pub fn transducer_table(&self) -> DataFrame {
398        let n = self.transducer_table_rows();
399        let mut dev_indices = vec![0; n];
400        let mut tr_indices = vec![0; n];
401        let mut x = vec![0.0; n];
402        let mut y = vec![0.0; n];
403        let mut z = vec![0.0; n];
404        let mut nx = vec![0.0; n];
405        let mut ny = vec![0.0; n];
406        let mut nz = vec![0.0; n];
407        self.dev_indices_inplace(&mut dev_indices);
408        self.tr_indices_inplace(&mut tr_indices);
409        self.tr_positions_inplace(&mut x, &mut y, &mut z);
410        self.tr_dir_inplace(&mut nx, &mut ny, &mut nz);
411        df!(
412            "dev_idx" => &dev_indices,
413            "tr_idx" => &tr_indices,
414            "x[mm]" => &x,
415            "y[mm]" => &y,
416            "z[mm]" => &z,
417            "nx" => &nx,
418            "ny" => &ny,
419            "nz" => &nz,
420        )
421        .unwrap()
422    }
423}
424
425/// A extension trait for `Controller<Recorder>`.
426pub trait RecorderControllerExt {
427    /// Progresses by the specified time.
428    fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError>;
429}
430
431impl RecorderControllerExt for Controller<Recorder> {
432    fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
433        self.link_mut().tick(tick)
434    }
435}