sensor_fusion/sensor_fusion.rs
1use vqm::{Quaternion, Quaternionf32, Vector3};
2
3/// Common interface for the sensor fusion filters (Madgwick, Mahony, complementary).
4/// ```
5/// use vqm::{Vector3f32,Quaternionf32};
6/// use sensor_fusion::{MadgwickFilterf32, SensorFusion};
7///
8/// let mut madgwick_filter = MadgwickFilterf32::default();
9///
10/// let delta_t: f32 = 0.0;
11/// let acc = Vector3f32::default();
12/// let gyro_rps = Vector3f32::default();
13///
14/// let orientation = madgwick_filter.fuse_acc_gyro(acc, gyro_rps, delta_t);
15/// assert!(orientation.is_near_one(1.2e-4));
16/// ```
17pub trait SensorFusion<T> {
18 fn set_gains(&mut self, gain0: T, gain1: T);
19 fn gains(&self) -> (T, T);
20
21 fn requires_initialization() -> bool;
22
23 fn fuse_acc_gyro(&mut self, acc: Vector3<T>, gyro_rps: Vector3<T>, delta_t: T) -> Quaternion<T>;
24 fn fuse_acc_gyro_mag(&mut self, acc: Vector3<T>, gyro: Vector3<T>, mag: Vector3<T>, delta_t: T) -> Quaternion<T>;
25 fn correct_yaw_with_gain(&mut self, yaw_radians: T, gain: T, delta_t: T) -> Quaternion<T>;
26}
27
28/// Trait to allow sensor fusion filters to be used with method-call syntax, ie:<br>
29/// `let orientation = (acc, gyro_rps).fuse_acc_gyro_using(&mut madgwick, dt);`
30/// ```
31/// use vqm::{Vector3f32, Quaternionf32};
32/// use sensor_fusion::{MadgwickFilterf32, SensorFusion, FuseAccGyro};
33///
34/// let mut madgwick_filter = MadgwickFilterf32::default();
35///
36/// let delta_t: f32 = 0.0;
37/// let acc = Vector3f32::default();
38/// let gyro_rps = Vector3f32::default();
39///
40/// let orientation = (acc, gyro_rps).fuse_acc_gyro_using(&mut madgwick_filter, delta_t);
41/// assert!(orientation.is_near_one(1.2e-4));
42/// ```
43pub trait FuseAccGyro<T> {
44 fn fuse_acc_gyro_using<F: SensorFusion<T>>(self, filter: &mut F, delta_t: T) -> Quaternion<T>;
45}
46
47impl<T> FuseAccGyro<T> for (Vector3<T>, Vector3<T>) {
48 fn fuse_acc_gyro_using<F: SensorFusion<T>>(self, filter: &mut F, delta_t: T) -> Quaternion<T> {
49 let (acc, gyro) = self;
50 filter.fuse_acc_gyro(acc, gyro, delta_t)
51 }
52}
53
54/// Trait to allow sensor fusion filters to be used with method-call syntax, ie:<br>
55/// `let orientation = (acc, gyro_rps).fuse_acc_gyro_using(&mut madgwick, dt);`
56/// ```
57/// use vqm::{Vector3f32,Quaternionf32};
58/// use sensor_fusion::{MadgwickFilterf32,SensorFusion,FuseAccGyroMag};
59/// let mut madgwick_filter = MadgwickFilterf32::default();
60/// let dt: f32 = 0.0;
61/// let acc = Vector3f32::default();
62/// let gyro_rps = Vector3f32::default();
63/// let mag = Vector3f32::default();
64/// let orientation = (acc, gyro_rps, mag).fuse_acc_gyro_mag_using(&mut madgwick_filter, dt);
65/// assert!(orientation.is_near_one(1.2e-4));
66/// ```
67pub trait FuseAccGyroMag<T> {
68 fn fuse_acc_gyro_mag_using<F: SensorFusion<T>>(self, sensor_fusion_filter: &mut F, delta_t: T) -> Quaternion<T>;
69}
70
71impl<T> FuseAccGyroMag<T> for (Vector3<T>, Vector3<T>, Vector3<T>) {
72 fn fuse_acc_gyro_mag_using<F: SensorFusion<T>>(self, sensor_fusion_filter: &mut F, delta_t: T) -> Quaternion<T> {
73 let (acc, gyro, mag) = self;
74 sensor_fusion_filter.fuse_acc_gyro_mag(acc, gyro, mag, delta_t)
75 }
76}
77
78/*
79/// Calculate quaternion derivative (dq/dt aka q_dot) from angular rate <https://ahrs.readthedocs.io/en/latest/filters/angular.html#quaternion-derivative>
80pub fn q_dot<T>(q: Quaternion<T>, gyro_rps: Vector3<T>) -> Quaternion<T>
81where
82 T: Copy + One + Neg<Output = T> + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
83{
84 let half = T::one() / (T::one() + T::one());
85 Quaternion {
86 w: (-q.x * gyro_rps.x - q.y * gyro_rps.y - q.z * gyro_rps.z) * half,
87 x: (q.w * gyro_rps.x + q.y * gyro_rps.z - q.z * gyro_rps.y) * half,
88 y: (q.w * gyro_rps.y - q.x * gyro_rps.z + q.z * gyro_rps.x) * half,
89 z: (q.w * gyro_rps.z + q.x * gyro_rps.y - q.y * gyro_rps.x) * half,
90 }
91}*/
92
93#[cfg(any(debug_assertions, test))]
94mod tests {
95 #![allow(clippy::wildcard_imports)]
96 use super::*;
97 use vqm::Vector3f32;
98
99 #[allow(unused)]
100 struct TestStruct;
101
102 impl SensorFusion<f32> for TestStruct {
103 fn set_gains(&mut self, _gain0: f32, _gain1: f32) {}
104 fn gains(&self) -> (f32, f32) {
105 (0.0, 0.0)
106 }
107
108 fn requires_initialization() -> bool {
109 true
110 }
111 fn fuse_acc_gyro(&mut self, _acc: Vector3f32, _gyro_rps: Vector3f32, _delta_t: f32) -> Quaternionf32 {
112 Quaternionf32::default()
113 }
114 fn fuse_acc_gyro_mag(
115 &mut self,
116 acc: Vector3f32,
117 gyro_rps: Vector3f32,
118 _mag: Vector3f32,
119 delta_t: f32,
120 ) -> Quaternionf32 {
121 self.fuse_acc_gyro(acc, gyro_rps, delta_t)
122 }
123 fn correct_yaw_with_gain(&mut self, _yaw_radians: f32, _gain: f32, _delta_t: f32) -> Quaternionf32 {
124 Quaternionf32::default()
125 }
126 }
127
128 #[test]
129 fn sensor_fusion() {
130 let mut test_struct: TestStruct = TestStruct {};
131 _ = TestStruct::requires_initialization();
132 //assert_eq!(TestStruct::requires_initialization(), true);
133
134 test_struct.set_gains(0.0, 0.0);
135
136 let delta_t: f32 = 0.0;
137 let acc = Vector3f32::default();
138 let gyro_rps = Vector3f32::default();
139
140 let orientation = test_struct.fuse_acc_gyro(acc, gyro_rps, delta_t);
141 assert_eq!(orientation, Quaternion::default());
142 }
143
144 #[test]
145 fn fuse_using() {
146 use crate::MadgwickFilterf32;
147
148 let mut madgwick_filter = MadgwickFilterf32::default();
149 let requires_initialization = MadgwickFilterf32::requires_initialization();
150 assert!(requires_initialization);
151
152 madgwick_filter.set_beta(1.0);
153
154 let delta_t: f32 = 0.0;
155 let acc = Vector3f32::default();
156 let gyro_rps = Vector3f32::default();
157
158 //let orientation = madgwick_filter.fuse_acc_gyro(acc, gyro_rps, delta_t);
159 let orientation = (acc, gyro_rps).fuse_acc_gyro_using(&mut madgwick_filter, delta_t);
160 //assert_eq!(orientation, Quaternion { w: 1.0, x: 0.0, y: 0.0, z: 0.0 });
161 assert!(orientation.is_near_one(1.2e-4));
162 }
163}