1#![allow(clippy::inline_always)]
2#![allow(clippy::many_single_char_names)]
3
4#[cfg(feature = "simd")]
5use core::simd::{f32x4, simd_swizzle};
6
7use vqm::{Quaternion, Vector3};
8
9pub trait SensorFusionMath: Sized {
11 fn estimate_gravity(q: Quaternion<Self>) -> Vector3<Self>;
12 fn derivative(q: Quaternion<Self>, gyro: Vector3<Self>) -> Quaternion<Self>;
13 fn madgwick_step_acc(q: Quaternion<Self>, acc: Vector3<Self>, max_acc_magnitude_squared: Self) -> Quaternion<Self>;
14 fn madgwick_step_acc_mag(
15 q: Quaternion<Self>,
16 acc: Vector3<Self>,
17 mag: Vector3<Self>,
18 max_acc_magnitude_squared: Self,
19 ) -> Quaternion<Self>;
20}
21
22impl SensorFusionMath for f32 {
23 #[inline(always)]
24 fn estimate_gravity(q: Quaternion<Self>) -> Vector3<Self> {
25 Vector3 {
26 x: 2.0 * (q.x * q.z - q.w * q.y),
27 y: 2.0 * (q.y * q.z + q.w * q.x),
28 z: q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
29 }
30 }
31
32 #[inline(always)]
33 fn derivative(q: Quaternion<Self>, gyro: Vector3<Self>) -> Quaternion<Self> {
34 #[cfg(feature = "simd")]
35 {
36 let q_v = f32x4::from(q);
76 let g_raw = f32x4::from(gyro);
77
78 let g_v = simd_swizzle!(g_raw, [3, 0, 1, 2]) * f32x4::from_array([0.0, 1.0, 1.0, 1.0]);
80
81 let w1 = simd_swizzle!(q_v, [0, 0, 0, 0]);
83 let x1 = simd_swizzle!(q_v, [1, 1, 1, 1]);
84 let y1 = simd_swizzle!(q_v, [2, 2, 2, 2]);
85 let z1 = simd_swizzle!(q_v, [3, 3, 3, 3]);
86
87 let g_w = g_v; let g_x = simd_swizzle!(g_v, [1, 0, 3, 2]); let g_y = simd_swizzle!(g_v, [2, 3, 0, 1]); let g_z = simd_swizzle!(g_v, [3, 2, 1, 0]); let res = (w1 * g_w)
93 + (x1 * g_x * f32x4::from_array([-1.0, 1.0, -1.0, 1.0]))
94 + (y1 * g_y * f32x4::from_array([-1.0, 1.0, 1.0, -1.0]))
95 + (z1 * g_z * f32x4::from_array([-1.0, -1.0, 1.0, 1.0]));
96
97 let q_dot = res * f32x4::splat(0.5);
98 q_dot.into()
99 }
100 #[cfg(not(feature = "simd"))]
101 {
102 Quaternion {
103 w: (-q.x * gyro.x - q.y * gyro.y - q.z * gyro.z) * 0.5,
104 x: f32::midpoint(q.w * gyro.x - q.z * gyro.y, q.y * gyro.z),
105 y: (q.z * gyro.x + q.w * gyro.y - q.x * gyro.z) * 0.5,
106 z: (-q.y * gyro.x + q.x * gyro.y + q.w * gyro.z) * 0.5,
107 }
108 }
109 }
110
111 #[inline(always)]
123 fn madgwick_step_acc(q: Quaternion<Self>, acc: Vector3<Self>, max_acc_magnitude_squared: Self) -> Quaternion<Self> {
124 use num_traits::Zero;
125 use vqm::MathMethods;
126
127 let acc_magnitude_squared = acc.norm_squared();
128 let mut a = acc;
131 if acc_magnitude_squared > max_acc_magnitude_squared || acc_magnitude_squared == 0.0 {
132 a = Vector3::zero();
133 } else {
134 a *= acc_magnitude_squared.sqrt_reciprocal();
135 }
136 #[cfg(feature = "simd")]
137 {
138 let q_v = f32x4::from(q);
139
140 let wz_common = 2.0 * (q.x * q.x + q.y * q.y);
142
143 let xy_common = 2.0 * (q.w * q.w + q.z * q.z - 1.0 + 2.0 * wz_common + a.z);
145
146 let common_scalars = f32x4::from_array([wz_common, xy_common, xy_common, wz_common]);
148 let common = q_v * common_scalars;
149
150 let ax_q_swiz = simd_swizzle!(q_v, [2, 3, 0, 1]);
154 let ax_signs = f32x4::from_array([1.0, -1.0, 1.0, -1.0]);
155 let ax = (ax_q_swiz * ax_signs) * f32x4::splat(a.x);
156
157 let ay_q_swiz = simd_swizzle!(q_v, [1, 0, 3, 2]);
161 let ay_signs = f32x4::splat(-1.0);
162 let ay = (ay_q_swiz * ay_signs) * f32x4::splat(a.y);
163
164 let ret_v = common + ax + ay;
166
167 ret_v.into()
168 }
169 #[cfg(not(feature = "simd"))]
170 {
171 let wz_common = 2.0 * (q.x * q.x + q.y * q.y);
172 let xy_common = 2.0 * (q.w * q.w + q.z * q.z - 1.0 + 2.0 * wz_common + a.z);
173 Quaternion {
174 w: q.w * wz_common + q.y * a.x - q.x * a.y,
175 x: q.x * xy_common - q.z * a.x - q.w * a.y,
176 y: q.y * xy_common + q.w * a.x - q.z * a.y,
177 z: q.z * wz_common - q.x * a.x - q.y * a.y,
178 }
179 }
180 }
181
182 #[inline(always)]
183 fn madgwick_step_acc_mag(
184 q: Quaternion<Self>,
185 acc: Vector3<Self>,
186 mag: Vector3<Self>,
187 max_acc_magnitude_squared: Self,
188 ) -> Quaternion<Self> {
189 use num_traits::Zero;
190 use vqm::MathMethods;
191
192 let acc_magnitude_squared = acc.norm_squared();
193 let mut a = acc;
196 if acc_magnitude_squared > max_acc_magnitude_squared || acc_magnitude_squared == 0.0 {
197 a = Vector3::zero();
198 } else {
199 a *= acc_magnitude_squared.sqrt_reciprocal();
200 }
201
202 let m = mag.normalize();
203
204 let q0 = q.w;
206 let q1 = q.x;
207 let q2 = q.y;
208 let q3 = q.z;
209 let q0q0 = q0 * q0;
211 let q0q1 = q0 * q1;
212 let q0q2 = q0 * q2;
213 let q0q3 = q0 * q3;
214 let q1q1 = q1 * q1;
215 let q1q2 = q1 * q2;
216 let q1q3 = q1 * q3;
217 let q2q2 = q2 * q2;
218 let q2q3 = q2 * q3;
219 let q3q3 = q3 * q3;
220
221 let q1q1_plus_q2q2 = q1q1 + q2q2;
222 let q2q2_plus_q3q3 = q2q2 + q3q3;
223
224 let h = Vector3 {
226 x: m.x * (q0q0 + q1q1 - q2q2_plus_q3q3) + 2.0 * (m.y * (q1q2 - q0q3) + m.z * (q0q2 + q1q3)),
227 y: (m.x * (q0q3 + q1q2) + m.y * (q0q0 - q1q1 + q2q2 - q3q3) + m.z * (q2q3 - q0q1)) * 2.0,
228 z: 0.0,
229 };
230
231 let bx_bx = h.x * h.x + h.y * h.y;
232 let b = Vector3 {
233 x: bx_bx.sqrt(),
234 y: 0.0,
235 z: 2.0 * (m.x * (q1q3 - q0q2) + m.y * (q0q1 + q2q3)) + m.z * (q0q0 - q1q1_plus_q2q2 + q3q3),
236 };
237
238 let a_dash = Vector3 { x: a.x + m.x * b.z, y: a.y + m.y * b.z, z: 0.0 };
239 let bz_bz = b.z * b.z;
240 let _4bx_bz = 4.0 * b.x * b.z;
241
242 let m_bx = m * b.x;
243 let mz_bz = m.z * b.z;
244
245 let sum_squares_minus_one = q0q0 + q1q1_plus_q2q2 + q3q3 - 1.0;
246 let xy_common = sum_squares_minus_one + q1q1_plus_q2q2;
247 let yz_common = sum_squares_minus_one + q2q2_plus_q3q3;
248 let wz_common = q1q1_plus_q2q2 * (1.0 + bz_bz) + bx_bx;
249
250 #[allow(clippy::used_underscore_binding)]
252 Quaternion {
253 w: q0 * 2.0 * (wz_common * q2q2_plus_q3q3) - q1 * a_dash.y
254 + q2 * (a_dash.x - m_bx.z)
255 + q3 * (m_bx.y - _4bx_bz * q0q1),
256
257 x: -q0 * a_dash.y + q1 * 2.0 * (xy_common * (1.0 + 2.0 * bz_bz) + mz_bz + bx_bx * q2q2_plus_q3q3 + a.z)
258 - q2 * m_bx.y
259 - q3 * (a_dash.x + m_bx.z + _4bx_bz * (0.5 * sum_squares_minus_one + q1q1)),
260
261 y: q0 * (a_dash.x - m_bx.z) - q1 * m_bx.y
262 + q2 * 2.0 * (xy_common * (1.0 + bz_bz) + mz_bz + m_bx.x + bx_bx * yz_common + a.z)
263 - q3 * (a_dash.y + _4bx_bz * q1q2),
264
265 z: q0 * m_bx.y - q1 * (a_dash.x + m_bx.z + _4bx_bz * (0.5 * sum_squares_minus_one + q3q3)) - q2 * a_dash.y
266 + q3 * 2.0 * (wz_common + m_bx.x * yz_common),
267 }
268 .normalize()
269 }
270}
271
272impl SensorFusionMath for f64 {
273 #[inline(always)]
274 fn estimate_gravity(q: Quaternion<Self>) -> Vector3<Self> {
275 Vector3 {
276 x: 2.0 * (q.x * q.z - q.w * q.y),
277 y: 2.0 * (q.y * q.z + q.w * q.x),
278 z: q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
279 }
280 }
281
282 #[inline(always)]
283 fn derivative(q: Quaternion<Self>, gyro_rps: Vector3<Self>) -> Quaternion<Self> {
284 Quaternion {
285 w: (-q.x * gyro_rps.x - q.y * gyro_rps.y - q.z * gyro_rps.z) * 0.5,
286 x: (q.w * gyro_rps.x + q.y * gyro_rps.z - q.z * gyro_rps.y) * 0.5,
287 y: f64::midpoint(q.w * gyro_rps.y - q.x * gyro_rps.z, q.z * gyro_rps.x),
288 z: (q.w * gyro_rps.z + q.x * gyro_rps.y - q.y * gyro_rps.x) * 0.5,
289 }
290 }
291
292 #[inline(always)]
293 fn madgwick_step_acc(q: Quaternion<Self>, acc: Vector3<Self>, max_acc_magnitude_squared: Self) -> Quaternion<Self> {
294 use num_traits::Zero;
295 use vqm::MathMethods;
296
297 let acc_magnitude_squared = acc.norm_squared();
298 let mut a = acc;
301 if acc_magnitude_squared > max_acc_magnitude_squared || acc_magnitude_squared == 0.0 {
302 a = Vector3::zero();
303 } else {
304 a *= acc_magnitude_squared.sqrt_reciprocal();
305 }
306 let wz_common = 2.0 * (q.x * q.x + q.y * q.y);
307 let xy_common = 2.0 * (q.w * q.w + q.z * q.z - 1.0 + 2.0 * wz_common + a.z);
308 Quaternion {
309 w: q.w * wz_common + q.y * a.x - q.x * a.y,
310 x: q.x * xy_common - q.z * a.x - q.w * a.y,
311 y: q.y * xy_common + q.w * a.x - q.z * a.y,
312 z: q.z * wz_common - q.x * a.x - q.y * a.y,
313 }
314 }
315
316 #[inline(always)]
318 fn madgwick_step_acc_mag(
319 q: Quaternion<Self>,
320 _acc: Vector3<Self>,
321 _mag: Vector3<Self>,
322 _max_acc_magnitude_squared: Self,
323 ) -> Quaternion<Self> {
324 q
325 }
326}