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
13pub use error::EmulatorError;
14pub use option::*;
15#[cfg(feature = "polars")]
16use polars::{df, frame::DataFrame};
17use record::TransducerRecord;
18pub use record::{Instant, InstantRecordOption, Record, Rms, RmsRecordOption};
19
20use std::time::Duration;
21
22use autd3::{
23    controller::{Controller, SenderOption},
24    driver::{common::ULTRASOUND_PERIOD, ethercat::DcSysTime},
25};
26use autd3_core::{
27    firmware::{Drive, Intensity, Phase},
28    geometry::{Device, Geometry, Transducer},
29    link::{Link, LinkError, RxMessage, TxBufferPoolSync, TxMessage},
30};
31use autd3_firmware_emulator::{
32    CPUEmulator,
33    cpu::params::{TAG_CLEAR, TAG_SILENCER},
34    fpga::emulator::SilencerEmulator,
35};
36
37use crate::utils::{aabb::Aabb, device::clone_device};
38
39pub(crate) struct RawTransducerRecord {
40    pub pulse_width: Vec<u16>,
41    pub phase: Vec<u8>,
42    pub silencer_phase: SilencerEmulator<Phase>,
43    pub silencer_intensity: SilencerEmulator<Intensity>,
44}
45
46pub(crate) struct RawDeviceRecord {
47    pub records: Vec<RawTransducerRecord>,
48}
49
50pub(crate) struct RawRecord {
51    pub records: Vec<RawDeviceRecord>,
52    pub start: DcSysTime,
53    pub current: DcSysTime,
54}
55
56struct NopSleeper;
57
58impl autd3_core::sleep::Sleeper for NopSleeper {
59    fn sleep(&self, _duration: Duration) {} // GRCOV_EXCL_LINE
60}
61
62/// A recorder for the sound field.
63pub struct Recorder {
64    start_time: DcSysTime,
65    is_open: bool,
66    emulators: Vec<CPUEmulator>,
67    geometry: Geometry,
68    record: RawRecord,
69    buffer_pool: TxBufferPoolSync,
70    drives_buffer: Vec<Vec<Drive>>,
71    phases_buffer: Vec<Vec<Phase>>,
72    output_mask_buffer: Vec<Vec<bool>>,
73}
74
75impl Recorder {
76    fn new(start_time: DcSysTime) -> Self {
77        Self {
78            start_time,
79            is_open: false,
80            emulators: Vec::new(),
81            geometry: Geometry::new(Vec::new()),
82            record: RawRecord {
83                records: Vec::new(),
84                start: DcSysTime::ZERO,
85                current: DcSysTime::ZERO,
86            },
87            buffer_pool: TxBufferPoolSync::new(),
88            drives_buffer: Vec::new(),
89            phases_buffer: Vec::new(),
90            output_mask_buffer: Vec::new(),
91        }
92    }
93}
94
95impl Link for Recorder {
96    // GRCOV_EXCL_START
97    fn close(&mut self) -> Result<(), LinkError> {
98        self.is_open = false;
99        Ok(())
100    }
101    // GRCOV_EXCL_STOP
102
103    fn send(&mut self, tx: Vec<TxMessage>) -> Result<(), LinkError> {
104        self.emulators
105            .iter_mut()
106            .zip(self.record.records.iter_mut())
107            .for_each(|(cpu, r)| {
108                cpu.send(&tx);
109
110                let should_update_silencer =
111                    |tag: u8| -> bool { matches!(tag, TAG_SILENCER | TAG_CLEAR) };
112                let update_silencer = should_update_silencer(tx[cpu.idx()].payload()[0]);
113                let slot_2_offset = tx[cpu.idx()].header.slot_2_offset as usize;
114                let update_silencer = if slot_2_offset != 0 {
115                    update_silencer
116                        || should_update_silencer(tx[cpu.idx()].payload()[slot_2_offset])
117                } else {
118                    update_silencer
119                };
120                if update_silencer {
121                    r.records.iter_mut().for_each(|tr| {
122                        tr.silencer_phase = cpu
123                            .fpga()
124                            .silencer_emulator_phase_continue_with(tr.silencer_phase);
125                        tr.silencer_intensity = cpu
126                            .fpga()
127                            .silencer_emulator_intensity_continue_with(tr.silencer_intensity);
128                    });
129                }
130            });
131        self.buffer_pool.return_buffer(tx);
132
133        Ok(())
134    }
135
136    fn receive(&mut self, rx: &mut [RxMessage]) -> Result<(), LinkError> {
137        self.emulators.iter_mut().for_each(|cpu| {
138            cpu.update_with_sys_time(self.record.current);
139            rx[cpu.idx()] = cpu.rx();
140        });
141
142        Ok(())
143    }
144
145    fn is_open(&self) -> bool {
146        self.is_open
147    }
148
149    fn open(&mut self, geometry: &Geometry) -> Result<(), LinkError> {
150        self.is_open = true;
151        self.buffer_pool.init(geometry);
152        self.emulators = geometry
153            .iter()
154            .enumerate()
155            .map(|(i, dev)| CPUEmulator::new(i, dev.num_transducers()))
156            .collect::<Vec<_>>();
157        self.record = RawRecord {
158            records: self
159                .emulators
160                .iter()
161                .map(|cpu| RawDeviceRecord {
162                    records: geometry[cpu.idx()]
163                        .iter()
164                        .map(|_| RawTransducerRecord {
165                            pulse_width: Vec::new(),
166                            phase: Vec::new(),
167                            silencer_phase: cpu.fpga().silencer_emulator_phase(0),
168                            silencer_intensity: cpu.fpga().silencer_emulator_intensity(0),
169                        })
170                        .collect(),
171                })
172                .collect(),
173            current: self.start_time,
174            start: self.start_time,
175        };
176        self.geometry = Geometry::new(geometry.iter().map(clone_device).collect());
177        self.drives_buffer = self
178            .emulators
179            .iter()
180            .map(|cpu| vec![Drive::NULL; cpu.num_transducers()])
181            .collect();
182        self.phases_buffer = self
183            .emulators
184            .iter()
185            .map(|cpu| vec![Phase::ZERO; cpu.num_transducers()])
186            .collect();
187        self.output_mask_buffer = self
188            .emulators
189            .iter()
190            .map(|cpu| vec![true; cpu.num_transducers()])
191            .collect();
192
193        Ok(())
194    }
195
196    fn alloc_tx_buffer(&mut self) -> Result<Vec<TxMessage>, LinkError> {
197        Ok(self.buffer_pool.borrow())
198    }
199}
200
201impl Recorder {
202    /// Progresses by the specified time.
203    pub fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
204        // This function must be public for capi.
205        if tick.is_zero() || !tick.as_nanos().is_multiple_of(ULTRASOUND_PERIOD.as_nanos()) {
206            return Err(EmulatorError::InvalidTick);
207        }
208        let mut t = self.record.current;
209        let end = t + tick;
210        loop {
211            self.emulators
212                .iter_mut()
213                .zip(self.geometry.iter())
214                .zip(self.drives_buffer.iter_mut())
215                .zip(self.phases_buffer.iter_mut())
216                .zip(self.output_mask_buffer.iter_mut())
217                .for_each(|((((cpu, dev), drives_buf), phase_buf), output_mask_buf)| {
218                    cpu.update_with_sys_time(t);
219                    let m = cpu.fpga().modulation();
220                    let cur_seg = cpu.fpga().current_stm_segment();
221                    let cur_idx = cpu.fpga().current_stm_idx();
222                    unsafe {
223                        cpu.fpga().drives_at_inplace(
224                            cur_seg,
225                            cur_idx,
226                            phase_buf.as_mut_ptr(),
227                            output_mask_buf.as_mut_ptr(),
228                            drives_buf.as_mut_ptr(),
229                        )
230                    };
231                    dev.iter().zip(drives_buf).for_each(|(tr, d)| {
232                        let tr_record = &mut self.record.records[tr.dev_idx()].records[tr.idx()];
233                        tr_record.pulse_width.push(
234                            cpu.fpga()
235                                .pulse_width_encoder_table_at(
236                                    tr_record
237                                        .silencer_intensity
238                                        .apply((d.intensity.0 as u16 * m as u16 / 255) as u8)
239                                        as _,
240                                )
241                                .pulse_width()
242                                .unwrap(),
243                        );
244                        tr_record
245                            .phase
246                            .push(tr_record.silencer_phase.apply(d.phase.0))
247                    });
248                });
249            t += ULTRASOUND_PERIOD;
250            if t == end {
251                break;
252            }
253        }
254        self.record.current = end;
255        Ok(())
256    }
257}
258
259/// A emulator for the AUTD devices.
260pub struct Emulator {
261    /// The geometry of the devices.
262    geometry: Geometry,
263}
264
265impl std::ops::Deref for Emulator {
266    type Target = Geometry;
267
268    fn deref(&self) -> &Self::Target {
269        &self.geometry
270    }
271}
272
273impl std::ops::DerefMut for Emulator {
274    fn deref_mut(&mut self) -> &mut Self::Target {
275        &mut self.geometry
276    }
277}
278
279impl Emulator {
280    /// Creates a new emulator.
281    pub fn new<D: Into<Device>, F: IntoIterator<Item = D>>(devices: F) -> Self {
282        Self {
283            geometry: Geometry::new(devices.into_iter().map(|dev| dev.into()).collect()),
284        }
285    }
286
287    #[doc(hidden)]
288    pub const fn geometry(&self) -> &Geometry {
289        &self.geometry
290    }
291
292    #[doc(hidden)]
293    pub fn geometry_mut(&mut self) -> &mut Geometry {
294        &mut self.geometry
295    }
296
297    /// Records the sound field.
298    ///
299    /// # Example
300    ///
301    /// ```
302    /// # use autd3::prelude::*;
303    /// # use autd3_emulator::*;
304    /// # use std::time::Duration;
305    /// # fn example() -> Result<(), EmulatorError> {
306    /// let emulator = Emulator::new([AUTD3 {
307    ///        pos: Point3::origin(),
308    ///        rot: UnitQuaternion::identity(),
309    ///    }]);
310    /// let record = emulator
311    ///      .record(|autd| {
312    ///          autd.send(Silencer::default())?;
313    ///          autd.send((Sine { freq: 200 * Hz, option: Default::default() }, Uniform { intensity: Intensity(0xFF), phase: Phase::ZERO }))?;
314    ///          autd.tick(Duration::from_millis(10))?;
315    ///          Ok(())
316    ///      })
317    ///      ?;
318    /// # Ok(())
319    /// # }
320    /// ```
321    pub fn record(
322        &self,
323        f: impl FnOnce(&mut Controller<Recorder>) -> Result<(), EmulatorError>,
324    ) -> Result<Record, EmulatorError> {
325        self.record_from(DcSysTime::ZERO, f)
326    }
327
328    fn collect_record(mut recorder: Controller<Recorder>) -> Result<Record, EmulatorError> {
329        let start = recorder.link().record.start;
330        let end = recorder.link().record.current;
331
332        // Here, we take the geometry from the recorder and clear it.
333        // So, calling `Controller::send` cause `failed to confirm response` error after here.
334        let mut geometry = {
335            let mut tmp = Geometry::new(Vec::new());
336            std::mem::swap(&mut tmp, &mut recorder);
337            tmp
338        };
339        recorder.link_mut().is_open = false;
340
341        let aabb = Aabb::from_geometry(&geometry);
342        let records = recorder
343            .link_mut()
344            .record
345            .records
346            .drain(..)
347            .zip(geometry.drain(..))
348            .flat_map(|(rd, dev)| {
349                rd.records
350                    .into_iter()
351                    .zip(dev)
352                    .map(|(r, tr)| TransducerRecord {
353                        pulse_width: r.pulse_width,
354                        phase: r.phase,
355                        tr,
356                    })
357            })
358            .collect();
359
360        drop(recorder);
361
362        Ok(Record {
363            records,
364            start,
365            end,
366            aabb,
367        })
368    }
369
370    /// Records the sound field from the specified time.
371    pub fn record_from(
372        &self,
373        start_time: DcSysTime,
374        f: impl FnOnce(&mut Controller<Recorder>) -> Result<(), EmulatorError>,
375    ) -> Result<Record, EmulatorError> {
376        let mut recorder = Controller::open_with(
377            self.geometry.iter().map(clone_device),
378            Recorder::new(start_time),
379            SenderOption {
380                send_interval: None,
381                receive_interval: None,
382                ..Default::default()
383            },
384            NopSleeper,
385        )?;
386        f(&mut recorder)?;
387        Self::collect_record(recorder)
388    }
389
390    // GRCOV_EXCL_START
391    #[doc(hidden)]
392    // This function is used in capi.
393    pub fn record_from_take(
394        &self,
395        start_time: DcSysTime,
396        f: impl FnOnce(Controller<Recorder>) -> Result<Controller<Recorder>, EmulatorError>,
397    ) -> Result<Record, EmulatorError> {
398        let recorder = Controller::open_with(
399            self.geometry.iter().map(clone_device),
400            Recorder::new(start_time),
401            SenderOption {
402                send_interval: None,
403                receive_interval: None,
404                ..Default::default()
405            },
406            NopSleeper,
407        )?;
408        let recorder = f(recorder)?;
409        Self::collect_record(recorder)
410    }
411    // GRCOV_EXCL_STOP
412
413    fn transducers(&self) -> impl Iterator<Item = &Transducer> {
414        self.geometry.iter().flat_map(|dev| dev.iter())
415    }
416
417    #[doc(hidden)]
418    pub fn transducer_table_rows(&self) -> usize {
419        self.geometry.num_transducers()
420    }
421
422    #[doc(hidden)]
423    pub fn dev_indices_inplace(&self, dev_indices: &mut [u16]) {
424        self.transducers()
425            .zip(dev_indices.iter_mut())
426            .for_each(|(tr, dst)| *dst = tr.dev_idx() as u16);
427    }
428
429    #[doc(hidden)]
430    pub fn tr_indices_inplace(&self, tr_indices: &mut [u8]) {
431        self.transducers()
432            .zip(tr_indices.iter_mut())
433            .for_each(|(tr, dst)| *dst = tr.idx() as u8);
434    }
435
436    #[doc(hidden)]
437    pub fn tr_positions_inplace(&self, x: &mut [f32], y: &mut [f32], z: &mut [f32]) {
438        self.transducers()
439            .zip(x.iter_mut())
440            .zip(y.iter_mut())
441            .zip(z.iter_mut())
442            .for_each(|(((tr, x), y), z)| {
443                *x = tr.position().x;
444                *y = tr.position().y;
445                *z = tr.position().z;
446            });
447    }
448
449    #[doc(hidden)]
450    pub fn tr_dir_inplace(&self, x: &mut [f32], y: &mut [f32], z: &mut [f32]) {
451        self.transducers()
452            .zip(x.iter_mut())
453            .zip(y.iter_mut())
454            .zip(z.iter_mut())
455            .for_each(|(((tr, x), y), z)| {
456                *x = self.geometry[tr.dev_idx()].axial_direction().x;
457                *y = self.geometry[tr.dev_idx()].axial_direction().y;
458                *z = self.geometry[tr.dev_idx()].axial_direction().z;
459            });
460    }
461
462    #[cfg(feature = "polars")]
463    /// Returns properties of transducers.
464    pub fn transducer_table(&self) -> DataFrame {
465        let n = self.transducer_table_rows();
466        let mut dev_indices = vec![0; n];
467        let mut tr_indices = vec![0; n];
468        let mut x = vec![0.0; n];
469        let mut y = vec![0.0; n];
470        let mut z = vec![0.0; n];
471        let mut nx = vec![0.0; n];
472        let mut ny = vec![0.0; n];
473        let mut nz = vec![0.0; n];
474        self.dev_indices_inplace(&mut dev_indices);
475        self.tr_indices_inplace(&mut tr_indices);
476        self.tr_positions_inplace(&mut x, &mut y, &mut z);
477        self.tr_dir_inplace(&mut nx, &mut ny, &mut nz);
478        df!(
479            "dev_idx" => &dev_indices,
480            "tr_idx" => &tr_indices,
481            "x[mm]" => &x,
482            "y[mm]" => &y,
483            "z[mm]" => &z,
484            "nx" => &nx,
485            "ny" => &ny,
486            "nz" => &nz,
487        )
488        .unwrap()
489    }
490}
491
492/// A extension trait for `Controller<Recorder>`.
493pub trait RecorderControllerExt {
494    /// Progresses by the specified time.
495    fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError>;
496}
497
498impl RecorderControllerExt for Controller<Recorder> {
499    fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
500        self.link_mut().tick(tick)
501    }
502}
503
504#[cfg(test)]
505mod tests {
506    use super::*;
507
508    use autd3::prelude::*;
509
510    #[test]
511    fn deref_mut() -> Result<(), Box<dyn std::error::Error>> {
512        let mut autd = Emulator::new([AUTD3::default()]);
513        assert_eq!(1, autd.len());
514        autd.reconfigure(|dev| dev);
515        Ok(())
516    }
517
518    #[test]
519    fn geometry() -> Result<(), Box<dyn std::error::Error>> {
520        let mut autd = Emulator::new([AUTD3::default()]);
521        assert_eq!(1, autd.geometry().len());
522        autd.geometry_mut().reconfigure(|dev| dev);
523        Ok(())
524    }
525}