autd3_core/devices/
autd3.rs

1use crate::{
2    common::mm,
3    geometry::{Device, Isometry3, Point3, Transducer, Translation3, UnitQuaternion},
4};
5use std::fmt::Debug;
6
7/// AUTD3 device.
8#[derive(Clone, Copy, Debug)]
9pub struct AUTD3<R: Into<UnitQuaternion> + Debug> {
10    /// The global position of the AUTD3 device.
11    pub pos: Point3,
12    /// The rotation of the AUTD3 device.
13    pub rot: R,
14}
15
16impl<R: Into<UnitQuaternion> + Debug> AUTD3<R> {
17    /// Create a new [`AUTD3`].
18    #[must_use]
19    pub fn new(pos: Point3, rot: R) -> Self {
20        Self { pos, rot }
21    }
22}
23
24impl Default for AUTD3<UnitQuaternion> {
25    fn default() -> Self {
26        Self {
27            pos: Point3::origin(),
28            rot: UnitQuaternion::identity(),
29        }
30    }
31}
32
33impl AUTD3<UnitQuaternion> {
34    /// The number of transducers in x-axis.
35    pub const NUM_TRANS_X: usize = 18;
36    /// The number of transducers in y-axis.
37    pub const NUM_TRANS_Y: usize = 14;
38    /// The number of transducers in a unit.
39    pub const NUM_TRANS_IN_UNIT: usize = Self::NUM_TRANS_X * Self::NUM_TRANS_Y - 3;
40    /// The spacing between transducers.
41    pub const TRANS_SPACING: f32 = 10.16 * mm;
42    /// The width of the device (including the substrate).
43    pub const DEVICE_WIDTH: f32 = 192.0 * mm;
44    /// The height of the device (including the substrate).
45    pub const DEVICE_HEIGHT: f32 = 151.4 * mm;
46
47    /// Gets the index in x- and y-axis from the transducer index.
48    #[must_use]
49    pub const fn grid_id(idx: usize) -> (usize, usize) {
50        let local_id = idx % Self::NUM_TRANS_IN_UNIT;
51        let uid = match local_id {
52            0..19 => local_id,
53            19..32 => local_id + 2,
54            _ => local_id + 3,
55        };
56        (uid % Self::NUM_TRANS_X, uid / Self::NUM_TRANS_X)
57    }
58}
59
60impl AUTD3<UnitQuaternion> {
61    #[must_use]
62    const fn is_missing_transducer(x: usize, y: usize) -> bool {
63        y == 1 && (x == 1 || x == 2 || x == 16)
64    }
65}
66
67impl<R: Into<UnitQuaternion> + Debug> From<AUTD3<R>> for Device {
68    fn from(autd3: AUTD3<R>) -> Self {
69        let rotation = autd3.rot.into();
70        let isometry = Isometry3 {
71            rotation,
72            translation: Translation3::from(autd3.pos),
73        };
74        Self::new(
75            rotation,
76            (0..AUTD3::NUM_TRANS_Y)
77                .flat_map(|y| {
78                    (0..AUTD3::NUM_TRANS_X)
79                        .filter(move |&x| !AUTD3::is_missing_transducer(x, y))
80                        .map(move |x| {
81                            isometry
82                                * Point3::new(
83                                    x as f32 * AUTD3::TRANS_SPACING,
84                                    y as f32 * AUTD3::TRANS_SPACING,
85                                    0.,
86                                )
87                        })
88                })
89                .map(Transducer::new)
90                .collect(),
91        )
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    use crate::geometry::Vector3;
100
101    #[test]
102    fn num_devices() {
103        let dev: Device = AUTD3::default().into();
104        assert_eq!(AUTD3::NUM_TRANS_IN_UNIT, dev.num_transducers());
105    }
106
107    #[rstest::rstest]
108    #[case(
109        Point3::new(0., 0., 0.),
110        0,
111        Point3::origin(),
112        UnitQuaternion::identity()
113    )]
114    #[case(
115        Point3::new(AUTD3::TRANS_SPACING, 0., 0.),
116        1,
117        Point3::origin(),
118        UnitQuaternion::identity()
119    )]
120    #[case(
121        Point3::new(0., AUTD3::TRANS_SPACING, 0.),
122        18,
123        Point3::origin(),
124        UnitQuaternion::identity()
125    )]
126    #[case(Point3::new(17. * AUTD3::TRANS_SPACING, 13. * AUTD3::TRANS_SPACING, 0.), 248, Point3::origin(), UnitQuaternion::identity())]
127    #[case(
128        Point3::new(1., 2., 3.),
129        0,
130        Point3::new(1., 2., 3.),
131        UnitQuaternion::identity()
132    )]
133    #[case(
134        Point3::new(AUTD3::TRANS_SPACING + 1., 2., 3.),
135        1,
136        Point3::new(1., 2., 3.),
137        UnitQuaternion::identity()
138    )]
139    #[case(
140        Point3::new(1., AUTD3::TRANS_SPACING + 2., 3.),
141        18,
142        Point3::new(1., 2., 3.),
143        UnitQuaternion::identity()
144    )]
145    #[case(Point3::new(17. * AUTD3::TRANS_SPACING + 1., 13. * AUTD3::TRANS_SPACING + 2., 3.), 248, Point3::new(1., 2., 3.), UnitQuaternion::identity())]
146    #[case(
147        Point3::new(0., 0., 0.),
148        0,
149        Point3::origin(),
150        UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2)
151    )]
152    #[case(
153        Point3::new(0., 0., -AUTD3::TRANS_SPACING),
154        1,
155        Point3::origin(),
156        UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2)
157    )]
158    #[case(
159        Point3::new(0., AUTD3::TRANS_SPACING, 0.),
160        18,
161        Point3::origin(),
162        UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2)
163    )]
164    #[case(Point3::new(0., 13. * AUTD3::TRANS_SPACING, -17. * AUTD3::TRANS_SPACING), 248, Point3::origin(), UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2))]
165    #[case(
166        Point3::new(1., 2., 3.),
167        0,
168        Point3::new(1., 2., 3.),
169        UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2)
170    )]
171    #[case(
172        Point3::new(1., 2., 3. - AUTD3::TRANS_SPACING),
173        1,
174        Point3::new(1., 2., 3.),
175        UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2)
176    )]
177    #[case(
178        Point3::new(1., 2. + AUTD3::TRANS_SPACING, 3.),
179        18,
180        Point3::new(1., 2., 3.),
181        UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2)
182    )]
183    #[case(Point3::new(1., 2. + 13. * AUTD3::TRANS_SPACING, 3. - 17. * AUTD3::TRANS_SPACING), 248, Point3::new(1., 2., 3.), UnitQuaternion::new(Vector3::y() * std::f32::consts::FRAC_PI_2))]
184    fn position(
185        #[case] expected: Point3,
186        #[case] idx: usize,
187        #[case] pos: Point3,
188        #[case] rot: impl Into<UnitQuaternion> + Debug,
189    ) {
190        let dev: Device = AUTD3 { pos, rot }.into();
191        approx::assert_relative_eq!(expected.x, dev[idx].position().x, epsilon = 1e-6);
192        approx::assert_relative_eq!(expected.y, dev[idx].position().y, epsilon = 1e-6);
193        approx::assert_relative_eq!(expected.z, dev[idx].position().z, epsilon = 1e-6);
194    }
195
196    #[test]
197    fn is_missing_transducer() {
198        assert!((0..AUTD3::NUM_TRANS_X).all(|x| !AUTD3::is_missing_transducer(x, 0)));
199
200        assert!(!AUTD3::is_missing_transducer(0, 1));
201        assert!(AUTD3::is_missing_transducer(1, 1));
202        assert!(AUTD3::is_missing_transducer(2, 1));
203        assert!((3..16).all(|x| !AUTD3::is_missing_transducer(x, 1)));
204        assert!(AUTD3::is_missing_transducer(16, 1));
205        assert!(!AUTD3::is_missing_transducer(17, 1));
206
207        assert!(
208            (2..AUTD3::NUM_TRANS_Y)
209                .all(|y| { (0..AUTD3::NUM_TRANS_X).all(|x| !AUTD3::is_missing_transducer(x, y)) })
210        );
211    }
212
213    #[rstest::rstest]
214    #[case(0, (0, 0))]
215    #[case(1, (1, 0))]
216    #[case(2, (2, 0))]
217    #[case(3, (3, 0))]
218    #[case(4, (4, 0))]
219    #[case(5, (5, 0))]
220    #[case(6, (6, 0))]
221    #[case(7, (7, 0))]
222    #[case(8, (8, 0))]
223    #[case(9, (9, 0))]
224    #[case(10, (10, 0))]
225    #[case(11, (11, 0))]
226    #[case(12, (12, 0))]
227    #[case(13, (13, 0))]
228    #[case(14, (14, 0))]
229    #[case(15, (15, 0))]
230    #[case(16, (16, 0))]
231    #[case(17, (17, 0))]
232    #[case(18, (0, 1))]
233    #[case(19, (3, 1))]
234    #[case(20, (4, 1))]
235    #[case(21, (5, 1))]
236    #[case(22, (6, 1))]
237    #[case(23, (7, 1))]
238    #[case(24, (8, 1))]
239    #[case(25, (9, 1))]
240    #[case(26, (10, 1))]
241    #[case(27, (11, 1))]
242    #[case(28, (12, 1))]
243    #[case(29, (13, 1))]
244    #[case(30, (14, 1))]
245    #[case(31, (15, 1))]
246    #[case(32, (17, 1))]
247    #[case(33, (0, 2))]
248    #[case(34, (1, 2))]
249    #[case(35, (2, 2))]
250    #[case(36, (3, 2))]
251    #[case(37, (4, 2))]
252    #[case(38, (5, 2))]
253    #[case(39, (6, 2))]
254    #[case(40, (7, 2))]
255    #[case(41, (8, 2))]
256    #[case(42, (9, 2))]
257    #[case(43, (10, 2))]
258    #[case(44, (11, 2))]
259    #[case(45, (12, 2))]
260    #[case(46, (13, 2))]
261    #[case(47, (14, 2))]
262    #[case(48, (15, 2))]
263    #[case(49, (16, 2))]
264    #[case(50, (17, 2))]
265    #[case(51, (0, 3))]
266    #[case(52, (1, 3))]
267    #[case(53, (2, 3))]
268    #[case(54, (3, 3))]
269    #[case(55, (4, 3))]
270    #[case(56, (5, 3))]
271    #[case(57, (6, 3))]
272    #[case(58, (7, 3))]
273    #[case(59, (8, 3))]
274    #[case(60, (9, 3))]
275    #[case(61, (10, 3))]
276    #[case(62, (11, 3))]
277    #[case(63, (12, 3))]
278    #[case(64, (13, 3))]
279    #[case(65, (14, 3))]
280    #[case(66, (15, 3))]
281    #[case(67, (16, 3))]
282    #[case(68, (17, 3))]
283    #[case(69, (0, 4))]
284    #[case(70, (1, 4))]
285    #[case(71, (2, 4))]
286    #[case(72, (3, 4))]
287    #[case(73, (4, 4))]
288    #[case(74, (5, 4))]
289    #[case(75, (6, 4))]
290    #[case(76, (7, 4))]
291    #[case(77, (8, 4))]
292    #[case(78, (9, 4))]
293    #[case(79, (10, 4))]
294    #[case(80, (11, 4))]
295    #[case(81, (12, 4))]
296    #[case(82, (13, 4))]
297    #[case(83, (14, 4))]
298    #[case(84, (15, 4))]
299    #[case(85, (16, 4))]
300    #[case(86, (17, 4))]
301    #[case(87, (0, 5))]
302    #[case(88, (1, 5))]
303    #[case(89, (2, 5))]
304    #[case(90, (3, 5))]
305    #[case(91, (4, 5))]
306    #[case(92, (5, 5))]
307    #[case(93, (6, 5))]
308    #[case(94, (7, 5))]
309    #[case(95, (8, 5))]
310    #[case(96, (9, 5))]
311    #[case(97, (10, 5))]
312    #[case(98, (11, 5))]
313    #[case(99, (12, 5))]
314    #[case(100, (13, 5))]
315    #[case(101, (14, 5))]
316    #[case(102, (15, 5))]
317    #[case(103, (16, 5))]
318    #[case(104, (17, 5))]
319    #[case(105, (0, 6))]
320    #[case(106, (1, 6))]
321    #[case(107, (2, 6))]
322    #[case(108, (3, 6))]
323    #[case(109, (4, 6))]
324    #[case(110, (5, 6))]
325    #[case(111, (6, 6))]
326    #[case(112, (7, 6))]
327    #[case(113, (8, 6))]
328    #[case(114, (9, 6))]
329    #[case(115, (10, 6))]
330    #[case(116, (11, 6))]
331    #[case(117, (12, 6))]
332    #[case(118, (13, 6))]
333    #[case(119, (14, 6))]
334    #[case(120, (15, 6))]
335    #[case(121, (16, 6))]
336    #[case(122, (17, 6))]
337    #[case(123, (0, 7))]
338    #[case(124, (1, 7))]
339    #[case(125, (2, 7))]
340    #[case(126, (3, 7))]
341    #[case(127, (4, 7))]
342    #[case(128, (5, 7))]
343    #[case(129, (6, 7))]
344    #[case(130, (7, 7))]
345    #[case(131, (8, 7))]
346    #[case(132, (9, 7))]
347    #[case(133, (10, 7))]
348    #[case(134, (11, 7))]
349    #[case(135, (12, 7))]
350    #[case(136, (13, 7))]
351    #[case(137, (14, 7))]
352    #[case(138, (15, 7))]
353    #[case(139, (16, 7))]
354    #[case(140, (17, 7))]
355    #[case(141, (0, 8))]
356    #[case(142, (1, 8))]
357    #[case(143, (2, 8))]
358    #[case(144, (3, 8))]
359    #[case(145, (4, 8))]
360    #[case(146, (5, 8))]
361    #[case(147, (6, 8))]
362    #[case(148, (7, 8))]
363    #[case(149, (8, 8))]
364    #[case(150, (9, 8))]
365    #[case(151, (10, 8))]
366    #[case(152, (11, 8))]
367    #[case(153, (12, 8))]
368    #[case(154, (13, 8))]
369    #[case(155, (14, 8))]
370    #[case(156, (15, 8))]
371    #[case(157, (16, 8))]
372    #[case(158, (17, 8))]
373    #[case(159, (0, 9))]
374    #[case(160, (1, 9))]
375    #[case(161, (2, 9))]
376    #[case(162, (3, 9))]
377    #[case(163, (4, 9))]
378    #[case(164, (5, 9))]
379    #[case(165, (6, 9))]
380    #[case(166, (7, 9))]
381    #[case(167, (8, 9))]
382    #[case(168, (9, 9))]
383    #[case(169, (10, 9))]
384    #[case(170, (11, 9))]
385    #[case(171, (12, 9))]
386    #[case(172, (13, 9))]
387    #[case(173, (14, 9))]
388    #[case(174, (15, 9))]
389    #[case(175, (16, 9))]
390    #[case(176, (17, 9))]
391    #[case(177, (0, 10))]
392    #[case(178, (1, 10))]
393    #[case(179, (2, 10))]
394    #[case(180, (3, 10))]
395    #[case(181, (4, 10))]
396    #[case(182, (5, 10))]
397    #[case(183, (6, 10))]
398    #[case(184, (7, 10))]
399    #[case(185, (8, 10))]
400    #[case(186, (9, 10))]
401    #[case(187, (10, 10))]
402    #[case(188, (11, 10))]
403    #[case(189, (12, 10))]
404    #[case(190, (13, 10))]
405    #[case(191, (14, 10))]
406    #[case(192, (15, 10))]
407    #[case(193, (16, 10))]
408    #[case(194, (17, 10))]
409    #[case(195, (0, 11))]
410    #[case(196, (1, 11))]
411    #[case(197, (2, 11))]
412    #[case(198, (3, 11))]
413    #[case(199, (4, 11))]
414    #[case(200, (5, 11))]
415    #[case(201, (6, 11))]
416    #[case(202, (7, 11))]
417    #[case(203, (8, 11))]
418    #[case(204, (9, 11))]
419    #[case(205, (10, 11))]
420    #[case(206, (11, 11))]
421    #[case(207, (12, 11))]
422    #[case(208, (13, 11))]
423    #[case(209, (14, 11))]
424    #[case(210, (15, 11))]
425    #[case(211, (16, 11))]
426    #[case(212, (17, 11))]
427    #[case(213, (0, 12))]
428    #[case(214, (1, 12))]
429    #[case(215, (2, 12))]
430    #[case(216, (3, 12))]
431    #[case(217, (4, 12))]
432    #[case(218, (5, 12))]
433    #[case(219, (6, 12))]
434    #[case(220, (7, 12))]
435    #[case(221, (8, 12))]
436    #[case(222, (9, 12))]
437    #[case(223, (10, 12))]
438    #[case(224, (11, 12))]
439    #[case(225, (12, 12))]
440    #[case(226, (13, 12))]
441    #[case(227, (14, 12))]
442    #[case(228, (15, 12))]
443    #[case(229, (16, 12))]
444    #[case(230, (17, 12))]
445    #[case(231, (0, 13))]
446    #[case(232, (1, 13))]
447    #[case(233, (2, 13))]
448    #[case(234, (3, 13))]
449    #[case(235, (4, 13))]
450    #[case(236, (5, 13))]
451    #[case(237, (6, 13))]
452    #[case(238, (7, 13))]
453    #[case(239, (8, 13))]
454    #[case(240, (9, 13))]
455    #[case(241, (10, 13))]
456    #[case(242, (11, 13))]
457    #[case(243, (12, 13))]
458    #[case(244, (13, 13))]
459    #[case(245, (14, 13))]
460    #[case(246, (15, 13))]
461    #[case(247, (16, 13))]
462    #[case(248, (17, 13))]
463    fn grid_id(#[case] idx: usize, #[case] expected: (usize, usize)) {
464        assert_eq!(expected, AUTD3::grid_id(idx));
465    }
466}