Files
ruvnet--RuView/v2/crates/wifi-densepose-sar/src/pointcloud.rs
T
ruv d781f20e1a feat(wifi-densepose-sar): coherent wideband RF tomography research crate (ADR-283)
New standalone leaf crate implementing the synthetic-aperture-radar
reconstruction primitive a handheld through-wall RF imaging device
would need: a stepped-frequency multi-position complex forward
measurement simulator, delay-and-sum backprojection reconstruction,
point-cloud extraction, and closed-form range/cross-range resolution
+ antenna-pose coherence-budget formulas checked against the
reconstruction's actual behavior in tests/physics_validation.rs.

Motivated by comparing this repo against Applied Electrodynamics'
"WaveSight" launch. Scoped explicitly below ADR-278's RISE/DiffRadar/
GeRaF reproduction gates: this is the bare measurement-model +
backprojection primitive, not a reproduction of any published system
or a claim about real hardware capability. Every number is
SYNTHETIC/L0 (ADR-282) -- no wideband RF hardware backs this crate.

24 tests (21 unit + 3 integration), 0 failed, clippy-clean. Adds a
tutorial walkthrough and MEASURED backprojection benchmark numbers.
2026-07-30 17:52:31 -04:00

117 lines
4.8 KiB
Rust

//! Extract a sparse point cloud from a dense [`ReflectivityImage`].
//!
//! A `nx * ny * nz` voxel grid is not a useful end product on its own --
//! real point-cloud consumers (visualization, `ruview-unified`'s
//! `GaussianMap`, downstream fusion) want a short list of "here is
//! something" points, not every voxel. This module does simple
//! threshold + local-maximum extraction: no clustering, no material
//! classification, no confidence calibration against real data (ADR-283
//! §5 -- explicitly out of scope for this crate).
use crate::geometry::Point3;
use crate::reconstruct::ReflectivityImage;
use serde::{Deserialize, Serialize};
/// A single detected point: a location and its reconstructed reflectivity
/// magnitude (relative, not calibrated to any physical unit).
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct PointCloudPoint {
/// World-space location, meters.
pub position: Point3,
/// Reconstructed reflectivity magnitude at this voxel.
pub magnitude: f64,
}
/// Extract detected points from `image`: every voxel whose magnitude is
/// (a) at least `threshold_fraction` of the image's peak magnitude, and
/// (b) a local maximum among its 6-connected neighbors (so a single broad
/// blob yields one point, not every voxel inside it).
///
/// `threshold_fraction` must be in `(0.0, 1.0]`. A typical value is
/// `0.5` (a classic radar/SAR "half-power point" style threshold).
pub fn extract_point_cloud(image: &ReflectivityImage, threshold_fraction: f64) -> Vec<PointCloudPoint> {
assert!(
threshold_fraction > 0.0 && threshold_fraction <= 1.0,
"threshold_fraction must be in (0, 1]"
);
let grid = &image.grid;
let peak = image.magnitude.iter().cloned().fold(0.0_f64, f64::max);
if peak <= 0.0 {
return Vec::new();
}
let threshold = peak * threshold_fraction;
let mag_at = |i: i64, j: i64, k: i64| -> f64 {
if i < 0 || j < 0 || k < 0 || i as usize >= grid.nx || j as usize >= grid.ny || k as usize >= grid.nz {
return f64::NEG_INFINITY;
}
let linear = grid.linear_index(i as usize, j as usize, k as usize);
image.magnitude[linear]
};
let mut points = Vec::new();
for k in 0..grid.nz {
for j in 0..grid.ny {
for i in 0..grid.nx {
let here = mag_at(i as i64, j as i64, k as i64);
if here < threshold {
continue;
}
let neighbors = [
mag_at(i as i64 - 1, j as i64, k as i64),
mag_at(i as i64 + 1, j as i64, k as i64),
mag_at(i as i64, j as i64 - 1, k as i64),
mag_at(i as i64, j as i64 + 1, k as i64),
mag_at(i as i64, j as i64, k as i64 - 1),
mag_at(i as i64, j as i64, k as i64 + 1),
];
if neighbors.iter().all(|&n| here >= n) {
points.push(PointCloudPoint {
position: grid.voxel_center(i, j, k),
magnitude: here,
});
}
}
}
}
points
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::linear_aperture;
use crate::measurement::{simulate_measurement, FrequencySweep, ScatteringTarget};
use crate::reconstruct::{backproject, VoxelGrid};
#[test]
fn single_target_yields_a_single_detected_point() {
let poses = linear_aperture(Point3::new(-0.5, 0.0, 0.0), Point3::new(0.5, 0.0, 0.0), 21);
let sweep = FrequencySweep::new(2.0e9, 6.0e9, 32);
let target = ScatteringTarget::new(Point3::new(0.0, 2.0, 0.0), 1.0);
let measurement = simulate_measurement(&poses, &sweep, &[target], 0.0, 3);
let grid = VoxelGrid::new(Point3::new(-0.5, 1.6, -0.5), 0.05, 21, 17, 21);
let image = backproject(&measurement, &poses, &sweep, &grid);
let points = extract_point_cloud(&image, 0.5);
assert!(!points.is_empty(), "must detect at least the true target");
let best = points.iter().max_by(|a, b| a.magnitude.partial_cmp(&b.magnitude).unwrap()).unwrap();
assert!(best.position.distance(&target.position) < 0.1);
}
#[test]
fn empty_image_yields_no_points() {
let grid = VoxelGrid::new(Point3::new(0.0, 0.0, 0.0), 0.1, 3, 3, 3);
let image = ReflectivityImage { grid, magnitude: vec![0.0; grid.len()] };
assert!(extract_point_cloud(&image, 0.5).is_empty());
}
#[test]
#[should_panic(expected = "threshold_fraction")]
fn rejects_out_of_range_threshold() {
let grid = VoxelGrid::new(Point3::new(0.0, 0.0, 0.0), 0.1, 2, 2, 2);
let image = ReflectivityImage { grid, magnitude: vec![1.0; grid.len()] };
let _ = extract_point_cloud(&image, 1.5);
}
}