use lin_alg::f32::{Quaternion, Vec3};
use num_traits::Float;
use crate::{Fix, DEG_SCALE_1E8};
pub const FIX_FUSED_SIZE: usize = 8 * 3 + 4 * 5;
#[cfg(feature = "defmt")]
use defmt::println;
pub fn ned_vel_to_xyz(ned_vel: [i32; 3]) -> Vec3 {
Vec3::new(
ned_vel[1] as f32 / 1_000.,
ned_vel[0] as f32 / 1_000.,
-ned_vel[2] as f32 / 1_000.,
)
}
#[derive(Default, Clone)]
pub struct PositFused {
pub timestamp_us: u64, pub lat_e8: i64,
pub lon_e8: i64,
pub elevation_hae: f32, pub elevation_msl: f32, pub ned_velocity: [f32; 3], }
impl PositFused {
pub fn new(
fix: &Fix,
inertial: &mut PositInertial,
timestamp_us: u64,
) -> Self {
let gnss_dr = PositVelEarthUnits::from_fix_dr(fix, timestamp_us);
let inertial_absolute = inertial.combine_with_anchor();
let update_amount = 0.5;
let update_amount = 1.;
let add_to_gnss = PositVelEarthUnits {
lat_e8: (inertial_absolute.lat_e8 - gnss_dr.lat_e8) / 2,
lon_e8: (inertial_absolute.lon_e8 - gnss_dr.lon_e8) / 2,
elevation_msl: (inertial_absolute.elevation_msl - gnss_dr.elevation_msl)
* update_amount,
velocity: (inertial_absolute.velocity - gnss_dr.velocity) * update_amount,
};
let add_to_gnss = PositVelEarthUnits {
lat_e8: 0,
lon_e8: 0,
elevation_msl: 0.,
velocity: Vec3::new_zero(),
};
let vel = gnss_dr.velocity + add_to_gnss.velocity;
let elevation_msl = gnss_dr.elevation_msl + add_to_gnss.elevation_msl;
let elevation_hae = (fix.elevation_hae as f32 / 1_000.)
+ (elevation_msl - (fix.elevation_msl as f32 / 1_000.));
Self {
timestamp_us,
lon_e8: gnss_dr.lon_e8 + add_to_gnss.lon_e8,
lat_e8: gnss_dr.lat_e8 + add_to_gnss.lat_e8,
elevation_msl,
elevation_hae,
ned_velocity: [vel.x, vel.y, vel.z],
}
}
pub fn to_bytes(&self) -> [u8; FIX_FUSED_SIZE] {
let mut result = [0; FIX_FUSED_SIZE];
result[0..8].copy_from_slice(&self.timestamp_us.to_le_bytes());
result[8..16].copy_from_slice(&self.lat_e8.to_le_bytes());
result[16..24].copy_from_slice(&self.lon_e8.to_le_bytes());
result[24..28].copy_from_slice(&self.elevation_hae.to_le_bytes());
result[28..32].copy_from_slice(&self.elevation_msl.to_le_bytes());
result[32..36].copy_from_slice(&self.ned_velocity[0].to_le_bytes());
result[36..40].copy_from_slice(&self.ned_velocity[1].to_le_bytes());
result[40..44].copy_from_slice(&self.ned_velocity[2].to_le_bytes());
result
}
}
#[derive(Default)]
pub struct PositInertial {
pub anchor: PositVelEarthUnits,
pub posit: Vec3,
pub velocity: Vec3,
}
impl PositInertial {
pub fn update(
&mut self,
acc_lin: Vec3,
attitude: Quaternion,
dt: f32, ) {
self.posit += self.velocity * dt;
self.velocity += attitude.inverse().rotate_vec(acc_lin) * dt;
static mut I: u32 = 0;
unsafe { I += 1 };
let combined = self.combine_with_anchor();
#[cfg(feature = "defmt")]
if false {
println!(
"\n\nInertial: x{} y{} z{} -- vx{} vy{} vz{}",
self.posit.x,
self.posit.y,
self.posit.z,
self.velocity.x,
self.velocity.y,
self.velocity.z
);
println!(
"Combined: lat{} lon{} msl{} -- vx{} vy{} vz{}",
combined.lat_e8,
combined.lon_e8,
combined.elevation_msl,
combined.velocity.x,
combined.velocity.y,
combined.velocity.z
);
}
}
pub fn update_anchor(&mut self, fix: &Fix) {
self.anchor = PositVelEarthUnits {
lat_e8: fix.lat_e7 as i64 * 10,
lon_e8: fix.lon_e7 as i64 * 10,
elevation_msl: fix.elevation_msl as f32 / 1_000.,
velocity: ned_vel_to_xyz(fix.ned_velocity),
};
self.posit = Vec3::new_zero();
self.velocity = Vec3::new_zero();
}
pub fn combine_with_anchor(&self) -> PositVelEarthUnits {
let (lat, lon) = augment_latlon(
self.anchor.lat_e8,
self.anchor.lon_e8,
self.posit.y,
self.posit.x,
);
PositVelEarthUnits {
lat_e8: lat,
lon_e8: lon,
elevation_msl: self.anchor.elevation_msl + self.posit.z,
velocity: self.anchor.velocity + self.velocity,
}
}
}
#[derive(Clone, Default)]
pub struct PositVelEarthUnits {
pub lat_e8: i64,
pub lon_e8: i64,
pub elevation_msl: f32,
pub velocity: Vec3,
}
impl PositVelEarthUnits {
pub fn from_fix(fix: &Fix) -> Self {
Self {
lat_e8: (fix.lat_e7 as i64) * 10,
lon_e8: (fix.lon_e7 as i64) * 10,
elevation_msl: fix.elevation_msl as f32 / 1_000.,
velocity: ned_vel_to_xyz(fix.ned_velocity),
}
}
pub fn from_fix_dr(fix: &Fix, timestamp_us: u64) -> Self {
let dt = (timestamp_us as f32 / 1_000_000.) - fix.timestamp_s;
let velocity = ned_vel_to_xyz(fix.ned_velocity);
let (lat_e8, lon_e8) = augment_latlon(
(fix.lat_e7 as i64) * 10,
(fix.lon_e7 as i64) * 10,
velocity.y * dt,
velocity.x * dt,
);
Self {
lat_e8,
lon_e8,
elevation_msl: (fix.elevation_msl as f32 / 1_000.) + velocity.z * dt,
velocity,
}
}
}
fn augment_latlon(lat_e8: i64, lon_e8: i64, add_lat_m: f32, add_lon_m: f32) -> (i64, i64) {
const M_PER_DEG_LAT_EQUATOR: f32 = 1_843. * 60.;
const M_PER_DEG_LAT_POLE: f32 = 1_861. * 60.;
let port_through_lat = (lat_e8 as f32).abs() / DEG_SCALE_1E8 / 90.;
let m_per_deg_lat =
M_PER_DEG_LAT_EQUATOR + port_through_lat * (M_PER_DEG_LAT_POLE - M_PER_DEG_LAT_EQUATOR);
let result_lat = lat_e8 + (add_lat_m / m_per_deg_lat * DEG_SCALE_1E8) as i64;
let m_per_deg_lon = (result_lat as f32 / DEG_SCALE_1E8).cos() * M_PER_DEG_LAT_EQUATOR;
let result_lon = lon_e8 + (add_lon_m / m_per_deg_lon * DEG_SCALE_1E8) as i64;
(result_lat, result_lon)
}