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