use embedded_dsp::*;
struct RadarTrackingModel;
impl EkfModel<4, 2> for RadarTrackingModel {
fn f(&self, x: &[f32; 4], dt: f32, out: &mut [f32; 4]) {
out[0] = x[0] + dt * x[1]; out[1] = x[1]; out[2] = x[2] + dt * x[3]; out[3] = x[3]; }
fn h(&self, x: &[f32; 4], out: &mut [f32; 2]) {
let px = x[0];
let py = x[2];
let range = (px * px + py * py).sqrt();
let bearing = py.atan2(px);
out[0] = range;
out[1] = bearing;
}
fn jacobian_f(&self, _x: &[f32; 4], dt: f32, out: &mut [[f32; 4]; 4]) {
*out = [
[1.0, dt, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, dt],
[0.0, 0.0, 0.0, 1.0],
];
}
fn jacobian_h(&self, x: &[f32; 4], out: &mut [[f32; 4]; 2]) {
let px = x[0];
let py = x[2];
let r2 = px * px + py * py;
let r = r2.max(1e-6).sqrt();
out[0] = [px / r, 0.0, py / r, 0.0];
let denom = r2.max(1e-6);
out[1] = [-py / denom, 0.0, px / denom, 0.0];
}
}
fn main() {
println!("===============================================================================");
println!(" embedded-dsp Sensor Fusion, Navigation & Attitude Estimation ");
println!("===============================================================================");
println!();
println!("--- 1. Sensor Conditioning & Glitch Removal (Conditional Median Filter) ---");
let raw_accel_z = [
9.81f32, 9.80, 9.82, 105.4, 9.81, 9.79, 9.83, 9.81, -45.0, 9.82, 9.80, 9.81,
];
let mut cleaned_accel_z = [0.0f32; 12];
median_filter_1d_f32(&raw_accel_z, &mut cleaned_accel_z, 3, 10.0);
println!(" Raw Accel Z (with spikes) : {:?}", raw_accel_z);
println!(" Cleaned Accel Z (spikes fixed): {:?}", cleaned_accel_z);
println!("\n--- 2. Sensor Factory Calibration (Polynomial Least-Squares Fit) ---");
let adc_counts = [100.0f32, 200.0, 300.0, 400.0, 500.0];
let ref_values = [57.5f32, 102.5, 147.5, 192.5, 237.5];
let mut calib_params = [0.0f32; 2];
let status = polynomial_least_squares_fit(&adc_counts, &ref_values, None, 1, &mut calib_params);
if status == Status::Success {
println!(
" Fitted Calibration Model: y = {:.4} + {:.4} * x",
calib_params[0], calib_params[1]
);
} else {
println!(" Least squares fitting error: {:?}", status);
}
println!("\n--- 3. 3D Attitude Estimation with Unit Quaternions ---");
let q_current = [1.0f32, 0.0, 0.0, 0.0];
let angle_y = core::f32::consts::FRAC_PI_2;
let q_pitch_90 = [(angle_y / 2.0).cos(), 0.0, (angle_y / 2.0).sin(), 0.0];
let mut q_rotated = [0.0f32; 4];
quaternion_product_f32(&q_pitch_90, &q_current, &mut q_rotated);
quaternion_normalize_f32(&mut q_rotated);
println!(
" Quaternion after 90° Pitch: [{:.4}, {:.4}, {:.4}, {:.4}]",
q_rotated[0], q_rotated[1], q_rotated[2], q_rotated[3]
);
let mut rot_matrix = [0.0f32; 9];
quaternion_to_rotmat_f32(&q_rotated, &mut rot_matrix);
println!(" Converted 3x3 Direction Cosine Matrix (DCM):");
println!(
" [{:>7.4}, {:>7.4}, {:>7.4}]",
rot_matrix[0], rot_matrix[1], rot_matrix[2]
);
println!(
" [{:>7.4}, {:>7.4}, {:>7.4}]",
rot_matrix[3], rot_matrix[4], rot_matrix[5]
);
println!(
" [{:>7.4}, {:>7.4}, {:>7.4}]",
rot_matrix[6], rot_matrix[7], rot_matrix[8]
);
let mut q_conj = [0.0f32; 4];
quaternion_conjugate_f32(&q_rotated, &mut q_conj);
let v_body = [0.0f32, 1.0, 0.0, 0.0]; let mut q_temp = [0.0f32; 4];
let mut v_nav_q = [0.0f32; 4];
quaternion_product_f32(&q_rotated, &v_body, &mut q_temp);
quaternion_product_f32(&q_temp, &q_conj, &mut v_nav_q);
println!(
" Body Vector [1, 0, 0] rotated to Navigation Frame: [{:.4}, {:.4}, {:.4}]",
v_nav_q[1], v_nav_q[2], v_nav_q[3]
);
println!("\n--- 4. 2D Kinematic Kalman Filter (Position + Velocity Fusion) ---");
const DT: f32 = 0.1; const STEPS: usize = 30;
let mut kf2d = KalmanFilter2D::new(0.0, 0.0, 0.1, 4.0); let mut estimated_pos = [0.0f32; STEPS];
let mut true_pos = [0.0f32; STEPS];
for k in 0..STEPS {
let t = k as f32 * DT;
let true_p = 2.0 * t;
true_pos[k] = true_p;
let prng =
((k as u64).wrapping_mul(1664525).wrapping_add(1013904223) % 1000) as f32 / 1000.0;
let noisy_gps = true_p + (prng - 0.5) * 3.0;
kf2d.predict(DT);
let est = kf2d.update(noisy_gps);
estimated_pos[k] = est[0];
if k % 10 == 0 || k == STEPS - 1 {
println!(
" Step {:>2} (t={:.1}s): True Pos={:>5.2}m, Noisy GPS={:>5.2}m, KF Pos={:>5.2}m, KF Vel={:>5.2}m/s",
k, t, true_p, noisy_gps, est[0], est[1]
);
}
}
println!("\n--- 5. Const-Generic Linear Kalman Filter (4x2 Tracking) ---");
let f_matrix = [
[1.0, DT, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, DT],
[0.0, 0.0, 0.0, 1.0],
];
let h_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]];
let mut p_cov = [[0.0f32; 4]; 4];
let mut q_cov = [[0.0f32; 4]; 4];
for i in 0..4 {
p_cov[i][i] = 1.0;
q_cov[i][i] = 0.05;
}
let r_cov = [[1.5, 0.0], [0.0, 1.5]];
let mut kf_4x2 = KalmanFilter::<4, 2>::new(
[0.0, 1.5, 0.0, -1.0], p_cov,
q_cov,
r_cov,
);
kf_4x2.predict(&f_matrix);
let meas_z = [0.18, -0.09];
let kf_status = kf_4x2.update(&h_matrix, &meas_z);
println!(
" Const-generic KalmanFilter<4, 2> update status: {:?}",
kf_status
);
println!(
" Updated State Vector: px={:.3}m, vx={:.3}m/s, py={:.3}m, vy={:.3}m/s",
kf_4x2.x[0], kf_4x2.x[1], kf_4x2.x[2], kf_4x2.x[3]
);
println!("\n--- 6. Non-Linear Extended Kalman Filter (Radar Tracking) ---");
let ekf_model = RadarTrackingModel;
let mut ekf = ExtendedKalmanFilter::<4, 2, RadarTrackingModel>::from_variances(
[100.0, 10.0, 50.0, 5.0], 10.0, 0.2, 1.0, ekf_model,
);
println!(" Target True Trajectory vs EKF Non-Linear Estimate:");
for step in 1..=5 {
let dt = 0.5;
let true_px = 100.0 + step as f32 * dt * 10.0;
let true_py = 50.0 + step as f32 * dt * 5.0;
let true_range = (true_px * true_px + true_py * true_py).sqrt();
let true_bearing = true_py.atan2(true_px);
ekf.predict(dt);
let z = [true_range + 0.5, true_bearing - 0.005]; let status = ekf.update(&z);
println!(
" Step {}: Meas [Range={:>6.1}m, Azimuth={:>6.3} rad] -> EKF Est [X={:>6.1}m, Y={:>6.1}m] (Status: {:?})",
step, z[0], z[1], ekf.x[0], ekf.x[2], status
);
}
println!("\n--- 7. Tracking Error Statistical Metrics ---");
let mut tracking_errors = [0.0f32; STEPS];
for i in 0..STEPS {
tracking_errors[i] = estimated_pos[i] - true_pos[i];
}
let mut mean_err = 0.0f32;
let mut std_err = 0.0f32;
let mut rms_err = 0.0f32;
let mut max_err = 0.0f32;
let mut max_idx = 0usize;
mean_f32(&tracking_errors, &mut mean_err);
std_f32(&tracking_errors, &mut std_err);
rms_f32(&tracking_errors, &mut rms_err);
max_f32(&tracking_errors, &mut max_err, &mut max_idx);
println!(" Position Tracking Error Metrics (over {} steps):", STEPS);
println!(" • Mean Error : {:>7.4} m", mean_err);
println!(" • Std Deviation : {:>7.4} m", std_err);
println!(" • RMS Error : {:>7.4} m", rms_err);
println!(
" • Max Error : {:>7.4} m (at step {})",
max_err, max_idx
);
println!();
println!("===============================================================================");
println!(" Sensor Fusion & Navigation Execution Complete! ");
println!("===============================================================================");
}