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