1use crate::{
2 common::mm,
3 geometry::{Device, Isometry3, Point3, Transducer, Translation3, UnitQuaternion},
4};
5use std::fmt::Debug;
6
7#[derive(Clone, Copy, Debug)]
9pub struct AUTD3<R: Into<UnitQuaternion> + Debug> {
10 pub pos: Point3,
12 pub rot: R,
14}
15
16impl<R: Into<UnitQuaternion> + Debug> AUTD3<R> {
17 #[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 pub const NUM_TRANS_X: usize = 18;
36 pub const NUM_TRANS_Y: usize = 14;
38 pub const NUM_TRANS_IN_UNIT: usize = Self::NUM_TRANS_X * Self::NUM_TRANS_Y - 3;
40 pub const TRANS_SPACING: f32 = 10.16 * mm;
42 pub const DEVICE_WIDTH: f32 = 192.0 * mm;
44 pub const DEVICE_HEIGHT: f32 = 151.4 * mm;
46
47 #[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}