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
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
65pub 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 fn close(&mut self) -> Result<(), LinkError> {
95 self.is_open = false;
96 Ok(())
97 }
98 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 pub fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
185 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#[derive(Getters, Deref, DerefMut)]
228pub struct Emulator {
229 #[getset(get = "pub")]
230 #[deref]
231 #[deref_mut]
232 geometry: Geometry,
234}
235
236impl Emulator {
237 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 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 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 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 #[doc(hidden)]
342 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 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 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
449pub trait RecorderControllerExt {
451 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}