1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2023 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker
15*cf78ab8cSAndroid Build Coastguard Worker //! Ranging library
16*cf78ab8cSAndroid Build Coastguard Worker
17*cf78ab8cSAndroid Build Coastguard Worker use glam::{EulerRot, Quat, Vec3};
18*cf78ab8cSAndroid Build Coastguard Worker
19*cf78ab8cSAndroid Build Coastguard Worker /// The Free Space Path Loss (FSPL) model is considered as the standard
20*cf78ab8cSAndroid Build Coastguard Worker /// under the ideal scenario.
21*cf78ab8cSAndroid Build Coastguard Worker
22*cf78ab8cSAndroid Build Coastguard Worker /// (dBm) PATH_LOSS at 1m for isotropic antenna transmitting BLE.
23*cf78ab8cSAndroid Build Coastguard Worker const PATH_LOSS_AT_1M: f32 = 40.20;
24*cf78ab8cSAndroid Build Coastguard Worker
25*cf78ab8cSAndroid Build Coastguard Worker /// Convert distance to RSSI using the free space path loss equation.
26*cf78ab8cSAndroid Build Coastguard Worker /// See [Free-space_path_loss][1].
27*cf78ab8cSAndroid Build Coastguard Worker ///
28*cf78ab8cSAndroid Build Coastguard Worker /// [1]: http://en.wikipedia.org/wiki/Free-space_path_loss
29*cf78ab8cSAndroid Build Coastguard Worker ///
30*cf78ab8cSAndroid Build Coastguard Worker /// # Parameters
31*cf78ab8cSAndroid Build Coastguard Worker ///
32*cf78ab8cSAndroid Build Coastguard Worker /// * `distance`: distance in meters (m).
33*cf78ab8cSAndroid Build Coastguard Worker /// * `tx_power`: transmitted power (dBm) calibrated to 1 meter.
34*cf78ab8cSAndroid Build Coastguard Worker ///
35*cf78ab8cSAndroid Build Coastguard Worker /// # Returns
36*cf78ab8cSAndroid Build Coastguard Worker ///
37*cf78ab8cSAndroid Build Coastguard Worker /// The rssi that would be measured at that distance, in the
38*cf78ab8cSAndroid Build Coastguard Worker /// range -120..20 dBm,
distance_to_rssi(tx_power: i8, distance: f32) -> i839*cf78ab8cSAndroid Build Coastguard Worker pub fn distance_to_rssi(tx_power: i8, distance: f32) -> i8 {
40*cf78ab8cSAndroid Build Coastguard Worker // TODO(b/285634913)
41*cf78ab8cSAndroid Build Coastguard Worker // Rootcanal reporting tx_power of 0 or 1 during Nearby Share
42*cf78ab8cSAndroid Build Coastguard Worker let new_tx_power = match tx_power {
43*cf78ab8cSAndroid Build Coastguard Worker 0 | 1 => -49,
44*cf78ab8cSAndroid Build Coastguard Worker _ => tx_power,
45*cf78ab8cSAndroid Build Coastguard Worker };
46*cf78ab8cSAndroid Build Coastguard Worker match distance == 0.0 {
47*cf78ab8cSAndroid Build Coastguard Worker true => (new_tx_power as f32 + PATH_LOSS_AT_1M).clamp(-120.0, 20.0) as i8,
48*cf78ab8cSAndroid Build Coastguard Worker false => (new_tx_power as f32 - 20.0 * distance.log10()).clamp(-120.0, 20.0) as i8,
49*cf78ab8cSAndroid Build Coastguard Worker }
50*cf78ab8cSAndroid Build Coastguard Worker }
51*cf78ab8cSAndroid Build Coastguard Worker
52*cf78ab8cSAndroid Build Coastguard Worker // helper function for performing division with
53*cf78ab8cSAndroid Build Coastguard Worker // zero division check
54*cf78ab8cSAndroid Build Coastguard Worker #[allow(unused)]
checked_div(num: f32, den: f32) -> Option<f32>55*cf78ab8cSAndroid Build Coastguard Worker fn checked_div(num: f32, den: f32) -> Option<f32> {
56*cf78ab8cSAndroid Build Coastguard Worker (den != 0.).then_some(num / den)
57*cf78ab8cSAndroid Build Coastguard Worker }
58*cf78ab8cSAndroid Build Coastguard Worker
59*cf78ab8cSAndroid Build Coastguard Worker // helper function for calculating azimuth angle
60*cf78ab8cSAndroid Build Coastguard Worker // from a given 3D delta vector.
61*cf78ab8cSAndroid Build Coastguard Worker #[allow(unused)]
azimuth(delta: Vec3) -> f3262*cf78ab8cSAndroid Build Coastguard Worker fn azimuth(delta: Vec3) -> f32 {
63*cf78ab8cSAndroid Build Coastguard Worker checked_div(delta.x, delta.z).map_or(
64*cf78ab8cSAndroid Build Coastguard Worker match delta.x == 0. {
65*cf78ab8cSAndroid Build Coastguard Worker true => 0.,
66*cf78ab8cSAndroid Build Coastguard Worker false => delta.x.signum() * std::f32::consts::FRAC_2_PI,
67*cf78ab8cSAndroid Build Coastguard Worker },
68*cf78ab8cSAndroid Build Coastguard Worker f32::atan,
69*cf78ab8cSAndroid Build Coastguard Worker ) + if delta.z >= 0. { 0. } else { delta.x.signum() * std::f32::consts::PI }
70*cf78ab8cSAndroid Build Coastguard Worker }
71*cf78ab8cSAndroid Build Coastguard Worker
72*cf78ab8cSAndroid Build Coastguard Worker // helper function for calculating elevation angle
73*cf78ab8cSAndroid Build Coastguard Worker // from a given 3D delta vector.
74*cf78ab8cSAndroid Build Coastguard Worker #[allow(unused)]
elevation(delta: Vec3) -> f3275*cf78ab8cSAndroid Build Coastguard Worker fn elevation(delta: Vec3) -> f32 {
76*cf78ab8cSAndroid Build Coastguard Worker checked_div(delta.y, f32::sqrt(delta.x.powi(2) + delta.z.powi(2)))
77*cf78ab8cSAndroid Build Coastguard Worker .map_or(delta.y.signum() * std::f32::consts::FRAC_PI_2, f32::atan)
78*cf78ab8cSAndroid Build Coastguard Worker }
79*cf78ab8cSAndroid Build Coastguard Worker
80*cf78ab8cSAndroid Build Coastguard Worker /// Pose struct
81*cf78ab8cSAndroid Build Coastguard Worker ///
82*cf78ab8cSAndroid Build Coastguard Worker /// This struct allows for a mathematical representation of
83*cf78ab8cSAndroid Build Coastguard Worker /// position and orientation values from the protobufs, which
84*cf78ab8cSAndroid Build Coastguard Worker /// would enable to compute range, azimuth, and elevation.
85*cf78ab8cSAndroid Build Coastguard Worker #[allow(unused)]
86*cf78ab8cSAndroid Build Coastguard Worker pub struct Pose {
87*cf78ab8cSAndroid Build Coastguard Worker position: Vec3,
88*cf78ab8cSAndroid Build Coastguard Worker orientation: Quat,
89*cf78ab8cSAndroid Build Coastguard Worker }
90*cf78ab8cSAndroid Build Coastguard Worker
91*cf78ab8cSAndroid Build Coastguard Worker impl Pose {
92*cf78ab8cSAndroid Build Coastguard Worker #[allow(unused)]
new(x: f32, y: f32, z: f32, yaw: f32, pitch: f32, roll: f32) -> Self93*cf78ab8cSAndroid Build Coastguard Worker pub fn new(x: f32, y: f32, z: f32, yaw: f32, pitch: f32, roll: f32) -> Self {
94*cf78ab8cSAndroid Build Coastguard Worker Pose {
95*cf78ab8cSAndroid Build Coastguard Worker // Converts x, y, z from meters to centimeters
96*cf78ab8cSAndroid Build Coastguard Worker position: Vec3::new(x * 100., y * 100., z * 100.),
97*cf78ab8cSAndroid Build Coastguard Worker // Converts roll, pitch, yaw from degrees to radians
98*cf78ab8cSAndroid Build Coastguard Worker orientation: Quat::from_euler(
99*cf78ab8cSAndroid Build Coastguard Worker EulerRot::ZXY,
100*cf78ab8cSAndroid Build Coastguard Worker roll.to_radians(),
101*cf78ab8cSAndroid Build Coastguard Worker pitch.to_radians(),
102*cf78ab8cSAndroid Build Coastguard Worker yaw.to_radians(),
103*cf78ab8cSAndroid Build Coastguard Worker ),
104*cf78ab8cSAndroid Build Coastguard Worker }
105*cf78ab8cSAndroid Build Coastguard Worker }
106*cf78ab8cSAndroid Build Coastguard Worker }
107*cf78ab8cSAndroid Build Coastguard Worker
108*cf78ab8cSAndroid Build Coastguard Worker /// UWB Ranging Model for computing range, azimuth, and elevation
109*cf78ab8cSAndroid Build Coastguard Worker /// The raning model brought from https://github.com/google/pica
110*cf78ab8cSAndroid Build Coastguard Worker #[allow(unused)]
compute_range_azimuth_elevation(a: &Pose, b: &Pose) -> anyhow::Result<(f32, i16, i8)>111*cf78ab8cSAndroid Build Coastguard Worker pub fn compute_range_azimuth_elevation(a: &Pose, b: &Pose) -> anyhow::Result<(f32, i16, i8)> {
112*cf78ab8cSAndroid Build Coastguard Worker let delta = b.position - a.position;
113*cf78ab8cSAndroid Build Coastguard Worker let distance = delta.length().clamp(0.0, u16::MAX as f32);
114*cf78ab8cSAndroid Build Coastguard Worker let direction = a.orientation.mul_vec3(delta);
115*cf78ab8cSAndroid Build Coastguard Worker let azimuth = azimuth(direction).to_degrees().round();
116*cf78ab8cSAndroid Build Coastguard Worker let elevation = elevation(direction).to_degrees().round();
117*cf78ab8cSAndroid Build Coastguard Worker
118*cf78ab8cSAndroid Build Coastguard Worker if !(-180. ..=180.).contains(&azimuth) {
119*cf78ab8cSAndroid Build Coastguard Worker return Err(anyhow::anyhow!("azimuth is not between -180 and 180. value: {azimuth}"));
120*cf78ab8cSAndroid Build Coastguard Worker }
121*cf78ab8cSAndroid Build Coastguard Worker if !(-90. ..=90.).contains(&elevation) {
122*cf78ab8cSAndroid Build Coastguard Worker return Err(anyhow::anyhow!("elevation is not between -90 and 90. value: {elevation}"));
123*cf78ab8cSAndroid Build Coastguard Worker }
124*cf78ab8cSAndroid Build Coastguard Worker Ok((distance, azimuth as i16, elevation as i8))
125*cf78ab8cSAndroid Build Coastguard Worker }
126*cf78ab8cSAndroid Build Coastguard Worker
127*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)]
128*cf78ab8cSAndroid Build Coastguard Worker mod tests {
129*cf78ab8cSAndroid Build Coastguard Worker use super::*;
130*cf78ab8cSAndroid Build Coastguard Worker
131*cf78ab8cSAndroid Build Coastguard Worker #[test]
rssi_at_0m()132*cf78ab8cSAndroid Build Coastguard Worker fn rssi_at_0m() {
133*cf78ab8cSAndroid Build Coastguard Worker let rssi_at_0m = distance_to_rssi(-120, 0.0);
134*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(rssi_at_0m, -79);
135*cf78ab8cSAndroid Build Coastguard Worker }
136*cf78ab8cSAndroid Build Coastguard Worker
137*cf78ab8cSAndroid Build Coastguard Worker #[test]
rssi_at_1m()138*cf78ab8cSAndroid Build Coastguard Worker fn rssi_at_1m() {
139*cf78ab8cSAndroid Build Coastguard Worker // With transmit power at 0 dBm verify a reasonable rssi at 1m
140*cf78ab8cSAndroid Build Coastguard Worker let rssi_at_1m = distance_to_rssi(0, 1.0);
141*cf78ab8cSAndroid Build Coastguard Worker assert!(rssi_at_1m < -35 && rssi_at_1m > -55);
142*cf78ab8cSAndroid Build Coastguard Worker }
143*cf78ab8cSAndroid Build Coastguard Worker
144*cf78ab8cSAndroid Build Coastguard Worker #[test]
rssi_saturate_inf()145*cf78ab8cSAndroid Build Coastguard Worker fn rssi_saturate_inf() {
146*cf78ab8cSAndroid Build Coastguard Worker // Verify that the rssi saturates at -120 for very large distances.
147*cf78ab8cSAndroid Build Coastguard Worker let rssi_inf = distance_to_rssi(-120, 1000.0);
148*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(rssi_inf, -120);
149*cf78ab8cSAndroid Build Coastguard Worker }
150*cf78ab8cSAndroid Build Coastguard Worker
151*cf78ab8cSAndroid Build Coastguard Worker #[test]
rssi_saturate_sup()152*cf78ab8cSAndroid Build Coastguard Worker fn rssi_saturate_sup() {
153*cf78ab8cSAndroid Build Coastguard Worker // Verify that the rssi saturates at +20 for the largest tx power
154*cf78ab8cSAndroid Build Coastguard Worker // and nearest distance.
155*cf78ab8cSAndroid Build Coastguard Worker let rssi_sup = distance_to_rssi(20, 0.0);
156*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(rssi_sup, 20);
157*cf78ab8cSAndroid Build Coastguard Worker }
158*cf78ab8cSAndroid Build Coastguard Worker
159*cf78ab8cSAndroid Build Coastguard Worker #[test]
range()160*cf78ab8cSAndroid Build Coastguard Worker fn range() {
161*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
162*cf78ab8cSAndroid Build Coastguard Worker {
163*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(10.0, 0.0, 0.0, 0.0, 0.0, 0.0);
164*cf78ab8cSAndroid Build Coastguard Worker let (range, _, _) = compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
165*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(range, 1000.);
166*cf78ab8cSAndroid Build Coastguard Worker }
167*cf78ab8cSAndroid Build Coastguard Worker {
168*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(-10.0, 0.0, 0.0, 0.0, 0.0, 0.0);
169*cf78ab8cSAndroid Build Coastguard Worker let (range, _, _) = compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
170*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(range, 1000.);
171*cf78ab8cSAndroid Build Coastguard Worker }
172*cf78ab8cSAndroid Build Coastguard Worker {
173*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(10.0, 10.0, 0.0, 0.0, 0.0, 0.0);
174*cf78ab8cSAndroid Build Coastguard Worker let (range, _, _) = compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
175*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(range, f32::sqrt(2000000.));
176*cf78ab8cSAndroid Build Coastguard Worker }
177*cf78ab8cSAndroid Build Coastguard Worker {
178*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(-10.0, -10.0, -10.0, 0.0, 0.0, 0.0);
179*cf78ab8cSAndroid Build Coastguard Worker let (range, _, _) = compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
180*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(range, f32::sqrt(3000000.));
181*cf78ab8cSAndroid Build Coastguard Worker }
182*cf78ab8cSAndroid Build Coastguard Worker }
183*cf78ab8cSAndroid Build Coastguard Worker
184*cf78ab8cSAndroid Build Coastguard Worker #[test]
azimuth_without_rotation()185*cf78ab8cSAndroid Build Coastguard Worker fn azimuth_without_rotation() {
186*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
187*cf78ab8cSAndroid Build Coastguard Worker {
188*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(10.0, 0.0, 10.0, 0.0, 0.0, 0.0);
189*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
190*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
191*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 45);
192*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
193*cf78ab8cSAndroid Build Coastguard Worker }
194*cf78ab8cSAndroid Build Coastguard Worker {
195*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(-10.0, 0.0, 10.0, 0.0, 0.0, 0.0);
196*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
197*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
198*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, -45);
199*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
200*cf78ab8cSAndroid Build Coastguard Worker }
201*cf78ab8cSAndroid Build Coastguard Worker {
202*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(10.0, 0.0, -10.0, 0.0, 0.0, 0.0);
203*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
204*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
205*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 135);
206*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
207*cf78ab8cSAndroid Build Coastguard Worker }
208*cf78ab8cSAndroid Build Coastguard Worker {
209*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(-10.0, 0.0, -10.0, 0.0, 0.0, 0.0);
210*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
211*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
212*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, -135);
213*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
214*cf78ab8cSAndroid Build Coastguard Worker }
215*cf78ab8cSAndroid Build Coastguard Worker }
216*cf78ab8cSAndroid Build Coastguard Worker
217*cf78ab8cSAndroid Build Coastguard Worker #[test]
elevation_without_rotation()218*cf78ab8cSAndroid Build Coastguard Worker fn elevation_without_rotation() {
219*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
220*cf78ab8cSAndroid Build Coastguard Worker {
221*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(0.0, 10.0, 10.0, 0.0, 0.0, 0.0);
222*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
223*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
224*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 0);
225*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 45);
226*cf78ab8cSAndroid Build Coastguard Worker }
227*cf78ab8cSAndroid Build Coastguard Worker {
228*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(0.0, -10.0, 10.0, 0.0, 0.0, 0.0);
229*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
230*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
231*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 0);
232*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, -45);
233*cf78ab8cSAndroid Build Coastguard Worker }
234*cf78ab8cSAndroid Build Coastguard Worker {
235*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(0.0, 10.0, -10.0, 0.0, 0.0, 0.0);
236*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
237*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
238*cf78ab8cSAndroid Build Coastguard Worker assert!(azimuth == 180 || azimuth == -180);
239*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 45);
240*cf78ab8cSAndroid Build Coastguard Worker }
241*cf78ab8cSAndroid Build Coastguard Worker {
242*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(0.0, -10.0, -10.0, 0.0, 0.0, 0.0);
243*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
244*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
245*cf78ab8cSAndroid Build Coastguard Worker assert!(azimuth == 180 || azimuth == -180);
246*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, -45);
247*cf78ab8cSAndroid Build Coastguard Worker }
248*cf78ab8cSAndroid Build Coastguard Worker }
249*cf78ab8cSAndroid Build Coastguard Worker
250*cf78ab8cSAndroid Build Coastguard Worker #[test]
rotation_only()251*cf78ab8cSAndroid Build Coastguard Worker fn rotation_only() {
252*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(0.0, 0.0, 10.0, 0.0, 0.0, 0.0);
253*cf78ab8cSAndroid Build Coastguard Worker {
254*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
255*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
256*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
257*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 0);
258*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
259*cf78ab8cSAndroid Build Coastguard Worker }
260*cf78ab8cSAndroid Build Coastguard Worker {
261*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 45.0, 0.0, 0.0); // <=> azimuth = -45deg
262*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
263*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
264*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 45);
265*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
266*cf78ab8cSAndroid Build Coastguard Worker }
267*cf78ab8cSAndroid Build Coastguard Worker {
268*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 45.0, 0.0);
269*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
270*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
271*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 0);
272*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, -45);
273*cf78ab8cSAndroid Build Coastguard Worker }
274*cf78ab8cSAndroid Build Coastguard Worker {
275*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 45.0);
276*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
277*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
278*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 0);
279*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
280*cf78ab8cSAndroid Build Coastguard Worker }
281*cf78ab8cSAndroid Build Coastguard Worker }
282*cf78ab8cSAndroid Build Coastguard Worker
283*cf78ab8cSAndroid Build Coastguard Worker #[test]
rotation_only_complex_position()284*cf78ab8cSAndroid Build Coastguard Worker fn rotation_only_complex_position() {
285*cf78ab8cSAndroid Build Coastguard Worker let b_pose = Pose::new(10.0, 10.0, 10.0, 0.0, 0.0, 0.0);
286*cf78ab8cSAndroid Build Coastguard Worker {
287*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
288*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
289*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
290*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 45);
291*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 35);
292*cf78ab8cSAndroid Build Coastguard Worker }
293*cf78ab8cSAndroid Build Coastguard Worker {
294*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 90.0, 0.0, 0.0);
295*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
296*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
297*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 135);
298*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 35);
299*cf78ab8cSAndroid Build Coastguard Worker }
300*cf78ab8cSAndroid Build Coastguard Worker {
301*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 90.0, 0.0);
302*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
303*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
304*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 45);
305*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, -35);
306*cf78ab8cSAndroid Build Coastguard Worker }
307*cf78ab8cSAndroid Build Coastguard Worker {
308*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, 0.0, 0.0, 90.0);
309*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
310*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
311*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, -45);
312*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 35);
313*cf78ab8cSAndroid Build Coastguard Worker }
314*cf78ab8cSAndroid Build Coastguard Worker {
315*cf78ab8cSAndroid Build Coastguard Worker let a_pose = Pose::new(0.0, 0.0, 0.0, -45.0, 35.0, 42.0);
316*cf78ab8cSAndroid Build Coastguard Worker let (_, azimuth, elevation) =
317*cf78ab8cSAndroid Build Coastguard Worker compute_range_azimuth_elevation(&a_pose, &b_pose).unwrap();
318*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(azimuth, 0);
319*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(elevation, 0);
320*cf78ab8cSAndroid Build Coastguard Worker }
321*cf78ab8cSAndroid Build Coastguard Worker }
322*cf78ab8cSAndroid Build Coastguard Worker }
323