#![no_std]
mod acc;
pub mod attitude;
mod linear_acc;
mod mag;
mod mag_decl_table;
mod mag_ellipsoid_fitting;
pub mod params;
pub mod ppks;
mod util;
use core::sync::atomic::{AtomicU16, Ordering};
use chrono::NaiveDateTime;
use lin_alg::f32::{Quaternion, Vec3};
use num_enum::TryFromPrimitive;
use num_traits::Float;
#[cfg(feature = "defmt")]
use defmt::{Format, println};
pub use crate::{attitude::Ahrs, params::Params};
pub const G: f32 = 9.80665;
pub const DEG_SCALE_1E8: f32 = 100_000_000.;
pub const UP: Vec3 = Vec3 {
x: 0.,
y: 0.,
z: 1.,
};
pub const FORWARD: Vec3 = Vec3 {
x: 0.,
y: 1.,
z: 0.,
};
pub const RIGHT: Vec3 = Vec3 {
x: 1.,
y: 0.,
z: 0.,
};
#[derive(Clone, Copy)]
pub enum DeviceOrientation {
YFwdXRight,
YLeftXFwd,
}
impl Default for DeviceOrientation {
fn default() -> Self {
Self::YFwdXRight
}
}
#[derive(Clone, Copy, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
pub enum FixType {
NoFix = 0,
DeadReckoning = 1,
Fix2d = 2,
Fix3d = 3,
Combined = 4,
TimeOnly = 5,
}
impl Default for FixType {
fn default() -> Self {
Self::NoFix
}
}
#[derive(Clone, Default)]
pub struct Fix {
pub timestamp_s: f32,
pub datetime: NaiveDateTime,
pub type_: FixType,
pub lat_e7: i32,
pub lon_e7: i32,
pub elevation_hae: i32,
pub elevation_msl: i32,
pub ground_speed: i32,
pub ned_velocity: [i32; 3],
pub heading: Option<f32>, pub sats_used: u8,
pub pdop: u16,
}
#[derive(Default, Clone)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct ImuReadings {
pub a_x: f32,
pub a_y: f32,
pub a_z: f32,
pub v_pitch: f32,
pub v_roll: f32,
pub v_yaw: f32,
}
impl ImuReadings {
pub fn from_buffer(buf: &[u8], accel_fullscale: f32, gyro_fullscale: f32) -> Self {
Self {
a_x: interpret_accel_or_gyro(i16::from_be_bytes([buf[1], buf[2]]), accel_fullscale),
a_y: interpret_accel_or_gyro(i16::from_be_bytes([buf[3], buf[4]]), accel_fullscale),
a_z: interpret_accel_or_gyro(i16::from_be_bytes([buf[5], buf[6]]), accel_fullscale),
v_pitch: interpret_accel_or_gyro(i16::from_be_bytes([buf[7], buf[8]]), gyro_fullscale),
v_roll: interpret_accel_or_gyro(i16::from_be_bytes([buf[9], buf[10]]), gyro_fullscale),
v_yaw: interpret_accel_or_gyro(i16::from_be_bytes([buf[11], buf[12]]), gyro_fullscale),
}
}
}
pub fn interpret_accel_or_gyro(val: i16, fullscale: f32) -> f32 {
(val as f32 / i16::MAX as f32) * fullscale
}
pub fn print_quat(quat: Quaternion, name: &str) {
let (x_component, y_component, z_component) = quat.to_axes();
#[cfg(feature = "defmt")]
println!(
"{} -- x{} y{} z{}",
name, x_component, y_component, z_component
);
}
pub(crate) fn blend(val0: f32, val1: f32, amount: f32) -> f32 {
let amount_inv = 1. - amount;
val0 * amount_inv + val1 * amount
}
pub enum CalResult {
Incomplete,
Success((f32, f32, f32)),
Fail,
Disabled,
}
static ACC_CAL_VALS_LOGGED: AtomicU16 = AtomicU16::new(0);
static mut ACC_CAL_TOTAL: Vec3 = Vec3::new_zero();
pub fn cal_accel(
calibrating: &mut bool,
num_vals: u16,
vals_to_skip: u16,
imu_data: &ImuReadings,
) -> CalResult {
if !*calibrating {
return CalResult::Disabled;
}
let vals = ACC_CAL_VALS_LOGGED.fetch_add(1, Ordering::Relaxed);
if vals > (num_vals + vals_to_skip) {
*calibrating = false;
ACC_CAL_VALS_LOGGED.store(0, Ordering::Release);
let x = unsafe { ACC_CAL_TOTAL.x } / num_vals as f32;
let y = unsafe { ACC_CAL_TOTAL.y } / num_vals as f32;
let z = unsafe { ACC_CAL_TOTAL.z } / num_vals as f32 - G;
const THRESH_A: f32 = 0.45; if x.abs() < THRESH_A && y.abs() < THRESH_A && z.abs() < THRESH_A {
return CalResult::Success((x, y, z));
} else {
#[cfg(feature = "defmt")]
println!(
"Acc cal failed due to out of bounds value. X: {} Y: {} Z: {} Thresh: {}",
x, y, z, THRESH_A
);
return CalResult::Fail;
}
} else if vals > vals_to_skip {
let acc_data = Vec3::new(imu_data.a_x, imu_data.a_y, imu_data.a_z);
const THRESH: f32 = 0.6; if acc_data.x.abs() < THRESH && acc_data.y.abs() < THRESH && (acc_data.z - G).abs() < THRESH
{
unsafe {
ACC_CAL_TOTAL += acc_data;
}
} else {
ACC_CAL_VALS_LOGGED.store(vals - 1, Ordering::Release);
}
}
CalResult::Incomplete
}