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 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
67pub 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 pub fn tick(&mut self, tick: Duration) -> Result<(), EmulatorError> {
178 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#[derive(Getters, Deref, DerefMut)]
221pub struct Emulator {
222 #[getset(get = "pub")]
223 #[deref]
224 #[deref_mut]
225 geometry: Geometry,
227}
228
229impl Emulator {
230 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 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 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 #[doc(hidden)]
326 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 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 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
425pub trait RecorderControllerExt {
427 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}