#![no_std]
#![deny(missing_docs)]
#[macro_use]
extern crate alloc;
pub mod kalman;
pub mod filters;
use crate::kalman::Kalman;
use crate::filters::{Smooth, HPFilter, LPFilter};
#[allow(unused_imports)]
use micromath::F32Ext;
pub const G: f32 = 9.80665;
pub const G2: f32 = G * G;
pub const PI: f32 = core::f32::consts::PI;
pub const RAD2DEG: f32 = 180.0 / PI;
pub const DEG2RAD: f32 = PI / 180.0;
pub const SQRT_G: f32 = 3.13155712;
pub const SQRT_G_KALMAN: f32 = SQRT_G * 3.0 / 4.0;
pub const G_TO_SCALE: f32 = 100.0;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Mode {
Dof6,
Dof9
}
pub struct Fusion {
acc_x: f32,
acc_y: f32,
acc_z: f32,
gyro_x: f32,
gyro_y: f32,
gyro_z: f32,
mag_rx: f32,
mag_ry: f32,
mag_rz: f32,
mag_x: f32,
mag_y: f32,
roll: f32,
pitch: f32,
yaw: f32,
heading: f32,
mode: Mode,
kalman_x_angle: f32,
kalman_y_angle: f32,
kalman_z_angle: f32,
kalman_x: Kalman,
kalman_y: Kalman,
kalman_z: Kalman,
ma_yaw: Smooth,
offset_kalman: f32,
sens: i8,
sens_prec: i8,
sens_count: i32,
total_z_angle: f32,
angular_vel_z: f32,
declination: f32,
enable_distance: bool,
hp_vx: HPFilter,
hp_e_vx: f32,
old_hp_e_vx: f32,
hp_s_vx: f32,
speed_x: f32,
distance_x: f32,
lp_vx: LPFilter,
lp_e_vx: f32,
lp_s_vx: f32,
hp_dx: HPFilter,
hp_e_dx: f32,
old_hp_e_dx: f32,
hp_s_dx: f32,
lp_dx: LPFilter,
lp_e_dx: f32,
lp_s_dx: f32,
ma_x: Smooth,
average_x: f32,
offset_x: f32,
hp_vy: HPFilter,
hp_e_vy : f32,
old_hp_e_vy: f32,
hp_s_vy: f32,
speed_y: f32,
distance_y: f32,
lp_vy: LPFilter,
lp_e_vy: f32,
lp_s_vy: f32,
hp_dy: HPFilter,
hp_e_dy: f32,
old_hp_e_dy: f32,
hp_s_dy: f32,
lp_dy: LPFilter,
lp_e_dy: f32,
lp_s_dy: f32,
ma_y: Smooth,
average_y: f32,
offset_y: f32,
counter: u32,
ready_2_go: bool,
final_distance: f32,
scale_factor: f32,
buffer_zone_x: f32,
buffer_zone_y: f32,
correction_factor: f32,
buffer_zone_kalman: f32,
counter_offset: u32,
}
impl Fusion {
pub fn new(hp_fc: f32, lp_fc: f32, num_readings: usize) -> Self {
Fusion {
acc_x: 0.,
acc_y: 0.,
acc_z: 0.,
gyro_x: 0.,
gyro_y: 0.,
gyro_z: 0.,
mag_rx: 0.,
mag_ry: 0.,
mag_rz: 0.,
mag_x: 0.,
mag_y: 0.,
roll: 0.,
pitch: 0.,
yaw: 0.,
heading: 0.,
mode: Mode::Dof6,
kalman_x_angle: 0.,
kalman_y_angle: 0.,
kalman_z_angle: 0.,
kalman_x: Kalman::new(),
kalman_y: Kalman::new(),
kalman_z: Kalman::new(),
ma_yaw: Smooth::new(num_readings),
offset_kalman: 0.,
sens: 0,
sens_prec: 0,
sens_count: 0,
total_z_angle: 0.,
angular_vel_z: 0.,
declination: 0.,
enable_distance: true,
hp_vx: HPFilter::new(hp_fc),
hp_e_vx: 0.,
old_hp_e_vx: 0.,
hp_s_vx: 0.,
speed_x: 0.,
distance_x: 0.,
lp_vx: LPFilter::new(lp_fc),
lp_e_vx: 0.,
lp_s_vx: 0.,
hp_dx: HPFilter::new(hp_fc),
hp_e_dx: 0.,
old_hp_e_dx: 0.,
hp_s_dx: 0.,
lp_dx: LPFilter::new(lp_fc),
lp_e_dx: 0.,
lp_s_dx: 0.,
ma_x: Smooth::new(num_readings),
average_x: 0.,
offset_x: 0.,
hp_vy: HPFilter::new(hp_fc),
hp_e_vy : 0.,
old_hp_e_vy: 0.,
hp_s_vy: 0.,
speed_y: 0.,
distance_y: 0.,
lp_vy: LPFilter::new(lp_fc),
lp_e_vy: 0.,
lp_s_vy: 0.,
hp_dy: HPFilter::new(hp_fc),
hp_e_dy: 0.,
old_hp_e_dy: 0.,
hp_s_dy: 0.,
lp_dy: LPFilter::new(lp_fc),
lp_e_dy: 0.,
lp_s_dy: 0.,
ma_y: Smooth::new(num_readings),
average_y: 0.,
offset_y: 0.,
counter: 0,
ready_2_go: false,
final_distance: 0.,
scale_factor: 100.,
buffer_zone_x: 13.,
buffer_zone_y: 10.,
correction_factor: G2 * SQRT_G,
buffer_zone_kalman: 0.05,
counter_offset: 500,
}
}
pub fn init(&mut self) {
self.roll = self.compute_roll();
self.pitch = self.compute_pitch();
self.yaw = self.compute_yaw();
self.kalman_x.set_angle(self.roll);
self.kalman_y.set_angle(self.pitch);
self.kalman_z.set_angle(self.yaw);
self.offset_kalman = self.yaw;
}
pub fn step(&mut self, dt: f32) {
self.roll = self.compute_roll();
self.pitch = self.compute_pitch();
self.yaw = self.compute_yaw();
let mut gyro_x_rate = self.gyro_x * RAD2DEG;
let gyro_y_rate = self.gyro_y * RAD2DEG;
let gyro_z_rate = self.gyro_z * RAD2DEG;
if ((self.pitch < -90.0) && (self.kalman_y_angle > 90.0)) || ((self.pitch > 90.0) && (self.kalman_y_angle < -90.0)) {
self.kalman_y.set_angle(self.pitch);
self.kalman_y_angle = self.pitch;
} else {
self.kalman_y.compute_angle(self.pitch, gyro_y_rate, dt);
self.kalman_y_angle = self.kalman_y.get_angle();
}
if self.kalman_y_angle.abs() > 90.0 {
gyro_x_rate = -gyro_x_rate;
}
self.kalman_x.compute_angle(self.roll, gyro_x_rate, dt);
self.kalman_x_angle = self.kalman_x.get_angle();
if self.mode == Mode::Dof6 {
self.kalman_z.compute_angle(self.yaw, gyro_z_rate, dt);
self.kalman_z_angle = self.kalman_z.get_angle();
let angle_z = self.kalman_z_angle - self.offset_kalman;
if angle_z < -self.buffer_zone_kalman {
self.sens = -1;
} else if angle_z > self.buffer_zone_kalman {
self.sens = 1;
} else {
self.sens = 0;
}
if (self.sens != self.sens_prec) && (self.sens != 0) {
self.sens_count += 1;
self.sens_prec = self.sens;
}
if self.sens_count > 3 {
self.sens_count = 0;
self.sens_prec = 0;
} else {
self.total_z_angle += self.compute_integral(angle_z, dt) * SQRT_G_KALMAN;
}
} else {
self.kalman_z.compute_angle(self.yaw, gyro_z_rate, dt);
self.kalman_z_angle = self.kalman_z.get_angle();
self.ma_yaw.add_reading(self.kalman_z_angle);
self.heading = self.ma_yaw.get_average();
self.angular_vel_z = self.heading * dt;
}
if self.enable_distance {
if self.counter == self.counter_offset {
self.offset_x = self.average_x;
self.offset_y = self.average_y;
self.ready_2_go = true;
self.distance_x = 0.;
self.distance_y = 0.;
}
if self.counter < self.counter_offset + 1 {
self.counter += 1;
}
let accel_x = self.acc_x * G_TO_SCALE;
let accel_y = self.acc_y * G_TO_SCALE;
self.hp_e_vx = accel_x;
self.hp_s_vx = self.hp_vx.compute(self.hp_e_vx, self.old_hp_e_vx, self.hp_s_vx, dt);
self.lp_e_vx = self.hp_s_vx;
self.lp_s_vx = self.lp_vx.compute(self.lp_e_vx, self.lp_s_vx, dt);
self.speed_x = self.compute_integral(self.lp_s_vx, dt);
self.old_hp_e_dx = self.hp_e_dx;
self.hp_e_dx = self.speed_x;
self.hp_s_dx = self.hp_dx.compute(self.hp_e_dx, self.old_hp_e_dx, self.hp_s_dx, dt);
self.lp_e_dx = self.hp_s_dx;
self.lp_s_dx = self.lp_dx.compute(self.lp_e_dx, self.lp_s_dx, dt);
self.hp_e_vy = accel_y;
self.hp_s_vy = self.hp_vy.compute(self.hp_e_vy, self.old_hp_e_vy, self.hp_s_vy, dt);
self.lp_e_vy = self.hp_s_vy;
self.lp_s_vy = self.lp_vy.compute(self.lp_e_vy, self.lp_s_vy, dt);
self.speed_y = self.compute_integral(self.lp_s_vy, dt);
self.old_hp_e_dy = self.hp_e_dy;
self.hp_e_dy = self.speed_y;
self.hp_s_dy = self.hp_dy.compute(self.hp_e_dy, self.old_hp_e_dy, self.hp_s_dy, dt);
self.lp_e_dy = self.hp_s_dy;
self.lp_s_dy = self.lp_dy.compute(self.lp_e_dy, self.lp_s_dy, dt);
self.ma_x.add_reading(self.lp_s_dx);
self.average_x = self.ma_x.get_average() * self.scale_factor - self.offset_x;
self.ma_y.add_reading(self.lp_s_dy);
self.average_y = self.ma_y.get_average() * self.scale_factor - self.offset_y;
let factor = self.correction_factor / self.scale_factor;
if (self.average_x.abs() > self.buffer_zone_x) && self.ready_2_go {
self.distance_x += self.compute_integral(self.average_x, dt) * factor;
}
if (self.average_y.abs() > self.buffer_zone_y) && self.ready_2_go {
self.distance_y += self.compute_integral(self.average_y, dt) * factor;
}
let dax = self.kalman_x_angle.sin() * G;
let day = self.kalman_y_angle.sin() * G;
let dpx = 0.5 * dax * dt * dt;
let dpy = 0.5 * day * dt * dt;
self.distance_x = (self.distance_x - dpx * self.distance_x).abs();
self.distance_y = (self.distance_y - dpy * self.distance_y).abs();
self.final_distance = self.distance(self.distance_x, self.distance_y);
}
}
pub fn set_accel_data(&mut self, acc_x: f32, acc_y: f32, acc_z: f32) {
self.acc_x = acc_x;
self.acc_y = acc_y;
self.acc_z = acc_z;
}
pub fn set_gyro_data(&mut self, gyro_x: f32, gyro_y: f32, gyro_z: f32) {
self.gyro_x = gyro_x;
self.gyro_y = gyro_y;
self.gyro_z = gyro_z;
}
pub fn set_mag_data(&mut self, mag_x: f32, mag_y: f32, mag_z: f32) {
self.mag_rx = mag_x;
self.mag_ry = mag_y;
self.mag_rz = mag_z;
}
pub fn set_data_dof9(&mut self, acc_x: f32, acc_y: f32, acc_z: f32, gyro_x: f32, gyro_y: f32, gyro_z: f32, mag_rx: f32, mag_ry: f32, mag_rz: f32) {
self.acc_x = acc_x;
self.acc_y = acc_y;
self.acc_z = acc_z;
self.gyro_x = gyro_x;
self.gyro_y = gyro_y;
self.gyro_z = gyro_z;
self.mag_rx = mag_rx;
self.mag_ry = mag_ry;
self.mag_rz = mag_rz;
}
pub fn set_data_dof6(&mut self, acc_x: f32, acc_y: f32, acc_z: f32, gyro_x: f32, gyro_y: f32, gyro_z: f32) {
self.acc_x = acc_x;
self.acc_y = acc_y;
self.acc_z = acc_z;
self.gyro_x = gyro_x;
self.gyro_y = gyro_y;
self.gyro_z = gyro_z;
}
pub fn set_old_values(&mut self, acc_x: f32, acc_y: f32) {
self.old_hp_e_vx = acc_x * G_TO_SCALE;
self.old_hp_e_vy = acc_y * G_TO_SCALE;
}
pub fn get_mag_x(&self) -> f32 {
self.mag_x
}
pub fn get_mag_y(&self) -> f32 {
self.mag_y
}
fn compute_integral(&self, value: f32, dt: f32) -> f32 {
value * dt
}
fn distance(&self, x: f32, y: f32) -> f32 {
(x * x + y * y).sqrt()
}
pub fn compute_roll(&self) -> f32 {
self.acc_y.atan2(self.acc_z) * RAD2DEG
}
pub fn compute_pitch(&self) -> f32 {
(-self.acc_x / (self.acc_y * self.acc_y + self.acc_z * self.acc_z).sqrt()).atan() * RAD2DEG
}
pub fn compute_yaw(&mut self) -> f32 {
if self.mode == Mode::Dof6 {
(self.acc_z / (self.acc_x * self.acc_x + self.acc_z * self.acc_z).sqrt()).atan() * RAD2DEG
} else {
let roll_rad = self.roll * DEG2RAD;
let pitch_rad = self.pitch * DEG2RAD;
self.mag_x = self.mag_rx * pitch_rad.cos() + self.mag_ry * roll_rad.sin() * pitch_rad.sin() - self.mag_rz * self.roll.cos() * pitch_rad.sin();
self.mag_y = -self.mag_ry * roll_rad.cos() + self.mag_rz * roll_rad.sin();
(-self.mag_y.atan2(self.mag_x)) * RAD2DEG
}
}
pub fn get_heading(&self) -> f32 {
if self.mode == Mode::Dof9 {
self.heading + self.declination
} else {
self.yaw
}
}
pub fn reset_distance(&mut self) {
self.distance_x = 0.;
self.distance_y = 0.;
self.final_distance = 0.;
}
pub fn reset_angle_z(&mut self) {
if self.mode == Mode::Dof6 {
self.total_z_angle = 0.;
}
}
pub fn reset_offset_distance(&mut self) {
self.offset_x = self.average_x;
self.offset_y = self.average_y;
}
pub fn reset_offset_angle(&mut self) {
if self.mode == Mode::Dof6 {
self.offset_kalman = self.yaw;
}
}
pub fn get_x_angle(&self) -> f32 {
self.kalman_x_angle
}
pub fn get_y_angle(&self) -> f32 {
self.kalman_y_angle
}
pub fn get_z_angle(&self) -> f32 {
if self.mode == Mode::Dof6 {
self.total_z_angle
} else {
self.kalman_z_angle
}
}
pub fn get_z_angular_velocity(&self) -> f32 {
if self.mode == Mode::Dof6 {
self.kalman_z_angle - self.offset_kalman
} else {
self.angular_vel_z
}
}
pub fn get_x_speed(&self) -> f32 {
self.speed_x
}
pub fn get_y_speed(&self) -> f32 {
self.speed_y
}
pub fn get_x_distance(&self) -> f32 {
self.distance_x
}
pub fn get_y_distance(&self) -> f32 {
self.distance_y
}
pub fn get_final_distance(&self) -> f32 {
self.final_distance
}
pub fn get_mode(&self) -> Mode {
self.mode
}
pub fn set_mode(&mut self, mode: Mode) {
self.mode = mode;
}
pub fn set_declination(&mut self, declination: f32) {
self.declination = declination;
}
pub fn get_declination(&self) -> f32 {
self.declination
}
pub fn set_buffer_zone_x(&mut self, buffer_zone: f32) {
self.buffer_zone_x = buffer_zone * self.scale_factor;
}
pub fn set_buffer_zone_y(&mut self, buffer_zone: f32) {
self.buffer_zone_y = buffer_zone * self.scale_factor;
}
pub fn set_scale_factor(&mut self, scale_factor: f32) {
self.scale_factor = scale_factor;
}
pub fn set_correction_factor(&mut self, correction_factor: f32) {
self.correction_factor = correction_factor;
}
pub fn set_buffer_zone_kalman(&mut self, buffer_zone: f32) {
self.buffer_zone_kalman = buffer_zone;
}
pub fn set_counter_offset(&mut self, counter_offset: u32) {
self.counter_offset = counter_offset;
}
pub fn disable_distance(&mut self, disable: bool) {
self.enable_distance = disable;
}
}