1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(missing_docs)]
3#![warn(rustdoc::missing_crate_level_docs)]
4#![warn(rustdoc::unescaped_backticks)]
5
6mod 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) {} }
61
62pub 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 fn close(&mut self) -> Result<(), LinkError> {
98 self.is_open = false;
99 Ok(())
100 }
101 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 pub fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
204 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
259pub struct Emulator {
261 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 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 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 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 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 #[doc(hidden)]
392 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 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 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
492pub trait RecorderControllerExt {
494 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}