1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(not(feature = "f32"), allow(clippy::unnecessary_cast))]
3#[cfg(feature = "f32")]
14pub type Float = f32;
20#[cfg(not(feature = "f32"))]
21pub type Float = f64;
27
28#[cfg(any(feature = "std", feature = "micromath"))]
29mod math {
30 use crate::Float;
31 #[cfg(feature = "micromath")]
32 use micromath::F32Ext;
33
34 pub fn sqrt(value: Float) -> Float {
35 value.sqrt()
36 }
37
38 pub fn fabs(value: Float) -> Float {
39 value.abs()
40 }
41
42 pub fn exp(value: Float) -> Float {
43 value.exp()
44 }
45
46 pub fn sin(value: Float) -> Float {
47 value.sin()
48 }
49
50 pub fn cos(value: Float) -> Float {
51 value.cos()
52 }
53
54 #[cfg(feature = "std")]
55 pub fn tan_f64(value: f64) -> f64 {
56 value.tan()
57 }
58
59 #[cfg(feature = "micromath")]
60 pub fn tan_f64(value: f64) -> f64 {
61 (value as f32).tan() as f64
62 }
63
64 pub fn asin(value: Float) -> Float {
65 value.asin()
66 }
67
68 pub fn acos(value: Float) -> Float {
69 value.acos()
70 }
71
72 pub fn atan2(value: Float, other: Float) -> Float {
73 value.atan2(other)
74 }
75}
76
77#[cfg(feature = "libm")]
78mod math {
79 use crate::Float;
80
81 pub fn sqrt(value: Float) -> Float {
82 libm::Libm::<Float>::sqrt(value)
83 }
84
85 pub fn fabs(value: Float) -> Float {
86 libm::Libm::<Float>::fabs(value)
87 }
88
89 pub fn exp(value: Float) -> Float {
90 libm::Libm::<Float>::exp(value)
91 }
92
93 pub fn sin(value: Float) -> Float {
94 libm::Libm::<Float>::sin(value)
95 }
96
97 pub fn cos(value: Float) -> Float {
98 libm::Libm::<Float>::cos(value)
99 }
100
101 pub fn tan_f64(value: f64) -> f64 {
102 libm::tan(value)
103 }
104
105 pub fn asin(value: Float) -> Float {
106 libm::Libm::<Float>::asin(value)
107 }
108
109 pub fn acos(value: Float) -> Float {
110 libm::Libm::<Float>::acos(value)
111 }
112
113 pub fn atan2(value: Float, other: Float) -> Float {
114 libm::Libm::<Float>::atan2(value, other)
115 }
116}
117
118#[cfg(feature = "f32")]
119use core::f32::consts as fc;
120#[cfg(not(feature = "f32"))]
121use core::f64::consts as fc;
122use core::{
123 f64::consts as f64c,
124 ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
125};
126
127fn flatten<const N: usize, const M: usize, const R: usize>(value: &mut [[Float; N]; M]) -> &mut [Float; R] {
128 assert_eq!(N * M, R);
129 unsafe { core::mem::transmute(value) }
131}
132
133#[derive(Clone, Copy, Default)]
137pub struct Quaternion(pub Float, pub Float, pub Float, pub Float);
138
139impl Mul for Quaternion {
140 type Output = Quaternion;
141
142 fn mul(self, rhs: Self) -> Self::Output {
143 let w = self.0 * rhs.0 - self.1 * rhs.1 - self.2 * rhs.2 - self.3 * rhs.3;
144 let x = self.0 * rhs.1 + self.1 * rhs.0 + self.2 * rhs.3 - self.3 * rhs.2;
145 let y = self.0 * rhs.2 - self.1 * rhs.3 + self.2 * rhs.0 + self.3 * rhs.1;
146 let z = self.0 * rhs.3 + self.1 * rhs.2 - self.2 * rhs.1 + self.3 * rhs.0;
147 Self(w, x, y, z)
148 }
149}
150
151impl MulAssign for Quaternion {
152 fn mul_assign(&mut self, rhs: Self) {
153 *self = *self * rhs;
154 }
155}
156
157impl From<[Float; 4]> for Quaternion {
158 fn from(value: [Float; 4]) -> Self {
159 Self(value[0], value[1], value[2], value[3])
160 }
161}
162
163impl Quaternion {
164 pub fn norm(&self) -> Float {
165 math::sqrt(square(self.0) + square(self.1) + square(self.2) + square(self.3))
166 }
167
168 pub fn normalize(&mut self) {
169 let n = self.norm();
170 if n < Float::EPSILON {
171 return;
172 }
173 self.0 /= n;
174 self.1 /= n;
175 self.2 /= n;
176 self.3 /= n;
177 }
178
179 pub fn rotate(&self, v: [Float; 3]) -> [Float; 3] {
180 let x = (1.0 - 2.0 * self.2 * self.2 - 2.0 * self.3 * self.3) * v[0]
181 + 2.0 * v[1] * (self.2 * self.1 - self.0 * self.3)
182 + 2.0 * v[2] * (self.0 * self.2 + self.3 * self.1);
183 let y = 2.0 * v[0] * (self.0 * self.3 + self.2 * self.1)
184 + v[1] * (1.0 - 2.0 * self.1 * self.1 - 2.0 * self.3 * self.3)
185 + 2.0 * v[2] * (self.2 * self.3 - self.1 * self.0);
186 let z = 2.0 * v[0] * (self.3 * self.1 - self.0 * self.2)
187 + 2.0 * v[1] * (self.0 * self.1 + self.3 * self.2)
188 + v[2] * (1.0 - 2.0 * self.1 * self.1 - 2.0 * self.2 * self.2);
189 [x, y, z]
190 }
191
192 pub fn apply_delta(&self, delta: Float) -> Quaternion {
193 let c = math::cos(delta / 2.0);
194 let s = math::sin(delta / 2.0);
195 let w = c * self.0 - s * self.3;
196 let x = c * self.1 - s * self.2;
197 let y = c * self.2 + s * self.1;
198 let z = c * self.3 + s * self.0;
199 Self(w, x, y, z)
200 }
201}
202
203#[derive(Clone, Copy)]
207pub struct Matrix<const W: usize, const H: usize>(pub [[Float; W]; H]);
208
209impl<const W: usize, const H: usize> Default for Matrix<W, H> {
210 fn default() -> Self {
211 Self([[0.0; W]; H])
212 }
213}
214
215impl<const W: usize, const H: usize> From<[[Float; W]; H]> for Matrix<W, H> {
216 fn from(value: [[Float; W]; H]) -> Self {
217 Self(value)
218 }
219}
220
221impl<const M: usize, const N: usize, const P: usize> Mul<Matrix<P, N>> for Matrix<N, M> {
222 type Output = Matrix<P, M>;
223
224 fn mul(self, rhs: Matrix<P, N>) -> Self::Output {
225 let mut out: Matrix<P, M> = Default::default();
226 for i in 0..M {
227 for j in 0..P {
228 let mut val = 0.0;
229 for k in 0..N {
230 val += self.0[i][k] * rhs.0[k][j];
231 }
232 out.0[i][j] = val;
233 }
234 }
235 out
236 }
237}
238
239impl<const X: usize> MulAssign for Matrix<X, X> {
240 fn mul_assign(&mut self, rhs: Self) {
241 *self = *self * rhs;
242 }
243}
244
245impl<const W: usize, const H: usize> Add for Matrix<W, H> {
246 type Output = Self;
247
248 fn add(self, rhs: Self) -> Self::Output {
249 let mut out: Self = Default::default();
250 for i in 0..W {
251 for j in 0..H {
252 out.0[j][i] = self.0[j][i] + rhs.0[j][i];
253 }
254 }
255 out
256 }
257}
258
259impl<const W: usize, const H: usize> AddAssign for Matrix<W, H> {
260 fn add_assign(&mut self, rhs: Self) {
261 *self = *self + rhs;
262 }
263}
264
265impl<const W: usize, const H: usize> Sub for Matrix<W, H> {
266 type Output = Self;
267
268 fn sub(self, rhs: Self) -> Self::Output {
269 let mut out: Self = Default::default();
270 for i in 0..W {
271 for j in 0..H {
272 out.0[j][i] = self.0[j][i] - rhs.0[j][i];
273 }
274 }
275 out
276 }
277}
278
279impl<const W: usize, const H: usize> SubAssign for Matrix<W, H> {
280 fn sub_assign(&mut self, rhs: Self) {
281 *self = *self - rhs;
282 }
283}
284
285impl<const W: usize, const H: usize> Matrix<W, H> {
286 pub fn transpose(self) -> Matrix<H, W> {
287 let mut out: Matrix<H, W> = Default::default();
288 for i in 0..W {
289 for j in 0..H {
290 out.0[i][j] = self.0[j][i];
291 }
292 }
293 out
294 }
295}
296
297impl Matrix<3, 3> {
298 pub fn invert(self) -> Option<Self> {
299 let a =
301 self.0[1][1] as f64 * self.0[2][2] as f64 - self.0[1][2] as f64 * self.0[2][1] as f64; let d =
303 self.0[0][2] as f64 * self.0[2][1] as f64 - self.0[0][1] as f64 * self.0[2][2] as f64; let g =
305 self.0[0][1] as f64 * self.0[1][2] as f64 - self.0[0][2] as f64 * self.0[1][1] as f64; let b =
307 self.0[1][2] as f64 * self.0[2][0] as f64 - self.0[1][0] as f64 * self.0[2][2] as f64; let e =
309 self.0[0][0] as f64 * self.0[2][2] as f64 - self.0[0][2] as f64 * self.0[2][0] as f64; let h =
311 self.0[0][2] as f64 * self.0[1][0] as f64 - self.0[0][0] as f64 * self.0[1][2] as f64; let c =
313 self.0[1][0] as f64 * self.0[2][1] as f64 - self.0[1][1] as f64 * self.0[2][0] as f64; let f =
315 self.0[0][1] as f64 * self.0[2][0] as f64 - self.0[0][0] as f64 * self.0[2][1] as f64; let i =
317 self.0[0][0] as f64 * self.0[1][1] as f64 - self.0[0][1] as f64 * self.0[1][0] as f64; let det = self.0[0][0] as f64 * a + self.0[0][1] as f64 * b + self.0[0][2] as f64 * c; if (-f64::EPSILON..=f64::EPSILON).contains(&det) {
322 return None;
323 }
324
325 Some(
327 [
328 [(a / det) as Float, (d / det) as Float, (g / det) as Float],
329 [(b / det) as Float, (e / det) as Float, (h / det) as Float],
330 [(c / det) as Float, (f / det) as Float, (i / det) as Float],
331 ]
332 .into(),
333 )
334 }
335}
336
337#[cfg_attr(doc, doc = include_str!("../katex.html"))]
347#[derive(Clone, Copy)]
348pub struct Params {
349 pub tau_acc: Float,
359
360 pub tau_mag: Float,
371
372 #[cfg(feature = "motion-bias-estimation")]
373 pub motion_bias_est_enabled: bool,
378
379 pub rest_bias_est_enabled: bool,
384
385 pub mag_dist_rejection_enabled: bool,
392
393 pub bias_sigma_init: Float,
397
398 pub bias_forgetting_time: Float,
404
405 pub bias_clip: Float,
413
414 #[cfg(feature = "motion-bias-estimation")]
415 pub bias_sigma_motion: Float,
421
422 #[cfg(feature = "motion-bias-estimation")]
423 pub bias_vertical_forgetting_factor: Float,
431
432 pub bias_sigma_rest: Float,
438
439 pub rest_min_t: Float,
445
446 pub rest_filter_tau: Float,
453
454 pub rest_th_gyr: Float,
461
462 pub rest_th_acc: Float,
469
470 pub mag_current_tau: Float,
478
479 pub mag_ref_tau: Float,
485
486 pub mag_norm_th: Float,
492
493 pub mag_dip_th: Float,
497
498 pub mag_new_time: Float,
506
507 pub mag_new_first_time: Float,
514
515 pub mag_new_min_gyr: Float,
521
522 pub mag_min_undisturbed_time: Float,
526
527 pub mag_max_rejection_time: Float,
536
537 pub mag_rejection_factor: Float,
547}
548
549impl Default for Params {
550 fn default() -> Self {
551 Self {
552 tau_acc: 3.0,
553 tau_mag: 9.0,
554 #[cfg(feature = "motion-bias-estimation")]
555 motion_bias_est_enabled: true,
556 rest_bias_est_enabled: true,
557 mag_dist_rejection_enabled: true,
558 bias_sigma_init: 0.5,
559 bias_forgetting_time: 100.0,
560 bias_clip: 2.0,
561 #[cfg(feature = "motion-bias-estimation")]
562 bias_sigma_motion: 0.1,
563 #[cfg(feature = "motion-bias-estimation")]
564 bias_vertical_forgetting_factor: 0.0001,
565 bias_sigma_rest: 0.03,
566 rest_min_t: 1.5,
567 rest_filter_tau: 0.5,
568 rest_th_gyr: 2.0,
569 rest_th_acc: 0.5,
570 mag_current_tau: 0.05,
571 mag_ref_tau: 20.0,
572 mag_norm_th: 0.1,
573 mag_dip_th: 10.0,
574 mag_new_time: 20.0,
575 mag_new_first_time: 5.0,
576 mag_new_min_gyr: 20.0,
577 mag_min_undisturbed_time: 0.5,
578 mag_max_rejection_time: 60.0,
579 mag_rejection_factor: 2.0,
580 }
581 }
582}
583
584#[cfg_attr(doc, doc = include_str!("../katex.html"))]
593#[derive(Clone, Copy, Default)]
594pub struct State {
595 pub gyr_quat: Quaternion,
597
598 pub acc_quat: Quaternion,
600
601 pub delta: Float,
606
607 pub rest_detected: bool,
611
612 pub mag_dist_detected: bool,
614
615 pub last_acc_lp: [Float; 3],
617
618 pub acc_lp_state: [[f64; 2]; 3],
620
621 pub last_acc_corr_angular_rate: Float,
626
627 pub k_mag_init: Float,
633
634 pub last_mag_dis_angle: Float,
639
640 pub last_mag_corr_angular_rate: Float,
645
646 pub bias: [Float; 3],
648 #[cfg(feature = "motion-bias-estimation")]
649 pub bias_p: Matrix<3, 3>,
654
655 #[cfg(not(feature = "motion-bias-estimation"))]
656 pub bias_p: Float,
660
661 #[cfg(feature = "motion-bias-estimation")]
662 pub motion_bias_est_rlp_state: [[f64; 2]; 9],
665 #[cfg(feature = "motion-bias-estimation")]
666 pub motion_bias_est_bias_lp_state: [[f64; 2]; 2],
668
669 pub rest_last_squared_deviations: [Float; 2],
677
678 pub rest_t: Float,
682
683 pub rest_last_gyr_lp: [Float; 3],
687
688 pub rest_gyr_lp_state: [[f64; 2]; 3],
690
691 pub rest_last_acc_lp: [Float; 3],
693
694 pub rest_acc_lp_state: [[f64; 2]; 3],
696
697 pub mag_ref_norm: Float,
701
702 pub mag_ref_dip: Float,
704
705 pub mag_undisturbed_t: Float,
709
710 pub mag_reject_t: Float,
715
716 pub mag_candidate_norm: Float,
718
719 pub mag_candidate_dip: Float,
721
722 pub mag_candidate_t: Float,
727
728 pub mag_norm_dip: [Float; 2],
732
733 pub mag_norm_dip_lp_state: [[f64; 2]; 2],
735}
736
737#[cfg_attr(doc, doc = include_str!("../katex.html"))]
742#[derive(Clone, Copy, Default)]
743pub struct Coefficients {
744 pub gyr_ts: Float,
746
747 pub acc_ts: Float,
749
750 pub mag_ts: Float,
752
753 pub acc_lp_b: [f64; 3],
757
758 pub acc_lp_a: [f64; 2],
762
763 pub k_mag: Float,
765
766 pub bias_p0: Float,
768
769 pub bias_v: Float,
771
772 #[cfg(feature = "motion-bias-estimation")]
773 pub bias_motion_w: Float,
775
776 #[cfg(feature = "motion-bias-estimation")]
777 pub bias_vertical_w: Float,
779
780 pub bias_rest_w: Float,
782
783 pub rest_gyr_lp_b: [f64; 3],
785
786 pub rest_gyr_lp_a: [f64; 2],
788
789 pub rest_acc_lp_b: [f64; 3],
791
792 pub rest_acc_lp_a: [f64; 2],
794
795 pub k_mag_ref: Float,
797
798 pub mag_norm_dip_lp_b: [f64; 3],
800
801 pub mag_norm_dip_lp_a: [f64; 2],
803}
804
805pub struct VQF {
828 params: Params,
829 state: State,
830 coeffs: Coefficients,
831}
832
833#[inline(always)]
834fn square(t: Float) -> Float {
835 t * t
836}
837
838#[cfg_attr(doc, doc = include_str!("../katex.html"))]
839impl VQF {
840 pub fn new(
853 gyr_ts: Float,
854 acc_ts: Option<Float>,
855 mag_ts: Option<Float>,
856 params: Option<Params>,
857 ) -> Self {
858 let mut ret = Self {
859 params: params.unwrap_or_default(),
860 state: Default::default(),
861 coeffs: Default::default(),
862 };
863 ret.coeffs.gyr_ts = gyr_ts;
864 ret.coeffs.acc_ts = acc_ts.unwrap_or(gyr_ts);
865 ret.coeffs.mag_ts = mag_ts.unwrap_or(gyr_ts);
866 ret.setup();
867 ret
868 }
869
870 pub fn update_gyr(&mut self, gyr: [Float; 3]) {
875 self.update_gyr_with_ts(gyr, self.coeffs.gyr_ts);
876 }
877
878 pub fn update_gyr_with_ts(&mut self, gyr: [Float; 3], ts: Float) {
880 if self.params.rest_bias_est_enabled || self.params.mag_dist_rejection_enabled {
882 Self::filter_vec(
883 gyr,
884 self.params.rest_filter_tau,
885 self.coeffs.gyr_ts,
886 self.coeffs.rest_gyr_lp_b,
887 self.coeffs.rest_gyr_lp_a,
888 &mut self.state.rest_gyr_lp_state,
889 &mut self.state.rest_last_gyr_lp,
890 );
891
892 self.state.rest_last_squared_deviations[0] =
893 square(gyr[0] - self.state.rest_last_gyr_lp[0])
894 + square(gyr[1] - self.state.rest_last_gyr_lp[1])
895 + square(gyr[2] - self.state.rest_last_gyr_lp[2]);
896
897 let bias_clip = self.params.bias_clip * (fc::PI / 180.0);
898 if self.state.rest_last_squared_deviations[0]
899 >= square(self.params.rest_th_gyr * (fc::PI / 180.0))
900 || math::fabs(self.state.rest_last_gyr_lp[0]) > bias_clip
901 || math::fabs(self.state.rest_last_gyr_lp[1]) > bias_clip
902 || math::fabs(self.state.rest_last_gyr_lp[2]) > bias_clip
903 {
904 self.state.rest_t = 0.0;
905 self.state.rest_detected = false;
906 }
907 }
908
909 let gyr_no_bias = [
911 gyr[0] - self.state.bias[0],
912 gyr[1] - self.state.bias[1],
913 gyr[2] - self.state.bias[2],
914 ];
915
916 let gyr_norm = Self::norm(&gyr_no_bias);
918 let angle = gyr_norm * ts;
919 if gyr_norm > Float::EPSILON {
920 let c = math::cos(angle / 2.0);
921 let s = math::sin(angle / 2.0) / gyr_norm;
922 let gyr_step_quat = [
923 c,
924 s * gyr_no_bias[0],
925 s * gyr_no_bias[1],
926 s * gyr_no_bias[2],
927 ];
928 self.state.gyr_quat *= gyr_step_quat.into();
929 self.state.gyr_quat.normalize();
930 }
931 }
932
933 pub fn update_acc(&mut self, acc: [Float; 3]) {
940 if acc == [0.0; 3] {
942 return;
943 }
944
945 if self.params.rest_bias_est_enabled {
947 Self::filter_vec(
948 acc,
949 self.params.rest_filter_tau,
950 self.coeffs.acc_ts,
951 self.coeffs.rest_acc_lp_b,
952 self.coeffs.rest_acc_lp_a,
953 &mut self.state.rest_acc_lp_state,
954 &mut self.state.rest_last_acc_lp,
955 );
956
957 self.state.rest_last_squared_deviations[1] =
958 square(acc[0] - self.state.rest_last_acc_lp[0])
959 + square(acc[1] - self.state.rest_last_acc_lp[1])
960 + square(acc[2] - self.state.rest_last_acc_lp[2]);
961
962 if self.state.rest_last_squared_deviations[1] >= square(self.params.rest_th_acc) {
963 self.state.rest_t = 0.0;
964 self.state.rest_detected = false;
965 } else {
966 self.state.rest_t += self.coeffs.acc_ts;
967 if self.state.rest_t >= self.params.rest_min_t {
968 self.state.rest_detected = true;
969 }
970 }
971 }
972
973 let acc_earth = self.state.gyr_quat.rotate(acc);
975 Self::filter_vec(
976 acc_earth,
977 self.params.tau_acc,
978 self.coeffs.acc_ts,
979 self.coeffs.acc_lp_b,
980 self.coeffs.acc_lp_a,
981 &mut self.state.acc_lp_state,
982 &mut self.state.last_acc_lp,
983 );
984
985 let mut acc_earth = self.state.acc_quat.rotate(self.state.last_acc_lp);
987 Self::normalize(&mut acc_earth);
988
989 let q_w = math::sqrt((acc_earth[2] + 1.0) / 2.0);
991 let acc_corr_quat: Quaternion = if q_w > 1e-6 {
992 [
993 q_w,
994 0.5 * acc_earth[1] / q_w,
995 -0.5 * acc_earth[0] / q_w,
996 0.0,
997 ]
998 .into()
999 } else {
1000 [0.0, 1.0, 0.0, 1.0].into()
1002 };
1003 self.state.acc_quat = acc_corr_quat * self.state.acc_quat;
1004 self.state.acc_quat.normalize();
1005
1006 self.state.last_acc_corr_angular_rate =
1008 math::acos(acc_earth[2]) / self.coeffs.acc_ts;
1009
1010 #[cfg(feature = "motion-bias-estimation")]
1012 {
1013 if self.params.motion_bias_est_enabled || self.params.rest_bias_est_enabled {
1014 let bias_clip = self.params.bias_clip * (fc::PI / 180.0);
1015
1016 let acc_gyr_quat = self.quat_6d();
1018 let mut r: Matrix<3, 3> = [
1019 [
1020 1.0 - 2.0 * square(acc_gyr_quat.2) - 2.0 * square(acc_gyr_quat.3), 2.0 * (acc_gyr_quat.2 * acc_gyr_quat.1 - acc_gyr_quat.0 * acc_gyr_quat.3), 2.0 * (acc_gyr_quat.0 * acc_gyr_quat.2 + acc_gyr_quat.3 * acc_gyr_quat.1), ],
1024 [
1025 2.0 * (acc_gyr_quat.0 * acc_gyr_quat.3 + acc_gyr_quat.2 * acc_gyr_quat.1), 1.0 - 2.0 * square(acc_gyr_quat.1) - 2.0 * square(acc_gyr_quat.3), 2.0 * (acc_gyr_quat.2 * acc_gyr_quat.3 - acc_gyr_quat.1 * acc_gyr_quat.0), ],
1029 [
1030 2.0 * (acc_gyr_quat.3 * acc_gyr_quat.1 - acc_gyr_quat.0 * acc_gyr_quat.2), 2.0 * (acc_gyr_quat.0 * acc_gyr_quat.1 + acc_gyr_quat.3 * acc_gyr_quat.2), 1.0 - 2.0 * square(acc_gyr_quat.1) - 2.0 * square(acc_gyr_quat.2), ],
1034 ]
1035 .into();
1036
1037 let mut bias_lp = [
1039 r.0[0][0] * self.state.bias[0]
1040 + r.0[0][1] * self.state.bias[1]
1041 + r.0[0][2] * self.state.bias[2],
1042 r.0[1][0] * self.state.bias[0]
1043 + r.0[1][1] * self.state.bias[1]
1044 + r.0[1][2] * self.state.bias[2],
1045 ];
1046
1047 let r_arr: &mut [Float; 9] = flatten(&mut r.0);
1049 Self::filter_vec(
1050 *r_arr,
1051 self.params.tau_acc,
1052 self.coeffs.acc_ts,
1053 self.coeffs.acc_lp_b,
1054 self.coeffs.acc_lp_a,
1055 &mut self.state.motion_bias_est_rlp_state,
1056 r_arr,
1057 );
1058 Self::filter_vec(
1059 bias_lp,
1060 self.params.tau_acc,
1061 self.coeffs.acc_ts,
1062 self.coeffs.acc_lp_b,
1063 self.coeffs.acc_lp_a,
1064 &mut self.state.motion_bias_est_bias_lp_state,
1065 &mut bias_lp,
1066 );
1067
1068 let (mut e, w) = if self.state.rest_detected && self.params.rest_bias_est_enabled {
1070 let e = [
1071 self.state.rest_last_gyr_lp[0] - self.state.bias[0],
1072 self.state.rest_last_gyr_lp[1] - self.state.bias[1],
1073 self.state.rest_last_gyr_lp[2] - self.state.bias[2],
1074 ];
1075 r = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]].into();
1076 let w = [self.coeffs.bias_rest_w; 3];
1077 (e, w)
1078 } else if self.params.motion_bias_est_enabled {
1079 let e = [
1080 -acc_earth[1] / self.coeffs.acc_ts + bias_lp[0]
1081 - r.0[0][0] * self.state.bias[0]
1082 - r.0[0][1] * self.state.bias[1]
1083 - r.0[0][2] * self.state.bias[2],
1084 acc_earth[0] / self.coeffs.acc_ts + bias_lp[1]
1085 - r.0[1][0] * self.state.bias[0]
1086 - r.0[1][1] * self.state.bias[1]
1087 - r.0[1][2] * self.state.bias[2],
1088 -r.0[2][0] * self.state.bias[0]
1089 - r.0[2][1] * self.state.bias[1]
1090 - r.0[2][2] * self.state.bias[2],
1091 ];
1092 let w = [
1093 self.coeffs.bias_motion_w,
1094 self.coeffs.bias_motion_w,
1095 self.coeffs.bias_vertical_w,
1096 ];
1097 (e, w)
1098 } else {
1099 ([0.0; 3], [-1.0; 3])
1100 };
1101
1102 if self.state.bias_p.0[0][0] < self.coeffs.bias_p0 {
1105 self.state.bias_p.0[0][0] += self.coeffs.bias_v;
1106 }
1107 if self.state.bias_p.0[1][1] < self.coeffs.bias_p0 {
1108 self.state.bias_p.0[1][1] += self.coeffs.bias_v;
1109 }
1110 if self.state.bias_p.0[2][2] < self.coeffs.bias_p0 {
1111 self.state.bias_p.0[2][2] += self.coeffs.bias_v;
1112 }
1113
1114 if w[0] >= 0.0 {
1115 Self::clip(&mut e, -bias_clip, bias_clip);
1118
1119 let mut k = self.state.bias_p * r.transpose(); k = r * k; k.0[0][0] += w[0];
1123 k.0[1][1] += w[1];
1124 k.0[2][2] += w[2]; k = k.invert().unwrap(); k = r.transpose() * k; k = self.state.bias_p * k; self.state.bias[0] += k.0[0][0] * e[0] + k.0[0][1] * e[1] + k.0[0][2] * e[2];
1131 self.state.bias[1] += k.0[1][0] * e[0] + k.0[1][1] * e[1] + k.0[1][2] * e[2];
1132 self.state.bias[2] += k.0[2][0] * e[0] + k.0[2][1] * e[1] + k.0[2][2] * e[2];
1133
1134 k *= r; k *= self.state.bias_p; self.state.bias_p -= k;
1138
1139 Self::clip(&mut self.state.bias, -bias_clip, bias_clip);
1141 }
1142 }
1143 }
1144 #[cfg(not(feature = "motion-bias-estimation"))]
1145 {
1146 if self.params.rest_bias_est_enabled {
1148 let bias_clip = self.params.bias_clip * (fc::PI / 180.0);
1149 if self.state.bias_p < self.coeffs.bias_p0 {
1150 self.state.bias_p += self.coeffs.bias_v;
1151 }
1152 if self.state.rest_detected {
1153 let mut e = [
1154 self.state.rest_last_gyr_lp[0] - self.state.bias[0],
1155 self.state.rest_last_gyr_lp[1] - self.state.bias[1],
1156 self.state.rest_last_gyr_lp[2] - self.state.bias[2],
1157 ];
1158 Self::clip(&mut e, -bias_clip, bias_clip);
1159
1160 let k = self.state.bias_p / (self.coeffs.bias_rest_w + self.state.bias_p);
1165 self.state.bias[0] += k * e[0];
1167 self.state.bias[1] += k * e[1];
1168 self.state.bias[2] += k * e[2];
1169 self.state.bias_p -= k * self.state.bias_p;
1171 Self::clip(&mut self.state.bias, -bias_clip, bias_clip);
1172 }
1173 }
1174 }
1175 }
1176
1177 pub fn update_mag(&mut self, mag: [Float; 3]) {
1184 if mag == [0.0; 3] {
1186 return;
1187 }
1188
1189 let acc_gyr_quat = self.quat_6d();
1191 let mag_earth = acc_gyr_quat.rotate(mag);
1192
1193 if self.params.mag_dist_rejection_enabled {
1194 self.state.mag_norm_dip[0] = Self::norm(&mag_earth);
1195 self.state.mag_norm_dip[1] =
1196 -math::asin(mag_earth[2] / self.state.mag_norm_dip[0]);
1197
1198 if self.params.mag_current_tau > 0.0 {
1199 Self::filter_vec(
1200 self.state.mag_norm_dip,
1201 self.params.mag_current_tau,
1202 self.coeffs.mag_ts,
1203 self.coeffs.mag_norm_dip_lp_b,
1204 self.coeffs.mag_norm_dip_lp_a,
1205 &mut self.state.mag_norm_dip_lp_state,
1206 &mut self.state.mag_norm_dip,
1207 );
1208 }
1209
1210 if math::fabs(self.state.mag_norm_dip[0] - self.state.mag_ref_norm)
1212 < self.params.mag_norm_th * self.state.mag_ref_norm
1213 && math::fabs(self.state.mag_norm_dip[1] - self.state.mag_ref_dip)
1214 < self.params.mag_dip_th * (fc::PI / 180.0)
1215 {
1216 self.state.mag_undisturbed_t += self.coeffs.mag_ts;
1217 if self.state.mag_undisturbed_t >= self.params.mag_min_undisturbed_time {
1218 self.state.mag_dist_detected = false;
1219 self.state.mag_ref_norm += self.coeffs.k_mag_ref
1220 * (self.state.mag_norm_dip[0] - self.state.mag_ref_norm);
1221 self.state.mag_ref_dip += self.coeffs.k_mag_ref
1222 * (self.state.mag_norm_dip[1] - self.state.mag_ref_dip);
1223 }
1224 } else {
1225 self.state.mag_undisturbed_t = 0.0;
1226 self.state.mag_dist_detected = true;
1227 }
1228
1229 if math::fabs(self.state.mag_norm_dip[0] - self.state.mag_candidate_norm)
1231 < self.params.mag_norm_th * self.state.mag_candidate_norm
1232 && math::fabs(self.state.mag_norm_dip[1] - self.state.mag_candidate_dip)
1233 < self.params.mag_dip_th * (fc::PI / 180.0)
1234 {
1235 if Self::norm(&self.state.rest_last_gyr_lp)
1236 >= self.params.mag_new_min_gyr * fc::PI / 180.0
1237 {
1238 self.state.mag_candidate_t += self.coeffs.mag_ts;
1239 }
1240 self.state.mag_candidate_norm += self.coeffs.k_mag_ref
1241 * (self.state.mag_norm_dip[0] - self.state.mag_candidate_norm);
1242 self.state.mag_candidate_dip += self.coeffs.k_mag_ref
1243 * (self.state.mag_norm_dip[1] - self.state.mag_candidate_dip);
1244
1245 if self.state.mag_dist_detected
1246 && (self.state.mag_candidate_t >= self.params.mag_new_time
1247 || (self.state.mag_ref_norm == 0.0
1248 && self.state.mag_candidate_t >= self.params.mag_new_first_time))
1249 {
1250 self.state.mag_ref_norm = self.state.mag_candidate_norm;
1251 self.state.mag_ref_dip = self.state.mag_candidate_dip;
1252 self.state.mag_dist_detected = false;
1253 self.state.mag_undisturbed_t = self.params.mag_min_undisturbed_time;
1254 }
1255 } else {
1256 self.state.mag_candidate_t = 0.0;
1257 self.state.mag_candidate_norm = self.state.mag_norm_dip[0];
1258 self.state.mag_candidate_dip = self.state.mag_norm_dip[1];
1259 }
1260 }
1261
1262 self.state.last_mag_dis_angle =
1264 math::atan2(mag_earth[0], mag_earth[1]) - self.state.delta;
1265
1266 if self.state.last_mag_dis_angle > fc::PI {
1268 self.state.last_mag_dis_angle -= 2.0 * fc::PI;
1269 } else if self.state.last_mag_dis_angle < (-fc::PI) {
1270 self.state.last_mag_dis_angle += 2.0 * fc::PI;
1271 }
1272
1273 let mut k = self.coeffs.k_mag;
1274
1275 if self.params.mag_dist_rejection_enabled {
1276 if self.state.mag_dist_detected {
1278 if self.state.mag_reject_t <= self.params.mag_max_rejection_time {
1279 self.state.mag_reject_t += self.coeffs.mag_ts;
1280 k = 0.0;
1281 } else {
1282 k /= self.params.mag_rejection_factor;
1283 }
1284 } else {
1285 self.state.mag_reject_t = (0.0 as Float).max(
1286 self.state.mag_reject_t - self.params.mag_rejection_factor * self.coeffs.mag_ts,
1287 );
1288 }
1289 }
1290
1291 if self.state.k_mag_init != 0.0 {
1293 if k < self.state.k_mag_init {
1295 k = self.state.k_mag_init;
1296 }
1297
1298 self.state.k_mag_init = self.state.k_mag_init / (self.state.k_mag_init + 1.0);
1300
1301 if self.state.k_mag_init * self.params.tau_mag < self.coeffs.mag_ts {
1303 self.state.k_mag_init = 0.0;
1304 }
1305 }
1306
1307 self.state.delta += k * self.state.last_mag_dis_angle;
1309 self.state.last_mag_corr_angular_rate =
1311 k * self.state.last_mag_dis_angle / self.coeffs.mag_ts;
1312
1313 if self.state.delta > fc::PI {
1315 self.state.delta -= 2.0 * fc::PI;
1316 } else if self.state.delta < -fc::PI {
1317 self.state.delta += 2.0 * fc::PI;
1318 }
1319 }
1320
1321 pub fn update(&mut self, gyr: [Float; 3], acc: [Float; 3], mag: Option<[Float; 3]>) {
1323 self.update_gyr(gyr);
1324 self.update_acc(acc);
1325 if let Some(mag) = mag {
1326 self.update_mag(mag);
1327 }
1328 }
1329
1330 pub fn quat_3d(&self) -> Quaternion {
1333 self.state.gyr_quat
1334 }
1335
1336 pub fn quat_6d(&self) -> Quaternion {
1339 self.state.acc_quat * self.state.gyr_quat
1340 }
1341
1342 pub fn quat_9d(&self) -> Quaternion {
1345 (self.state.acc_quat * self.state.gyr_quat).apply_delta(self.state.delta)
1346 }
1347
1348 pub fn delta(&self) -> Float {
1353 self.state.delta
1354 }
1355
1356 #[cfg(feature = "motion-bias-estimation")]
1357 pub fn bias_estimate(&self) -> ([Float; 3], Float) {
1362 let sum1 = math::fabs(self.state.bias_p.0[0][0])
1365 + math::fabs(self.state.bias_p.0[0][1])
1366 + math::fabs(self.state.bias_p.0[0][2]);
1367 let sum2 = math::fabs(self.state.bias_p.0[1][0])
1368 + math::fabs(self.state.bias_p.0[1][1])
1369 + math::fabs(self.state.bias_p.0[1][2]);
1370 let sum3 = math::fabs(self.state.bias_p.0[2][0])
1371 + math::fabs(self.state.bias_p.0[2][1])
1372 + math::fabs(self.state.bias_p.0[2][2]);
1373 let p = sum1.max(sum2).max(sum3).min(self.coeffs.bias_p0);
1374 (
1375 self.state.bias,
1376 math::sqrt(p) * (fc::PI / 100.0 / 180.0),
1377 )
1378 }
1379
1380 #[cfg(not(feature = "motion-bias-estimation"))]
1381 pub fn bias_estimate(&self) -> ([Float; 3], Float) {
1386 (
1387 self.state.bias,
1388 math::sqrt(self.state.bias_p) * (fc::PI / 100.0 / 180.0),
1389 )
1390 }
1391
1392 pub fn set_bias_estimate(&mut self, bias: [Float; 3], sigma: Option<Float>) {
1397 self.state.bias = bias;
1398 if let Some(sigma) = sigma {
1399 let p = square(sigma * (180.0 * 100.0 / fc::PI));
1400 #[cfg(feature = "motion-bias-estimation")]
1401 {
1402 self.state.bias_p = [[p, 0.0, 0.0], [0.0, p, 0.0], [0.0, 0.0, p]].into();
1403 }
1404 #[cfg(not(feature = "motion-bias-estimation"))]
1405 {
1406 self.state.bias_p = p;
1407 }
1408 }
1409 }
1410
1411 pub fn rest_detected(&self) -> bool {
1413 self.state.rest_detected
1414 }
1415
1416 pub fn mag_dist_detected(&self) -> bool {
1418 self.state.mag_dist_detected
1419 }
1420
1421 pub fn relative_rest_deviations(&self) -> [Float; 2] {
1427 [
1428 math::sqrt(self.state.rest_last_squared_deviations[0])
1429 / (self.params.rest_th_gyr * (fc::PI / 180.0)),
1430 math::sqrt(self.state.rest_last_squared_deviations[1])
1431 / self.params.rest_th_acc,
1432 ]
1433 }
1434
1435 pub fn mag_ref_norm(&self) -> Float {
1437 self.state.mag_ref_norm
1438 }
1439
1440 pub fn mag_ref_dip(&self) -> Float {
1442 self.state.mag_ref_dip
1443 }
1444
1445 pub fn set_mag_ref(&mut self, norm: Float, dip: Float) {
1447 self.state.mag_ref_norm = norm;
1448 self.state.mag_ref_dip = dip;
1449 }
1450
1451 pub fn set_tau_acc(&mut self, tau_acc: Float) {
1455 if self.params.tau_acc == tau_acc {
1456 return;
1457 }
1458 self.params.tau_acc = tau_acc;
1459 let mut new_b = [0.0; 3];
1460 let mut new_a = [0.0; 2];
1461
1462 Self::filter_coeffs(
1463 self.params.tau_acc,
1464 self.coeffs.acc_ts,
1465 &mut new_b,
1466 &mut new_a,
1467 );
1468 Self::filter_adapt_state_for_coeff_change(
1469 self.state.last_acc_lp,
1470 self.coeffs.acc_lp_b,
1471 self.coeffs.acc_lp_a,
1472 new_b,
1473 new_a,
1474 &mut self.state.acc_lp_state,
1475 );
1476
1477 #[cfg(feature = "motion-bias-estimation")]
1478 {
1479 let mut r: [Float; 9] = [0.0; 9];
1482 for (i, val) in r.iter_mut().enumerate() {
1483 *val = self.state.motion_bias_est_rlp_state[i][0] as Float;
1484 }
1485 Self::filter_adapt_state_for_coeff_change(
1486 r,
1487 self.coeffs.acc_lp_b,
1488 self.coeffs.acc_lp_a,
1489 new_b,
1490 new_a,
1491 &mut self.state.motion_bias_est_rlp_state,
1492 );
1493 let mut bias_lp: [Float; 2] = [0.0; 2];
1494 for (i, val) in bias_lp.iter_mut().enumerate() {
1495 *val = self.state.motion_bias_est_bias_lp_state[i][0] as Float;
1496 }
1497 Self::filter_adapt_state_for_coeff_change(
1498 bias_lp,
1499 self.coeffs.acc_lp_b,
1500 self.coeffs.acc_lp_a,
1501 new_b,
1502 new_a,
1503 &mut self.state.motion_bias_est_bias_lp_state,
1504 );
1505 }
1506
1507 self.coeffs.acc_lp_b = new_b;
1508 self.coeffs.acc_lp_a = new_a;
1509 }
1510
1511 pub fn set_tau_mag(&mut self, tau_mag: Float) {
1515 self.params.tau_mag = tau_mag;
1516 self.coeffs.k_mag = Self::gain_from_tau(self.params.tau_mag, self.coeffs.mag_ts)
1517 }
1518
1519 #[cfg(feature = "motion-bias-estimation")]
1520 pub fn set_motion_bias_est_enabled(&mut self, enabled: bool) {
1522 if self.params.motion_bias_est_enabled == enabled {
1523 return;
1524 }
1525 self.params.motion_bias_est_enabled = enabled;
1526 self.state.motion_bias_est_rlp_state = [[f64::NAN; 2]; 9];
1527 self.state.motion_bias_est_bias_lp_state = [[f64::NAN; 2]; 2];
1528 }
1529
1530 pub fn set_rest_bias_est_enabled(&mut self, enabled: bool) {
1532 if self.params.rest_bias_est_enabled == enabled {
1533 return;
1534 }
1535 self.params.rest_bias_est_enabled = enabled;
1536 self.state.rest_detected = false;
1537 self.state.rest_last_squared_deviations = [0.0; 2];
1538 self.state.rest_t = 0.0;
1539 self.state.rest_last_gyr_lp = [0.0; 3];
1540 self.state.rest_gyr_lp_state = [[f64::NAN; 2]; 3];
1541 self.state.rest_last_acc_lp = [0.0; 3];
1542 self.state.rest_acc_lp_state = [[f64::NAN; 2]; 3];
1543 }
1544
1545 pub fn set_mag_dist_rejection_enabled(&mut self, enabled: bool) {
1547 if self.params.mag_dist_rejection_enabled == enabled {
1548 return;
1549 }
1550 self.params.mag_dist_rejection_enabled = enabled;
1551 self.state.mag_dist_detected = true;
1552 self.state.mag_ref_norm = 0.0;
1553 self.state.mag_ref_dip = 0.0;
1554 self.state.mag_undisturbed_t = 0.0;
1555 self.state.mag_reject_t = self.params.mag_max_rejection_time;
1556 self.state.mag_candidate_norm = -1.0;
1557 self.state.mag_candidate_dip = 0.0;
1558 self.state.mag_candidate_t = 0.0;
1559 self.state.mag_norm_dip_lp_state = [[f64::NAN; 2]; 2];
1560 }
1561
1562 pub fn set_rest_detection_thresholds(&mut self, th_gyr: Float, th_acc: Float) {
1566 self.params.rest_th_gyr = th_gyr;
1567 self.params.rest_th_acc = th_acc;
1568 }
1569
1570 pub fn params(&self) -> &Params {
1572 &self.params
1573 }
1574
1575 pub fn coeffs(&self) -> &Coefficients {
1577 &self.coeffs
1578 }
1579
1580 pub fn state(&self) -> &State {
1582 &self.state
1583 }
1584
1585 pub fn state_mut(&mut self) -> &mut State {
1589 &mut self.state
1590 }
1591
1592 pub fn reset_state(&mut self) {
1596 self.state.gyr_quat = [1.0, 0.0, 0.0, 0.0].into();
1597 self.state.acc_quat = [1.0, 0.0, 0.0, 0.0].into();
1598 self.state.delta = 0.0;
1599
1600 self.state.rest_detected = false;
1601 self.state.mag_dist_detected = true;
1602
1603 self.state.last_acc_lp = [0.0; 3];
1604 self.state.acc_lp_state = [[f64::NAN; 2]; 3];
1605 self.state.last_acc_corr_angular_rate = 0.0;
1606
1607 self.state.k_mag_init = 1.0;
1608 self.state.last_mag_dis_angle = 0.0;
1609 self.state.last_mag_corr_angular_rate = 0.0;
1610
1611 self.state.bias = [0.0; 3];
1612
1613 #[cfg(feature = "motion-bias-estimation")]
1614 {
1615 self.state.bias_p = [
1616 [self.coeffs.bias_p0, 0.0, 0.0],
1617 [0.0, self.coeffs.bias_p0, 0.0],
1618 [0.0, 0.0, self.coeffs.bias_p0],
1619 ]
1620 .into();
1621 }
1622
1623 #[cfg(not(feature = "motion-bias-estimation"))]
1624 {
1625 self.state.bias_p = self.coeffs.bias_p0;
1626 }
1627
1628 #[cfg(feature = "motion-bias-estimation")]
1629 {
1630 self.state.motion_bias_est_rlp_state = [[f64::NAN; 2]; 9];
1631 self.state.motion_bias_est_bias_lp_state = [[f64::NAN; 2]; 2];
1632 }
1633
1634 self.state.rest_last_squared_deviations = [0.0; 2];
1635 self.state.rest_t = 0.0;
1636 self.state.rest_last_gyr_lp = [0.0; 3];
1637 self.state.rest_gyr_lp_state = [[f64::NAN; 2]; 3];
1638 self.state.rest_last_acc_lp = [0.0; 3];
1639 self.state.rest_acc_lp_state = [[f64::NAN; 2]; 3];
1640
1641 self.state.mag_ref_norm = 0.0;
1642 self.state.mag_ref_dip = 0.0;
1643 self.state.mag_undisturbed_t = 0.0;
1644 self.state.mag_reject_t = self.params.mag_max_rejection_time;
1645 self.state.mag_candidate_norm = -1.0;
1646 self.state.mag_candidate_dip = 0.0;
1647 self.state.mag_candidate_t = 0.0;
1648 self.state.mag_norm_dip = [0.0; 2];
1649 self.state.mag_norm_dip_lp_state = [[f64::NAN; 2]; 2];
1650 }
1651
1652 fn norm<const N: usize>(vec: &[Float; N]) -> Float {
1653 let mut s = 0.0;
1654 for i in vec {
1655 s += i * i;
1656 }
1657 math::sqrt(s)
1658 }
1659
1660 fn normalize<const N: usize>(vec: &mut [Float; N]) {
1661 let n = Self::norm(vec);
1662 if n < Float::EPSILON {
1663 return;
1664 }
1665 for i in vec.iter_mut() {
1666 *i /= n;
1667 }
1668 }
1669
1670 fn clip<const N: usize>(vec: &mut [Float; N], min: Float, max: Float) {
1671 for i in vec.iter_mut() {
1672 if *i < min {
1673 *i = min;
1674 } else if *i > max {
1675 *i = max;
1676 }
1677 }
1678 }
1679
1680 fn gain_from_tau(tau: Float, ts: Float) -> Float {
1681 assert!(ts > 0.0);
1682 if tau < 0.0 {
1683 0.0 } else if tau == 0.0 {
1685 1.0 } else {
1687 1.0 - math::exp(-ts / tau) }
1689 }
1690
1691 fn filter_coeffs(tau: Float, ts: Float, out_b: &mut [f64; 3], out_a: &mut [f64; 2]) {
1692 assert!(tau > 0.0);
1693 assert!(ts > 0.0);
1694 let fc = (f64c::SQRT_2 / (2.0 * f64c::PI)) / (tau as f64); let c = math::tan_f64(f64c::PI * fc * ts as f64);
1697 let d = c * c + f64c::SQRT_2 * c + 1.0;
1698 let b0 = c * c / d;
1699 out_b[0] = b0;
1700 out_b[1] = 2.0 * b0;
1701 out_b[2] = b0;
1702 out_a[0] = 2.0 * (c * c - 1.0) / d; out_a[1] = (1.0 - f64c::SQRT_2 * c + c * c) / d; }
1706
1707 fn filter_initial_state(x0: Float, b: [f64; 3], a: [f64; 2], out: &mut [f64; 2]) {
1708 out[0] = (x0 as f64) * (1.0 - b[0]);
1711 out[1] = (x0 as f64) * (b[2] - a[1]);
1712 }
1713
1714 fn filter_adapt_state_for_coeff_change<const N: usize>(
1715 last_y: [Float; N],
1716 b_old: [f64; 3],
1717 a_old: [f64; 2],
1718 b_new: [f64; 3],
1719 a_new: [f64; 2],
1720 state: &mut [[f64; 2]; N],
1721 ) {
1722 if state[0][0].is_nan() {
1723 return;
1724 }
1725 for (i, row) in state.iter_mut().enumerate() {
1726 row[0] += (b_old[0] - b_new[0]) * last_y[i] as f64;
1727 row[1] += (b_old[1] - b_new[1] - a_old[0] + a_new[0]) * last_y[i] as f64;
1728 }
1729 }
1730
1731 fn filter_step(x: Float, b: [f64; 3], a: [f64; 2], state: &mut [f64; 2]) -> Float {
1732 let y = b[0] * (x as f64) + state[0];
1735 state[0] = b[1] * (x as f64) - a[0] * y + state[1];
1736 state[1] = b[2] * (x as f64) - a[1] * y;
1737 y as Float
1738 }
1739
1740 fn filter_vec<const N: usize>(
1741 x: [Float; N],
1742 tau: Float,
1743 ts: Float,
1744 b: [f64; 3],
1745 a: [f64; 2],
1746 state: &mut [[f64; 2]; N],
1747 out: &mut [Float; N],
1748 ) {
1749 assert!(N >= 2);
1750
1751 if state[0][0].is_nan() {
1754 if state[1][0].is_nan() {
1756 state[1][0] = 0.0; for row in state.iter_mut() {
1759 row[1] = 0.0; }
1761 }
1762 state[1][0] += 1.0;
1763 for i in 0..N {
1764 state[i][1] += x[i] as f64;
1765 out[i] = (state[i][1] / state[1][0]) as Float;
1766 }
1767 if (state[1][0] as Float) * ts >= tau {
1768 for i in 0..N {
1769 Self::filter_initial_state(out[i], b, a, &mut state[i]);
1770 }
1771 }
1772 return;
1773 }
1774
1775 for i in 0..N {
1776 out[i] = Self::filter_step(x[i], b, a, &mut state[i]);
1777 }
1778 }
1779
1780 fn setup(&mut self) {
1781 assert!(self.coeffs.gyr_ts > 0.0);
1782 assert!(self.coeffs.acc_ts > 0.0);
1783 assert!(self.coeffs.mag_ts > 0.0);
1784
1785 Self::filter_coeffs(
1786 self.params.tau_acc,
1787 self.coeffs.acc_ts,
1788 &mut self.coeffs.acc_lp_b,
1789 &mut self.coeffs.acc_lp_a,
1790 );
1791
1792 self.coeffs.k_mag = Self::gain_from_tau(self.params.tau_mag, self.coeffs.mag_ts);
1793
1794 self.coeffs.bias_p0 = square(self.params.bias_sigma_init * 100.0);
1795 self.coeffs.bias_v =
1797 square(0.1 * 100.0) * self.coeffs.acc_ts / self.params.bias_forgetting_time;
1798
1799 #[cfg(feature = "motion-bias-estimation")]
1800 {
1801 let p_motion = square(self.params.bias_sigma_motion * 100.0);
1802 self.coeffs.bias_motion_w = square(p_motion) / self.coeffs.bias_v + p_motion;
1803 self.coeffs.bias_vertical_w =
1804 self.coeffs.bias_motion_w / self.params.bias_vertical_forgetting_factor.max(1e-10);
1805 }
1806
1807 let p_rest = square(self.params.bias_sigma_rest * 100.0);
1808 self.coeffs.bias_rest_w = square(p_rest) / self.coeffs.bias_v + p_rest;
1809
1810 Self::filter_coeffs(
1811 self.params.rest_filter_tau,
1812 self.coeffs.gyr_ts,
1813 &mut self.coeffs.rest_gyr_lp_b,
1814 &mut self.coeffs.rest_gyr_lp_a,
1815 );
1816 Self::filter_coeffs(
1817 self.params.rest_filter_tau,
1818 self.coeffs.acc_ts,
1819 &mut self.coeffs.rest_acc_lp_b,
1820 &mut self.coeffs.rest_acc_lp_a,
1821 );
1822
1823 self.coeffs.k_mag_ref = Self::gain_from_tau(self.params.mag_ref_tau, self.coeffs.mag_ts);
1824 if self.params.mag_current_tau > 0.0 {
1825 Self::filter_coeffs(
1826 self.params.mag_current_tau,
1827 self.coeffs.mag_ts,
1828 &mut self.coeffs.mag_norm_dip_lp_b,
1829 &mut self.coeffs.mag_norm_dip_lp_a,
1830 );
1831 } else {
1832 self.coeffs.mag_norm_dip_lp_b = [f64::NAN; 3];
1833 self.coeffs.mag_norm_dip_lp_a = [f64::NAN; 2];
1834 }
1835
1836 self.reset_state();
1837 }
1838}
1839
1840#[cfg(test)]
1841mod tests {
1842 use crate::{Params, Quaternion, VQF};
1843
1844 #[test]
1845 fn basic_parity() {
1846 for mode in 0..=5 {
1847 let params = match mode {
1848 0 => Default::default(),
1849 1 => Params {
1850 mag_dist_rejection_enabled: false,
1851 ..Default::default()
1852 },
1853 2 => Params {
1854 rest_bias_est_enabled: false,
1855 ..Default::default()
1856 },
1857 3 => Params {
1858 motion_bias_est_enabled: false,
1859 ..Default::default()
1860 },
1861 4 => Params {
1862 rest_bias_est_enabled: false,
1863 motion_bias_est_enabled: false,
1864 ..Default::default()
1865 },
1866 5 => Params {
1867 mag_dist_rejection_enabled: false,
1868 rest_bias_est_enabled: false,
1869 motion_bias_est_enabled: false,
1870 ..Default::default()
1871 },
1872 _ => panic!(),
1873 };
1874 let expected: Quaternion = match mode {
1875 0 => [0.499988, 0.499988, 0.500012, 0.500012].into(),
1876 1 => [0.5, 0.5, 0.5, 0.5].into(),
1877 2 => [0.451372, 0.453052, 0.543672, 0.543533].into(),
1878 3 => [0.499988, 0.499988, 0.500012, 0.500012].into(),
1879 4 => [0.424513, 0.454375, 0.555264, 0.55228].into(),
1880 5 => [0.44869, 0.478654, 0.534476, 0.532825].into(),
1881 _ => panic!(),
1882 };
1883 let mut vqf = VQF::new(0.01, None, None, Some(params));
1884
1885 let gyr = [0.01; 3];
1886 let acc = [0.0, 9.8, 0.0];
1887 let mag = [0.5, 0.8, 0.0];
1888
1889 for _ in 0..10000 {
1890 vqf.update(gyr, acc, Some(mag))
1891 }
1892
1893 let quat = vqf.quat_9d();
1894 assert!((quat.0 - expected.0).abs() < 1e-6);
1895 assert!((quat.1 - expected.1).abs() < 1e-6);
1896 assert!((quat.2 - expected.2).abs() < 1e-6);
1897 assert!((quat.3 - expected.3).abs() < 1e-6);
1898 }
1899 }
1900
1901 #[allow(clippy::approx_constant)]
1902 #[allow(non_snake_case)]
1903 mod wigwagwent_tests {
1904 use crate::{Params, VQF};
1905
1906
1907 #[test]
1908 fn single_same_3D_quat() {
1909 let mut vqf = VQF::new(0.01, None, None, None);
1910
1911 let gyr = [0.021, 0.021, 0.021];
1912 vqf.update_gyr(gyr);
1913
1914 let quat = vqf.quat_3d();
1915 assert!((quat.0 - 1.0).abs() < 1e-6);
1916 assert!((quat.1 - 0.000105).abs() < 1e-6);
1917 assert!((quat.2 - 0.000105).abs() < 1e-6);
1918 assert!((quat.3 - 0.000105).abs() < 1e-6);
1919 }
1920
1921 #[test]
1922 fn single_x_3D_quat() {
1923 let mut vqf = VQF::new(0.01, None, None, None);
1924
1925 let gyr = [0.25, 0.0, 0.0];
1926 vqf.update_gyr(gyr);
1927
1928 let quat = vqf.quat_3d();
1929 assert!((quat.0 - 0.9999999).abs() < 1e-6);
1930 assert!((quat.1 - 0.00125).abs() < 1e-6);
1931 assert!((quat.2 - 0.0).abs() < 1e-6);
1932 assert!((quat.3 - 0.0).abs() < 1e-6);
1933 }
1934
1935 #[test]
1936 fn single_y_3D_quat() {
1937 let mut vqf = VQF::new(0.01, None, None, None);
1938
1939 let gyr = [0.0, 0.25, 0.0];
1940 vqf.update_gyr(gyr);
1941
1942 let quat = vqf.quat_3d();
1943 assert!((quat.0 - 0.9999999).abs() < 1e-6);
1944 assert!((quat.1 - 0.0).abs() < 1e-6);
1945 assert!((quat.2 - 0.00125).abs() < 1e-6);
1946 assert!((quat.3 - 0.0).abs() < 1e-6);
1947 }
1948
1949 #[test]
1950 fn single_z_3D_quat() {
1951 let mut vqf = VQF::new(0.01, None, None, None);
1952
1953 let gyr = [0.0, 0.0, 0.25];
1954 vqf.update_gyr(gyr);
1955
1956 let quat = vqf.quat_3d();
1957 assert!((quat.0 - 0.9999999).abs() < 1e-6);
1958 assert!((quat.1 - 0.0).abs() < 1e-6);
1959 assert!((quat.2 - 0.0).abs() < 1e-6);
1960 assert!((quat.3 - 0.00125).abs() < 1e-6);
1961 }
1962
1963 #[test]
1964 fn single_different_3D_quat() {
1965 let mut vqf = VQF::new(0.01, None, None, None);
1966
1967 let gyr = [0.054, 0.012, -0.9];
1968 vqf.update_gyr(gyr);
1969
1970 let quat = vqf.quat_3d();
1971 assert!((quat.0 - 0.99999).abs() < 1e-6);
1972 assert!((quat.1 - 0.000269999).abs() < 1e-6);
1973 assert!((quat.2 - 5.99998e-5).abs() < 1e-6);
1974 assert!((quat.3 - -0.00449998).abs() < 1e-6);
1975 }
1976
1977 #[test]
1978 fn many_same_3D_quat() {
1979 let mut vqf = VQF::new(0.01, None, None, None);
1980
1981 let gyr = [0.021, 0.021, 0.021];
1982 for _ in 0..10_000 {
1983 vqf.update_gyr(gyr);
1984 }
1985
1986 let quat = vqf.quat_3d();
1987 assert!((quat.0 - -0.245327).abs() < 1e-6); assert!((quat.1 - 0.559707).abs() < 1e-6);
1989 assert!((quat.2 - 0.559707).abs() < 1e-6);
1990 assert!((quat.3 - 0.559707).abs() < 1e-6);
1991 }
1992
1993 #[test]
1994 fn many_different_3D_quat() {
1995 let mut vqf = VQF::new(0.01, None, None, None);
1996
1997 let gyr = [0.054, 0.012, -0.09];
1998 for _ in 0..10_000 {
1999 vqf.update_gyr(gyr);
2000 }
2001
2002 let quat = vqf.quat_3d();
2003 assert!((quat.0 - 0.539342).abs() < 1e-6); assert!((quat.1 - -0.430446).abs() < 1e-6);
2005 assert!((quat.2 - -0.0956546).abs() < 1e-6);
2006 assert!((quat.3 - 0.71741).abs() < 1e-6);
2007 }
2008
2009 #[test]
2010 fn single_same_6D_quat() {
2011 let mut vqf = VQF::new(0.01, None, None, None);
2012
2013 let gyr = [0.021, 0.021, 0.021];
2014 let acc = [5.663806, 5.663806, 5.663806];
2015 vqf.update_gyr(gyr);
2016 vqf.update_acc(acc);
2017
2018 let quat = vqf.quat_6d();
2019 assert!((quat.0 - 0.888074).abs() < 1e-6);
2020 assert!((quat.1 - 0.325117).abs() < 1e-6);
2021 assert!((quat.2 - -0.324998).abs() < 1e-6);
2022 assert!((quat.3 - 0.00016151).abs() < 1e-6);
2023 }
2024
2025 #[test]
2026 fn single_x_6D_quat() {
2027 let mut vqf = VQF::new(0.01, None, None, None);
2028
2029 let gyr = [0.021, 0.021, 0.021];
2030 let acc = [9.81, 0.0, 0.0];
2031 vqf.update(gyr, acc, None);
2032
2033 let quat = vqf.quat_6d();
2034 assert!((quat.0 - 0.707107).abs() < 1e-6);
2035 assert!((quat.1 - 0.000148508).abs() < 1e-6);
2036 assert!((quat.2 - -0.707107).abs() < 1e-6);
2037 assert!((quat.3 - 0.000148508).abs() < 1e-6);
2038 }
2039
2040 #[test]
2041 fn single_y_6D_quat() {
2042 let mut vqf = VQF::new(0.01, None, None, None);
2043
2044 let gyr = [0.021, 0.021, 0.021];
2045 let acc = [0.0, 9.81, 0.0];
2046 vqf.update(gyr, acc, None);
2047
2048 let quat = vqf.quat_6d();
2049 assert!((quat.0 - 0.707107).abs() < 1e-6);
2050 assert!((quat.1 - 0.707107).abs() < 1e-6);
2051 assert!((quat.2 - 0.000148477).abs() < 1e-6);
2052 assert!((quat.3 - 0.000148477).abs() < 1e-6);
2053 }
2054
2055 #[test]
2056 fn single_z_6D_quat() {
2057 let mut vqf = VQF::new(0.01, None, None, None);
2058
2059 let gyr = [0.021, 0.021, 0.021];
2060 let acc = [0.0, 0.0, 9.81];
2061 vqf.update(gyr, acc, None);
2062
2063 let quat = vqf.quat_6d();
2064 assert!((quat.0 - 1.0).abs() < 1e-6);
2065 assert!((quat.1 - -1.72732e-20).abs() < 1e-6);
2066 assert!((quat.2 - -4.06576e-20).abs() < 1e-6);
2067 assert!((quat.3 - 0.000105).abs() < 1e-6);
2068 }
2069
2070 #[test]
2071 fn single_different_6D_quat() {
2072 let mut vqf = VQF::new(0.01, None, None, None);
2073
2074 let gyr = [0.021, 0.021, 0.021];
2075 let acc = [4.5, 6.7, 3.2];
2076 vqf.update(gyr, acc, None);
2077
2078 let quat = vqf.quat_6d();
2079 assert!((quat.0 - 0.827216).abs() < 1e-6);
2080 assert!((quat.1 - 0.466506).abs() < 1e-6);
2081 assert!((quat.2 - -0.313187).abs() < 1e-6);
2082 assert!((quat.3 - 0.000168725).abs() < 1e-6);
2083 }
2084
2085 #[test]
2086 fn many_same_6D_quat() {
2087 let mut vqf = VQF::new(0.01, None, None, None);
2088
2089 let gyr = [0.021, 0.021, 0.021];
2090 let acc = [5.663806, 5.663806, 5.663806];
2091
2092 for _ in 0..10_000 {
2093 vqf.update(gyr, acc, None);
2094 }
2095
2096 let quat = vqf.quat_6d();
2097 assert!((quat.0 - 0.887649).abs() < 1e-6); assert!((quat.1 - 0.334951).abs() < 1e-6); assert!((quat.2 - -0.314853).abs() < 1e-6); assert!((quat.3 - 0.0274545).abs() < 1e-6);
2101 }
2102
2103 #[test]
2104 fn many_different_6D_quat() {
2105 let mut vqf = VQF::new(0.01, None, None, None);
2106
2107 let gyr = [0.021, 0.021, 0.021];
2108 let acc = [4.5, 6.7, 3.2];
2109
2110 for _ in 0..10_000 {
2111 vqf.update(gyr, acc, None);
2112 }
2113
2114 let quat = vqf.quat_6d();
2115 assert!((quat.0 - 0.826852).abs() < 1e-6); assert!((quat.1 - 0.475521).abs() < 1e-6); assert!((quat.2 - -0.299322).abs() < 1e-6); assert!((quat.3 - 0.0245133).abs() < 1e-6);
2119 }
2120
2121 #[test]
2122 fn single_same_9D_quat() {
2123 let mut vqf = VQF::new(0.01, None, None, None);
2124
2125 let gyr = [0.021, 0.021, 0.021];
2126 let acc = [5.663806, 5.663806, 5.663806];
2127 let mag = [10.0, 10.0, 10.0];
2128 vqf.update_gyr(gyr);
2129 vqf.update_acc(acc);
2130 vqf.update_mag(mag);
2131
2132 let quat = vqf.quat_9d();
2133 assert!((quat.0 - 0.86428).abs() < 1e-6);
2134 assert!((quat.1 - 0.391089).abs() < 1e-6);
2135 assert!((quat.2 - -0.241608).abs() < 1e-6);
2136 assert!((quat.3 - 0.204195).abs() < 1e-6);
2137 }
2138
2139 #[test]
2140 fn single_x_9D_quat() {
2141 let mut vqf = VQF::new(0.01, None, None, None);
2142
2143 let gyr = [0.021, 0.021, 0.021];
2144 let acc = [5.663806, 5.663806, 5.663806];
2145 let mag = [10.0, 0.0, 0.0];
2146 vqf.update(gyr, acc, Some(mag));
2147
2148 let quat = vqf.quat_9d();
2149 assert!((quat.0 - 0.540625).abs() < 1e-6);
2150 assert!((quat.1 - 0.455768).abs() < 1e-6);
2151 assert!((quat.2 - 0.060003).abs() < 1e-6);
2152 assert!((quat.3 - 0.704556).abs() < 1e-6);
2153 }
2154
2155 #[test]
2156 fn single_y_9D_quat() {
2157 let mut vqf = VQF::new(0.01, None, None, None);
2158
2159 let gyr = [0.021, 0.021, 0.021];
2160 let acc = [5.663806, 5.663806, 5.663806];
2161 let mag = [0.0, 10.0, 0.0];
2162 vqf.update(gyr, acc, Some(mag));
2163
2164 let quat = vqf.quat_9d();
2165 assert!((quat.0 - 0.880476).abs() < 1e-6);
2166 assert!((quat.1 - 0.279848).abs() < 1e-6);
2167 assert!((quat.2 - -0.364705).abs() < 1e-6);
2168 assert!((quat.3 - -0.115917).abs() < 1e-6);
2169 }
2170
2171 #[test]
2172 fn single_z_9D_quat() {
2173 let mut vqf = VQF::new(0.01, None, None, None);
2174
2175 let gyr = [0.021, 0.021, 0.021];
2176 let acc = [5.663806, 5.663806, 5.663806];
2177 let mag = [0.0, 0.0, 10.0];
2178 vqf.update(gyr, acc, Some(mag));
2179
2180 let quat = vqf.quat_9d();
2181 assert!((quat.0 - 0.339851).abs() < 1e-6);
2182 assert!((quat.1 - -0.17592).abs() < 1e-6);
2183 assert!((quat.2 - -0.424708).abs() < 1e-6);
2184 assert!((quat.3 - -0.820473).abs() < 1e-6);
2185 }
2186
2187 #[test]
2188 fn single_different_9D_quat() {
2189 let mut vqf = VQF::new(0.01, None, None, None);
2190
2191 let gyr = [0.021, 0.021, 0.021];
2192 let acc = [5.663806, 5.663806, 5.663806];
2193 let mag = [3.54, 6.32, -2.34];
2194 vqf.update(gyr, acc, Some(mag));
2195
2196 let quat = vqf.quat_9d();
2197 assert!((quat.0 - 0.864117).abs() < 1e-6);
2198 assert!((quat.1 - 0.391281).abs() < 1e-6);
2199 assert!((quat.2 - -0.241297).abs() < 1e-6);
2200 assert!((quat.3 - 0.204882).abs() < 1e-6);
2201 }
2202
2203 #[test]
2204 fn many_same_9D_quat() {
2205 let mut vqf = VQF::new(0.01, None, None, None);
2206
2207 let gyr = [0.021, 0.021, 0.021];
2208 let acc = [5.663806, 5.663806, 5.663806];
2209 let mag = [10.0, 10.0, 10.0];
2210
2211 for _ in 0..10_000 {
2212 vqf.update(gyr, acc, Some(mag));
2213 }
2214
2215 let quat = vqf.quat_9d();
2216 assert!((quat.0 - 0.338005).abs() < 1e-6); assert!((quat.1 - -0.176875).abs() < 1e-6); assert!((quat.2 - -0.424311).abs() < 1e-6); assert!((quat.3 - -0.821236).abs() < 1e-6);
2220 }
2221
2222 #[test]
2223 fn many_different_9D_quat() {
2224 let mut vqf = VQF::new(0.01, None, None, None);
2225
2226 let gyr = [0.021, 0.021, 0.021];
2227 let acc = [5.663806, 5.663806, 5.663806];
2228 let mag = [3.54, 6.32, -2.34];
2229
2230 for _ in 0..10_000 {
2231 vqf.update(gyr, acc, Some(mag));
2232 }
2233
2234 let quat = vqf.quat_9d();
2235 assert!((quat.0 - 0.864111).abs() < 1e-6); assert!((quat.1 - 0.391288).abs() < 1e-6); assert!((quat.2 - -0.241286).abs() < 1e-6); assert!((quat.3 - 0.204906).abs() < 1e-6);
2239 }
2240
2241 #[test]
2242 fn run_vqf_cpp_example() {
2243 let mut vqf = VQF::new(0.01, None, None, None);
2244
2245 let gyr = [0.01745329; 3];
2246 let acc = [5.663806; 3];
2247
2248 for _ in 0..6000 {
2249 vqf.update(gyr, acc, None);
2250 }
2251
2252 let quat = vqf.quat_6d();
2253 assert!((quat.0 - 0.887781).abs() < 1e-6);
2254 assert!((quat.1 - 0.333302).abs() < 1e-6);
2255 assert!((quat.2 - -0.316598).abs() < 1e-6);
2256 assert!((quat.3 - 0.0228175).abs() < 1e-6);
2257 }
2258
2259 #[test]
2260 fn run_vqf_cpp_example_basic() {
2261 let mut vqf = VQF::new(0.01, None, None, Some(Params {
2262 rest_bias_est_enabled: false,
2263 motion_bias_est_enabled: false,
2264 mag_dist_rejection_enabled: false,
2265 ..Default::default()
2266 }));
2267
2268 let gyr = [0.01745329; 3];
2269 let acc = [5.663806; 3];
2270
2271 for _ in 0..6000 {
2272 vqf.update(gyr, acc, None);
2273 }
2274
2275 let quat = vqf.quat_6d();
2276 assert!((quat.0 - 0.547223).abs() < 1e-6);
2277 assert!((quat.1 - 0.456312).abs() < 1e-6);
2278 assert!((quat.2 - 0.055717).abs() < 1e-6);
2279 assert!((quat.3 - 0.699444).abs() < 1e-6);
2280 }
2281 }
2282}