1use std::f32::consts::PI;
2
3use bvh::aabb::Aabb;
4use derive_more::{Deref, IntoIterator};
5use getset::Getters;
6
7use crate::defined::{METER, ULTRASOUND_FREQ};
8
9use super::{Isometry, Point3, Quaternion, Transducer, UnitQuaternion, UnitVector3, Vector3};
10
11#[derive(Getters, Deref, IntoIterator)]
13pub struct Device {
14 pub(crate) idx: u16,
15 #[deref]
16 #[into_iterator(ref)]
17 pub(crate) transducers: Vec<Transducer>,
18 pub enable: bool,
20 pub sound_speed: f32,
22 #[getset(get = "pub")]
23 rotation: UnitQuaternion,
25 #[getset(get = "pub")]
26 center: Point3,
28 #[getset(get = "pub")]
29 x_direction: UnitVector3,
31 #[getset(get = "pub")]
32 y_direction: UnitVector3,
34 #[getset(get = "pub")]
35 axial_direction: UnitVector3,
37 #[doc(hidden)]
38 #[getset(get = "pub")]
39 inv: Isometry,
40 #[getset(get = "pub")]
41 aabb: Aabb<f32, 3>,
43}
44
45impl Device {
46 fn init(&mut self) {
47 self.center = Point3::from(
48 self.transducers
49 .iter()
50 .map(|tr| tr.position().coords)
51 .sum::<Vector3>()
52 / self.transducers.len() as f32,
53 );
54 self.x_direction = Self::get_direction(Vector3::x(), &self.rotation);
55 self.y_direction = Self::get_direction(Vector3::y(), &self.rotation);
56 self.axial_direction = if cfg!(feature = "left_handed") {
57 Self::get_direction(-Vector3::z(), &self.rotation) } else {
59 Self::get_direction(Vector3::z(), &self.rotation)
60 };
61 self.inv = (nalgebra::Translation3::<f32>::from(*self.transducers[0].position())
62 * self.rotation)
63 .inverse();
64 self.aabb = self
65 .transducers
66 .iter()
67 .fold(Aabb::empty(), |aabb, tr| aabb.grow(tr.position()));
68 }
69
70 #[doc(hidden)]
71 #[must_use]
72 pub fn new(rot: UnitQuaternion, transducers: Vec<Transducer>) -> Self {
73 let mut transducers = transducers;
74 transducers.iter_mut().enumerate().for_each(|(tr_idx, tr)| {
75 tr.idx = tr_idx as _;
76 });
77 let mut dev = Self {
78 idx: 0,
79 transducers,
80 enable: true,
81 sound_speed: 340.0 * METER,
82 rotation: rot,
83 center: Point3::origin(),
84 x_direction: Vector3::x_axis(),
85 y_direction: Vector3::y_axis(),
86 axial_direction: Vector3::z_axis(),
87 inv: nalgebra::Isometry3::identity(),
88 aabb: Aabb::empty(),
89 };
90 dev.init();
91 dev
92 }
93
94 #[must_use]
96 pub const fn idx(&self) -> usize {
97 self.idx as _
98 }
99
100 #[must_use]
102 pub fn num_transducers(&self) -> usize {
103 self.transducers.len()
104 }
105
106 pub fn set_sound_speed_from_temp(&mut self, temp: f32) {
110 self.set_sound_speed_from_temp_with(temp, 1.4, 8.314_463, 28.9647e-3);
111 }
112
113 pub fn set_sound_speed_from_temp_with(&mut self, temp: f32, k: f32, r: f32, m: f32) {
115 self.sound_speed = (k * r * (273.15 + temp) / m).sqrt() * METER;
116 }
117
118 #[must_use]
120 pub const fn wavelength(&self) -> f32 {
121 self.sound_speed / ULTRASOUND_FREQ.hz() as f32
122 }
123
124 #[must_use]
126 pub const fn wavenumber(&self) -> f32 {
127 2.0 * PI * ULTRASOUND_FREQ.hz() as f32 / self.sound_speed
128 }
129
130 #[must_use]
131 fn get_direction(dir: Vector3, rotation: &UnitQuaternion) -> UnitVector3 {
132 let dir: UnitQuaternion = UnitQuaternion::from_quaternion(Quaternion::from_imag(dir));
133 UnitVector3::new_normalize((rotation * dir * rotation.conjugate()).imag())
134 }
135}
136
137#[cfg(test)]
138pub(crate) mod tests {
139 use super::*;
140 use crate::{
141 defined::{PI, mm},
142 geometry::tests::{TestDevice, create_device},
143 };
144
145 macro_rules! assert_approx_eq_vec3 {
146 ($a:expr, $b:expr) => {
147 approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3);
148 approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3);
149 approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3);
150 };
151 }
152
153 #[test]
154 fn idx() {
155 assert_eq!(0, create_device(249).idx());
156 }
157
158 #[rstest::rstest]
159 #[test]
160 #[case(1)]
161 #[case(249)]
162 fn num_transducers(#[case] n: u8) {
163 assert_eq!(n, create_device(n).num_transducers() as u8);
164 }
165
166 #[test]
167 fn center() {
168 let device: Device = TestDevice::new_autd3(Point3::origin()).into();
169 let expected =
170 device.iter().map(|t| t.position().coords).sum::<Vector3>() / device.len() as f32;
171 assert_approx_eq_vec3!(expected, device.center());
172 }
173
174 #[rstest::rstest]
175 #[test]
176 #[case(
177 Vector3::new(10., 20., 30.),
178 Vector3::new(10., 20., 30.),
179 Point3::origin(),
180 UnitQuaternion::identity()
181 )]
182 #[case(
183 Vector3::zeros(),
184 Vector3::new(10., 20., 30.),
185 Point3::new(10., 20., 30.),
186 UnitQuaternion::identity()
187 )]
188 #[case(
189 Vector3::new(20., -10., 30.),
190 Vector3::new(10., 20., 30.),
191 Point3::origin(),
192 UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.)
193 )]
194 #[case(
195 Vector3::new(30., 30., -30.),
196 Vector3::new(40., 50., 60.),
197 Point3::new(10., 20., 30.),
198 UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.)
199 )]
200 fn inv(
201 #[case] expected: Vector3,
202 #[case] target: Vector3,
203 #[case] origin: Point3,
204 #[case] rot: UnitQuaternion,
205 ) {
206 let device: Device = TestDevice::new_autd3_with_rot(origin, rot).into();
207 assert_approx_eq_vec3!(expected, device.inv.transform_point(&Point3::from(target)));
208 }
209
210 #[rstest::rstest]
211 #[test]
212 #[case(340.29525e3, 15.)]
213 #[case(343.23497e3, 20.)]
214 #[case(349.04013e3, 30.)]
215 fn set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) {
216 let mut device = create_device(249);
217 device.set_sound_speed_from_temp(temp);
218 approx::assert_abs_diff_eq!(expected * mm, device.sound_speed, epsilon = 1e-3);
219 }
220
221 #[rstest::rstest]
222 #[test]
223 #[case(8.5, 340e3)]
224 #[case(10., 400e3)]
225 fn wavelength(#[case] expect: f32, #[case] c: f32) {
226 let mut device = create_device(249);
227 device.sound_speed = c;
228 approx::assert_abs_diff_eq!(expect, device.wavelength());
229 }
230
231 #[rstest::rstest]
232 #[test]
233 #[case(0.739_198_27, 340e3)]
234 #[case(0.628_318_55, 400e3)]
235 fn wavenumber(#[case] expect: f32, #[case] c: f32) {
236 let mut device = create_device(249);
237 device.sound_speed = c;
238 approx::assert_abs_diff_eq!(expect, device.wavenumber());
239 }
240}