1#![allow(non_snake_case)]
2#![allow(clippy::many_single_char_names)]
3
4use crate::ahrs::{Ahrs, AhrsError};
5use core::hash;
6use nalgebra::{Quaternion, Scalar, UnitQuaternion, Vector2, Vector3};
7use simba::simd::{SimdRealField as RealField, SimdRealField, SimdValue};
8
9#[derive(Debug)]
20pub struct Mahony<N: Scalar + SimdValue + Copy> {
21 sample_period: N,
23 kp: N,
25 ki: N,
27 e_int: Vector3<N>,
29 pub quat: UnitQuaternion<N>,
31}
32
33impl<N: SimdRealField + Eq + Copy> Eq for Mahony<N> where N::Element: SimdRealField {}
34
35impl<N: SimdRealField + Copy> PartialEq for Mahony<N>
36where
37 N::Element: SimdRealField,
38{
39 fn eq(&self, rhs: &Self) -> bool {
40 self.sample_period == rhs.sample_period
41 && self.kp == rhs.kp
42 && self.ki == rhs.ki
43 && self.e_int == rhs.e_int
44 && self.quat == rhs.quat
45 }
46}
47
48impl<N: SimdRealField + hash::Hash + Copy> hash::Hash for Mahony<N> {
49 fn hash<H: hash::Hasher>(&self, state: &mut H) {
50 self.sample_period.hash(state);
51 self.kp.hash(state);
52 self.ki.hash(state);
53 self.e_int.hash(state);
54 self.quat.hash(state);
55 }
56}
57
58impl<N: Scalar + Copy + SimdValue> Copy for Mahony<N> {}
59
60impl<N: Scalar + SimdValue + Copy> Clone for Mahony<N> {
61 #[inline]
62 fn clone(&self) -> Self {
63 *self
64 }
65}
66
67impl Default for Mahony<f64> {
68 fn default() -> Mahony<f64> {
86 Mahony {
87 sample_period: (1.0f64) / (256.0),
88 kp: 0.5f64,
89 ki: 0.0f64,
90 e_int: Vector3::new(0.0, 0.0, 0.0),
91 quat: UnitQuaternion::new_unchecked(Quaternion::new(1.0f64, 0.0, 0.0, 0.0)),
92 }
93 }
94}
95
96impl<N: RealField + Copy> Mahony<N> {
97 pub fn new(sample_period: N, kp: N, ki: N) -> Self {
105 Mahony::new_with_quat(
106 sample_period,
107 kp,
108 ki,
109 UnitQuaternion::new_unchecked(Quaternion::new(
110 N::one(),
111 N::zero(),
112 N::zero(),
113 N::zero(),
114 )),
115 )
116 }
117
118 pub fn new_with_quat(sample_period: N, kp: N, ki: N, quat: UnitQuaternion<N>) -> Self {
127 Mahony {
128 sample_period,
129 kp,
130 ki,
131 e_int: nalgebra::zero(),
132 quat,
133 }
134 }
135}
136
137#[cfg(feature = "field_access")]
138impl<N: Scalar + SimdValue + Copy> Mahony<N> {
139 pub fn sample_period(&self) -> N {
141 self.sample_period
142 }
143
144 pub fn sample_period_mut(&mut self) -> &mut N {
146 &mut self.sample_period
147 }
148
149 pub fn kp(&self) -> N {
151 self.kp
152 }
153
154 pub fn kp_mut(&mut self) -> &mut N {
156 &mut self.kp
157 }
158
159 pub fn ki(&self) -> N {
161 self.ki
162 }
163
164 pub fn ki_mut(&mut self) -> &mut N {
166 &mut self.ki
167 }
168
169 pub fn e_int(&self) -> Vector3<N> {
171 self.e_int
172 }
173
174 pub fn e_int_mut(&mut self) -> &mut Vector3<N> {
176 &mut self.e_int
177 }
178
179 pub fn quat(&self) -> UnitQuaternion<N> {
181 self.quat
182 }
183
184 pub fn quat_mut(&mut self) -> &mut UnitQuaternion<N> {
186 &mut self.quat
187 }
188}
189
190impl<N: simba::scalar::RealField + Copy> Ahrs<N> for Mahony<N> {
191 fn update(
192 &mut self,
193 gyroscope: &Vector3<N>,
194 accelerometer: &Vector3<N>,
195 magnetometer: &Vector3<N>,
196 ) -> Result<&UnitQuaternion<N>, AhrsError> {
197 let q = self.quat.as_ref();
198
199 let zero: N = nalgebra::zero();
200 let two: N = nalgebra::convert(2.0);
201 let half: N = nalgebra::convert(0.5);
202
203 let Some(accel) = accelerometer.try_normalize(zero) else {
205 return Err(AhrsError::AccelerometerNormZero);
206 };
207
208 let Some(mag) = magnetometer.try_normalize(zero) else {
210 return Err(AhrsError::MagnetometerNormZero);
211 };
212
213 let h = q * (Quaternion::from_parts(zero, mag) * q.conjugate());
215 let b = Quaternion::new(zero, Vector2::new(h[0], h[1]).norm(), zero, h[2]);
216
217 #[rustfmt::skip]
218 let v = Vector3::new(
219 two*( q[0]*q[2] - q[3]*q[1] ),
220 two*( q[3]*q[0] + q[1]*q[2] ),
221 q[3]*q[3] - q[0]*q[0] - q[1]*q[1] + q[2]*q[2]
222 );
223
224 #[rustfmt::skip]
225 let w = Vector3::new(
226 two*b[0]*(half - q[1]*q[1] - q[2]*q[2]) + two*b[2]*(q[0]*q[2] - q[3]*q[1]),
227 two*b[0]*(q[0]*q[1] - q[3]*q[2]) + two*b[2]*(q[3]*q[0] + q[1]*q[2]),
228 two*b[0]*(q[3]*q[1] + q[0]*q[2]) + two*b[2]*(half - q[0]*q[0] - q[1]*q[1])
229 );
230
231 let e: Vector3<N> = accel.cross(&v) + mag.cross(&w);
233
234 self.e_int += e * self.sample_period;
236
237 let gyro = *gyroscope + e * self.kp + self.e_int * self.ki;
239
240 let qDot = q * Quaternion::from_parts(zero, gyro) * half;
242
243 self.quat = UnitQuaternion::from_quaternion(q + qDot * self.sample_period);
245
246 Ok(&self.quat)
247 }
248
249 fn update_imu(
250 &mut self,
251 gyroscope: &Vector3<N>,
252 accelerometer: &Vector3<N>,
253 ) -> Result<&UnitQuaternion<N>, AhrsError> {
254 let q = self.quat.as_ref();
255
256 let zero: N = nalgebra::zero();
257 let two: N = nalgebra::convert(2.0);
258 let half: N = nalgebra::convert(0.5);
259
260 let Some(accel) = accelerometer.try_normalize(zero) else {
262 return Err(AhrsError::AccelerometerNormZero);
263 };
264
265 #[rustfmt::skip]
266 let v = Vector3::new(
267 two*( q[0]*q[2] - q[3]*q[1] ),
268 two*( q[3]*q[0] + q[1]*q[2] ),
269 q[3]*q[3] - q[0]*q[0] - q[1]*q[1] + q[2]*q[2]
270 );
271
272 let e = accel.cross(&v);
274
275 self.e_int += e * self.sample_period;
277
278 let gyro = *gyroscope + e * self.kp + self.e_int * self.ki;
280
281 let qDot = q * Quaternion::from_parts(zero, gyro) * half;
283
284 self.quat = UnitQuaternion::from_quaternion(q + qDot * self.sample_period);
286
287 Ok(&self.quat)
288 }
289
290 fn update_gyro(
291 &mut self,
292 gyroscope: &Vector3<N>
293 ) -> &UnitQuaternion<N> {
294 let q = self.quat.as_ref();
295
296 let zero: N = nalgebra::zero();
297 let half: N = nalgebra::convert(0.5);
298
299 let qDot = q * Quaternion::from_parts(zero, *gyroscope) * half;
301
302 self.quat = UnitQuaternion::from_quaternion(q + qDot * self.sample_period);
304
305 &self.quat
306 }
307}