1pub mod earth;
53pub mod filter;
54
55use std::fmt::Debug;
56use angle::Deg; use nalgebra::{Matrix3, Rotation3, SVector, Vector3};
58
59#[derive(Clone, Copy, Debug)]
66pub struct IMUData {
67 pub accel: Vector3<f64>, pub gyro: Vector3<f64>, }
70impl IMUData {
71 pub fn new() -> IMUData {
73 IMUData {
74 accel: Vector3::zeros(),
75 gyro: Vector3::zeros(),
76 }
77 }
78}
79
80#[derive(Clone, Copy)]
86pub struct StrapdownState {
87 pub position: Vector3<f64>, pub velocity: Vector3<f64>, pub attitude: Rotation3<f64>, }
92
93impl Debug for StrapdownState {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 let (roll, pitch, yaw) = self.attitude.euler_angles();
96 write!(
97 f,
98 "StrapdownState {{ position: [{:.4}, {:.4}, {:.4}], velocity: [{:.4}, {:.4}, {:.4}], attitude: [{:.4}, {:.4}, {:.4}] }}",
99 self.position[0].to_degrees(),
100 self.position[1].to_degrees(),
101 self.position[2],
102 self.velocity[0],
103 self.velocity[1],
104 self.velocity[2],
105 roll.to_degrees(),
106 pitch.to_degrees(),
107 yaw.to_degrees()
108 )
109 }
110}
111
112impl StrapdownState {
113 pub fn new() -> StrapdownState {
115 StrapdownState {
116 position: Vector3::zeros(),
117 velocity: Vector3::zeros(),
118 attitude: Rotation3::identity(),
119 }
121 }
122 pub fn new_from(
125 position: Vector3<f64>,
126 velocity: Vector3<f64>,
127 attitude: Vector3<f64>,
128 ) -> StrapdownState {
129 StrapdownState {
130 attitude: Rotation3::from_euler_angles(
131 attitude[0].to_radians(),
132 attitude[1].to_radians(),
133 attitude[2].to_radians(),
134 ),
135 velocity: velocity,
136 position: position,
137 }
139 }
140 pub fn new_from_vector(state: SVector<f64, 9>) -> StrapdownState {
146 StrapdownState {
147 attitude: Rotation3::from_euler_angles(state[6], state[7], state[8]),
148 velocity: Vector3::new(state[3], state[4], state[5]),
149 position: Vector3::new(state[0], state[1], state[2]),
150 }
152 }
153 fn attitude_update(&self, gyros: &Vector3<f64>, dt: f64) -> Matrix3<f64> {
155 let transport_rate: Matrix3<f64> = earth::vector_to_skew_symmetric(&earth::transport_rate(
156 &self.position[0],
157 &self.position[2],
158 &self.velocity,
159 ));
160 let rotation_rate: Matrix3<f64> =
161 earth::vector_to_skew_symmetric(&earth::earth_rate_lla(&self.position[0]));
162 let omega_ib: Matrix3<f64> = earth::vector_to_skew_symmetric(&gyros);
163 let c_1: Matrix3<f64> = &self.attitude * (Matrix3::identity() + omega_ib * dt)
164 - (rotation_rate + transport_rate) * &self.attitude * dt;
165 return c_1;
166 }
167 fn velocity_update(&self, f_1: &Vector3<f64>, dt: f64) -> Vector3<f64> {
169 let transport_rate: Matrix3<f64> = earth::vector_to_skew_symmetric(&earth::transport_rate(
170 &self.position[0],
171 &self.position[2],
172 &self.velocity,
173 ));
174 let rotation_rate: Matrix3<f64> =
175 earth::vector_to_skew_symmetric(&earth::earth_rate_lla(&self.position[0]));
176 let r = earth::ecef_to_lla(&self.position[0], &self.position[1]);
177 let grav: Vector3<f64> = earth::gravitation(&self.position[0], &self.position[1], &self.position[2]);
178 let v_1: Vector3<f64> = &self.velocity
179 + (f_1 + grav - r * (transport_rate + 2.0 * rotation_rate) * &self.velocity) * dt;
180 return v_1;
181 }
182
183 pub fn forward(&mut self, imu_data: &IMUData, dt: f64) {
185 let c_0: Rotation3<f64> = self.attitude.clone();
187 let c_1: Matrix3<f64> = self.attitude_update(&imu_data.gyro, dt);
189 let f_1: Vector3<f64> = 0.5 * (c_0.matrix() + c_1) * imu_data.accel;
191 let v_0: Vector3<f64> = self.velocity.clone();
193 let v_1: Vector3<f64> = self.velocity_update(&f_1, dt);
194 let (r_n, r_e_0, _) = earth::principal_radii(&self.position[0], &self.position[2]);
196 let lat_0: f64 = self.position[0].clone().to_radians();
197 let alt_0: f64 = self.position[2].clone();
198 self.position[2] += 0.5 * (v_0[2] + v_1[2]) * dt;
200 let lat_1: f64 = &self.position[0].to_radians()
202 + 0.5 * (v_0[0] / (r_n + alt_0) + v_1[0] / (r_n + &self.position[2])) * dt;
203 let (_, r_e_1, _) = earth::principal_radii(&lat_1, &self.position[2]);
205 let lon_1: f64 = &self.position[1].to_radians()
206 + 0.5
207 * (v_0[1] / ((r_e_0 + alt_0) * lat_0.cos())
208 + v_1[1] / ((r_e_1 + &self.position[2]) * &lat_1.cos()))
209 * dt;
210 self.position[0] = lat_1.to_degrees();
212 self.position[1] = lon_1.to_degrees();
213 self.attitude = Rotation3::from_matrix(&c_1);
215 self.velocity = v_1;
217 }
218
219 pub fn to_vector(&self, in_degrees: bool) -> SVector<f64, 9> {
229 let mut state: SVector<f64, 9> = SVector::zeros();
230 state[2] = self.position[2];
231 state[3] = self.velocity[0];
232 state[4] = self.velocity[1];
233 state[5] = self.velocity[2];
234 let (roll, pitch, yaw) = &self.attitude.euler_angles();
235 if in_degrees {
236 state[0] = self.position[0].to_degrees();
237 state[1] = self.position[1].to_degrees();
238 state[6] = Deg(roll.to_degrees()).0;
239 state[7] = Deg(pitch.to_degrees()).0;
240 state[8] = Deg(yaw.to_degrees()).0;
241 } else {
242 state[6] = *roll;
243 state[7] = *pitch;
244 state[8] = *yaw;
245 }
246 return state;
247 }
248}
249
250pub fn wrap_to_180<T>(angle: T) -> T
255where
256 T: PartialOrd + Copy + std::ops::SubAssign + std::ops::AddAssign + From<f64>,
257{
258 let mut wrapped: T = angle;
259 while wrapped > T::from(180.0) {
260 wrapped -= T::from(360.0);
261 }
262 while wrapped < T::from(-180.0) {
263 wrapped += T::from(360.0);
264 }
265 return wrapped;
266}
267
268pub fn wrap_to_360<T>(angle: T) -> T
271where
272 T: PartialOrd + Copy + std::ops::SubAssign + std::ops::AddAssign + From<f64>,
273{
274 let mut wrapped: T = angle;
275 while wrapped > T::from(360.0) {
276 wrapped -= T::from(360.0);
277 }
278 while wrapped < T::from(0.0) {
279 wrapped += T::from(360.0);
280 }
281 return wrapped;
282}
283pub fn wrap_to_pi<T>(angle: T) -> T
286where
287 T: PartialOrd + Copy + std::ops::SubAssign + std::ops::AddAssign + From<f64>,
288{
289 let mut wrapped: T = angle;
290 while wrapped > T::from(std::f64::consts::PI) {
291 wrapped -= T::from(2.0 * std::f64::consts::PI);
292 }
293 while wrapped < T::from(-std::f64::consts::PI) {
294 wrapped += T::from(2.0 * std::f64::consts::PI);
295 }
296 return wrapped;
297}
298pub fn wrap_to_2pi<T>(angle: T) -> T
301where
302 T: PartialOrd + Copy + std::ops::SubAssign + std::ops::AddAssign + From<f64>,
303 T: PartialOrd + Copy + std::ops::SubAssign + std::ops::AddAssign + From<i32>,
304{
305 let mut wrapped: T = angle;
306 while wrapped > T::from(2.0 * std::f64::consts::PI) {
307 wrapped -= T::from(2.0 * std::f64::consts::PI);
308 }
309 while wrapped < T::from(0.0) {
310 wrapped += T::from(2.0 * std::f64::consts::PI);
311 }
312 return wrapped;
313}
314
315#[cfg(test)]
318mod tests {
319 use super::*;
320 use assert_approx_eq::assert_approx_eq;
321
322 #[test]
323 fn test_strapdown_state_new() {
324 let state = StrapdownState::new();
325 assert_eq!(state.position, Vector3::zeros());
326 assert_eq!(state.velocity, Vector3::zeros());
327 assert_eq!(state.attitude, Rotation3::identity());
328 }
329 #[test]
330 fn test_to_vector_zeros() {
331 let state = StrapdownState::new();
332 let state_vector = state.to_vector(true);
333 let zeros: SVector<f64, 9> = SVector::zeros();
334 assert_eq!(state_vector, zeros);
335 }
336 #[test]
337 fn test_new_from_vector() {
338 let roll: f64 = 15.0;
339 let pitch: f64 = 45.0;
340 let yaw: f64 = 90.0;
341 let state_vector: SVector<f64, 9> = nalgebra::vector![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, roll.to_radians(), pitch.to_radians(), yaw.to_radians()];
342 let state: StrapdownState = StrapdownState::new_from_vector(state_vector);
343 assert_eq!(state.position, Vector3::new(0.0, 0.0, 0.0));
344 assert_eq!(state.velocity, Vector3::new(0.0, 0.0, 0.0));
345 let eulers = state.attitude.euler_angles();
346 assert_approx_eq!(eulers.0.to_degrees(), roll, 1e-6);
347 assert_approx_eq!(eulers.1.to_degrees(), pitch, 1e-6);
348 assert_approx_eq!(eulers.2.to_degrees(), yaw, 1e-6);
349 }
350 #[test]
351 fn test_dcm_to_vector() {
352 let state: StrapdownState = StrapdownState::new_from(
353 Vector3::new(0.0, 1.0, 2.0),
354 Vector3::new(0.0, 15.0, 45.0),
355 Vector3::new(0.0, 0.0, 0.0),
356 );
357 let state_vector = state.to_vector(true);
358 assert_eq!(state_vector[0], 0.0);
359 assert_eq!(state_vector[1], (1.0_f64).to_degrees());
360 assert_eq!(state_vector[2], 2.0);
361 assert_eq!(state_vector[3], 0.0);
362 assert_eq!(state_vector[4], 15.0);
363 assert_eq!(state_vector[5], 45.0);
364 assert_eq!(state_vector[6], 0.0);
365 assert_eq!(state_vector[7], 0.0);
366 assert_eq!(state_vector[8], 0.0);
367 }
368 #[test]
369 fn test_attitude_update_no_motion() {
371 let gyros = Vector3::new(0.0, 0.0, 0.0);
372 let state: StrapdownState = StrapdownState::new();
373 let dt = 1.0;
374 let new_attitude = state.attitude_update(&gyros, dt);
375 assert_approx_eq!(new_attitude[(0, 0)], 1.0, 1e-3);
376 assert_approx_eq!(new_attitude[(0, 1)], 0.0, 1e-3);
377 assert_approx_eq!(new_attitude[(0, 2)], 0.0, 1e-3);
378 assert_approx_eq!(new_attitude[(1, 0)], 0.0, 1e-3);
379 assert_approx_eq!(new_attitude[(1, 1)], 1.0, 1e-3);
380 assert_approx_eq!(new_attitude[(1, 2)], 0.0, 1e-3);
381 assert_approx_eq!(new_attitude[(2, 0)], 0.0, 1e-3);
382 assert_approx_eq!(new_attitude[(2, 1)], 0.0, 1e-3);
383 assert_approx_eq!(new_attitude[(2, 2)], 1.0, 1e-3);
384 }
385 #[test]
386 fn test_attitude_update_yawing() {
387 let gyros = Vector3::new(0.0, 0.0, 0.1);
388 let state: StrapdownState = StrapdownState::new();
389 let dt = 1.0;
390 let new_attitude = state.attitude_update(&gyros, dt);
391 let eulers = Rotation3::from_matrix(&new_attitude).euler_angles();
392 assert_approx_eq!(eulers.0, 0.0, 1e-3);
393 assert_approx_eq!(eulers.1, 0.0, 1e-3);
394 assert_approx_eq!(eulers.2, 0.1, 1e-3);
395 }
396 #[test]
397 fn test_attitude_update_rolling() {
398 let gyros = Vector3::new(0.1, 0.0, 0.0);
399 let state: StrapdownState = StrapdownState::new();
400 let dt = 1.0;
401 let new_attitude = state.attitude_update(&gyros, dt);
402 let eulers = Rotation3::from_matrix(&new_attitude).euler_angles();
403 assert_approx_eq!(eulers.0, 0.1, 1e-3);
404 assert_approx_eq!(eulers.1, 0.0, 1e-3);
405 assert_approx_eq!(eulers.2, 0.0, 1e-3);
406 }
407 #[test]
408 fn test_attitude_update_pitching() {
409 let gyros = Vector3::new(0.0, 0.1, 0.0);
410 let state: StrapdownState = StrapdownState::new();
411 let dt = 1.0;
412 let new_attitude = state.attitude_update(&gyros, dt);
413 let eulers = Rotation3::from_matrix(&new_attitude).euler_angles();
414 assert_approx_eq!(eulers.0, 0.0, 1e-3);
415 assert_approx_eq!(eulers.1, 0.1, 1e-3);
416 assert_approx_eq!(eulers.2, 0.0, 1e-3);
417 }
418
419 #[test]
420 fn test_forward_freefall() {
422 let mut state: StrapdownState = StrapdownState::new_from(
423 Vector3::new(0.0, 0.0, 0.0),
424 Vector3::new(0.0, 0.0, 0.0),
425 Vector3::new(0.0, 0.0, 0.0),
426 );
427 let imu_data = IMUData {
428 accel: Vector3::new(0.0, 0.0, 0.0), gyro: Vector3::new(0.0, 0.0, 0.0),
430 };
431 state.forward(&imu_data, 1.0);
432 assert_approx_eq!(state.velocity[0], 0.00, 1e-6);
433 assert_approx_eq!(state.velocity[1], 0.0004, 1e-3);
434 assert_approx_eq!(state.velocity[2], 9.8142, 1e-3);
435
436 assert_approx_eq!(state.position[0], 0.0);
437 assert_approx_eq!(state.position[1], 0.0);
438 assert_approx_eq!(state.position[2], 4.9071, 1e-3);
439
440 let attitude = state.attitude.matrix();
441 assert_approx_eq!(&attitude[(0, 0)], 1.0, 1e-4);
442 assert_approx_eq!(&attitude[(0, 1)], 0.0, 1e-4);
443 assert_approx_eq!(&attitude[(0, 2)], 0.0, 1e-4);
444 assert_approx_eq!(&attitude[(1, 0)], 0.0, 1e-4);
445 assert_approx_eq!(&attitude[(1, 1)], 1.0, 1e-4);
446 assert_approx_eq!(&attitude[(1, 2)], 0.0, 1e-4);
447 assert_approx_eq!(&attitude[(2, 0)], 0.0, 1e-4);
448 assert_approx_eq!(&attitude[(2, 1)], 0.0, 1e-4);
449 assert_approx_eq!(&attitude[(2, 2)], 1.0, 1e-4);
450 }
451 fn test_wrap_to_180() {
452 assert_eq!(super::wrap_to_180(190.0), -170.0);
453 assert_eq!(super::wrap_to_180(-190.0), 170.0);
454 assert_eq!(super::wrap_to_180(0.0), 0.0);
455 assert_eq!(super::wrap_to_180(180.0), 180.0);
456 assert_eq!(super::wrap_to_180(-180.0), -180.0);
457 }
458 #[test]
459 fn test_wrap_to_360() {
460 assert_eq!(super::wrap_to_360(370.0), 10.0);fn test_wrap_to_180() {
461 assert_eq!(super::wrap_to_180(190.0), -170.0);
462 assert_eq!(super::wrap_to_180(-190.0), 170.0);
463 assert_eq!(super::wrap_to_180(0.0), 0.0);
464 assert_eq!(super::wrap_to_180(180.0), 180.0);
465 assert_eq!(super::wrap_to_180(-180.0), -180.0);
466 }
467 #[test]
468 fn test_wrap_to_360() {
469 assert_eq!(super::wrap_to_360(370.0), 10.0);
470 assert_eq!(super::wrap_to_360(-10.0), 350.0);
471 assert_eq!(super::wrap_to_360(0.0), 0.0);
472 }
473 #[test]
474 fn test_wrap_to_pi() {
475 assert_eq!(super::wrap_to_pi(3.0 * std::f64::consts::PI), std::f64::consts::PI);
476 assert_eq!(super::wrap_to_pi(-3.0 * std::f64::consts::PI), -std::f64::consts::PI);
477 assert_eq!(super::wrap_to_pi(0.0), 0.0);
478 assert_eq!(super::wrap_to_pi(std::f64::consts::PI), std::f64::consts::PI);
479 assert_eq!(super::wrap_to_pi(-std::f64::consts::PI), -std::f64::consts::PI);
480 }
481 #[test]
482 fn test_wrap_to_2pi() {
483 assert_eq!(super::wrap_to_2pi(7.0 * std::f64::consts::PI), std::f64::consts::PI);
484 assert_eq!(super::wrap_to_2pi(-5.0 * std::f64::consts::PI), std::f64::consts::PI);
485 assert_eq!(super::wrap_to_2pi(0.0), 0.0);
486 assert_eq!(super::wrap_to_2pi(std::f64::consts::PI), std::f64::consts::PI);
487 assert_eq!(super::wrap_to_2pi(-std::f64::consts::PI), std::f64::consts::PI);
488 }
489 assert_eq!(super::wrap_to_360(-10.0), 350.0);
490 assert_eq!(super::wrap_to_360(0.0), 0.0);
491 }
492 #[test]
493 fn test_wrap_to_pi() {
494 assert_eq!(super::wrap_to_pi(3.0 * std::f64::consts::PI), std::f64::consts::PI);
495 assert_eq!(super::wrap_to_pi(-3.0 * std::f64::consts::PI), -std::f64::consts::PI);
496 assert_eq!(super::wrap_to_pi(0.0), 0.0);
497 assert_eq!(super::wrap_to_pi(std::f64::consts::PI), std::f64::consts::PI);
498 assert_eq!(super::wrap_to_pi(-std::f64::consts::PI), -std::f64::consts::PI);
499 }
500 #[test]
501 fn test_wrap_to_2pi() {
502 assert_eq!(super::wrap_to_2pi(7.0 * std::f64::consts::PI), std::f64::consts::PI);
503 assert_eq!(super::wrap_to_2pi(-5.0 * std::f64::consts::PI), std::f64::consts::PI);
504 assert_eq!(super::wrap_to_2pi(0.0), 0.0);
505 assert_eq!(super::wrap_to_2pi(std::f64::consts::PI), std::f64::consts::PI);
506 assert_eq!(super::wrap_to_2pi(-std::f64::consts::PI), std::f64::consts::PI);
507 }
508}
509
510
511pub fn add(a: f64, b: f64) -> f64 {
513 a + b
514}