pub fn field_likelihood(predicted: f64, measured: f64, sigma: f64) -> f64 {
let z = (predicted - measured) / sigma;
(-0.5 * z * z).exp()
}
pub fn map_match_likelihood<S>(field: S, lat: f64, lon: f64, measured: f64, sigma: f64) -> f64
where
S: Fn(f64, f64) -> f64,
{
field_likelihood(field(lat, lon), measured, sigma)
}
pub(crate) fn offset_grid(center: [f64; 2], n_side: i64, step: f64) -> Vec<Vec<f64>> {
let count = ((2 * n_side + 1) * (2 * n_side + 1)).max(1) as usize;
let mut g = Vec::with_capacity(count);
for i in -n_side..=n_side {
for j in -n_side..=n_side {
g.push(vec![
center[0] + i as f64 * step,
center[1] + j as f64 * step,
]);
}
}
g
}
pub(crate) fn hierarchical_offset_search<W>(
weigh: W,
half: f64,
step: f64,
stages: usize,
factor: f64,
) -> [f64; 2]
where
W: Fn(&[f64]) -> f64,
{
use crate::particle_filter::ParticleFilter;
let n_side = (half / step).round().max(1.0) as i64;
let stages = stages.max(1);
let factor = factor.max(1.000_1);
let mut center = [0.0_f64, 0.0_f64];
let mut step = step;
let mut est = center;
for _ in 0..stages {
let grid = offset_grid(center, n_side, step);
let mut pf = ParticleFilter::new(grid);
pf.update(&weigh);
let e = pf.estimate();
est = [e[0], e[1]];
center = est;
step /= factor;
}
est
}
#[cfg(test)]
mod tests {
use super::*;
use crate::particle_filter::ParticleFilter;
#[test]
fn likelihood_peaks_at_a_perfect_match() {
assert!((field_likelihood(100.0, 100.0, 5.0) - 1.0).abs() < 1e-12);
assert!((field_likelihood(105.0, 100.0, 5.0) - (-0.5_f64).exp()).abs() < 1e-12);
assert!(field_likelihood(140.0, 100.0, 5.0) < 1e-6);
}
#[test]
fn terrain_match_recovers_position_with_a_particle_filter() {
let terrain = |lat: f64, lon: f64| {
1000.0 * (-((lat - 2.0).powi(2) + (lon - 3.0).powi(2)) / 0.5).exp()
};
let truth = (2.0, 3.0);
let measured = terrain(truth.0, truth.1);
let mut particles = Vec::new();
for i in 0..41 {
for j in 0..41 {
particles.push(vec![0.0 + 0.1 * i as f64, 1.0 + 0.1 * j as f64]);
}
}
let mut pf = ParticleFilter::new(particles);
pf.update(|p| map_match_likelihood(terrain, p[0], p[1], measured, 50.0));
let est = pf.estimate();
assert!((est[0] - truth.0).abs() < 0.1, "lat = {}", est[0]);
assert!((est[1] - truth.1).abs() < 0.1, "lon = {}", est[1]);
}
}