perf(wifi-densepose-sar): incremental phasor rotation in backprojection (~4.4-4.5x, MEASURED)

focus_at_point called Complex64::from_polar (a sin/cos pair) once per
(pose, frequency) term. FrequencySweep::frequencies() produces evenly
spaced frequencies by construction, so the per-term phase is an
arithmetic progression in the frequency index -- the phasor can be
evaluated once per pose and advanced by a fixed complex-multiply step
per frequency instead, turning K trig evaluations into 2.

focus_at_point's signature changes from a raw &[f64] frequency slice
to &FrequencySweep, so the evenly-spaced-frequencies precondition
this optimization depends on is a type-level invariant rather than a
caller-observed one -- an arbitrary non-uniform frequency list is no
longer constructible through this API at all.

MEASURED (criterion regression detection, p < 0.001): ~4.4-4.5x
faster across 512/4096/32768-voxel grids (300us/1.97ms/14.5ms vs the
prior 1.47ms/10.4ms/73.5ms). Proven equivalent, not just faster: a new
test independently reimplements the direct per-frequency computation
as a reference and checks the optimized path against it across four
sweep sizes (incl. the n_steps=1 degenerate case) and both on-target
and off-target points, to <1e-9 relative error.

25 tests (22 unit + 3 integration), 0 failed, clippy-clean.
This commit is contained in:
ruv
2026-07-30 21:09:57 -04:00
parent d781f20e1a
commit 895c04747e
5 changed files with 118 additions and 16 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- **`wifi-densepose-sar` — coherent wideband RF tomography research crate (ADR-283).** New standalone leaf crate (the `nvsim` pattern; zero coupling to `wifi-densepose-hardware` or any real ingestion path) implementing the synthetic-aperture-radar reconstruction primitive a handheld through-wall RF imaging device would need — motivated by comparison against Applied Electrodynamics' "WaveSight" launch, and explicitly scoped below ADR-278's RISE/DiffRadar/GeRaF reproduction gates. Ships: (1) a stepped-frequency, multi-position complex forward measurement simulator (`y_{m,k} = Σ σ_j/R² · exp(-i·4π·f·R/c) + noise`, deterministic ChaCha20 seeding); (2) delay-and-sum backprojection reconstruction onto a 3D voxel grid, rayon-parallelized over voxels; (3) threshold + local-maximum point-cloud extraction; (4) closed-form range/cross-range resolution and antenna-pose coherence-budget formulas (`ΔR=c/2B`, `δ_CR≈λR/2L`, `Δp≤λ/8`) checked against the reconstruction's *actual* behavior in `tests/physics_validation.rs` rather than merely documented — forward-simulating two targets at controlled separations and proving they resolve or merge exactly where the formulas predict, and that reconstructed focus at a known target degrades as injected antenna-pose error grows. Every number is SYNTHETIC/L0 (ADR-282) — no real wideband RF hardware backs this crate; see the crate README and `docs/tutorials/coherent-rf-tomography-backprojection.md` for the full honesty boundary and a worked walkthrough. 24 tests (21 unit + 3 integration), 0 failed, clippy-clean; MEASURED backprojection throughput ~350-450K voxels/sec (criterion, 21 poses × 32 freq steps).
- **`wifi-densepose-sar` — coherent wideband RF tomography research crate (ADR-283).** New standalone leaf crate (the `nvsim` pattern; zero coupling to `wifi-densepose-hardware` or any real ingestion path) implementing the synthetic-aperture-radar reconstruction primitive a handheld through-wall RF imaging device would need — motivated by comparison against Applied Electrodynamics' "WaveSight" launch, and explicitly scoped below ADR-278's RISE/DiffRadar/GeRaF reproduction gates. Ships: (1) a stepped-frequency, multi-position complex forward measurement simulator (`y_{m,k} = Σ σ_j/R² · exp(-i·4π·f·R/c) + noise`, deterministic ChaCha20 seeding); (2) delay-and-sum backprojection reconstruction onto a 3D voxel grid, rayon-parallelized over voxels; (3) threshold + local-maximum point-cloud extraction; (4) closed-form range/cross-range resolution and antenna-pose coherence-budget formulas (`ΔR=c/2B`, `δ_CR≈λR/2L`, `Δp≤λ/8`) checked against the reconstruction's *actual* behavior in `tests/physics_validation.rs` rather than merely documented — forward-simulating two targets at controlled separations and proving they resolve or merge exactly where the formulas predict, and that reconstructed focus at a known target degrades as injected antenna-pose error grows. Every number is SYNTHETIC/L0 (ADR-282) — no real wideband RF hardware backs this crate; see the crate README and `docs/tutorials/coherent-rf-tomography-backprojection.md` for the full honesty boundary and a worked walkthrough. `focus_at_point` exploits the evenly-spaced-by-construction frequency sweep (an arithmetic progression in per-term phase) to evaluate each pose's phasor once and advance it by a fixed complex-multiply step per frequency instead of one `sin`/`cos` pair per frequency — **MEASURED ~4.4-4.5x faster** (criterion regression detection, p < 0.001) than the first-shipped direct-computation version, proven equivalent (not just faster) to an independently reimplemented reference across four sweep sizes and on-/off-target points. 25 tests (22 unit + 3 integration), 0 failed, clippy-clean; MEASURED backprojection throughput ~1.7-2.3M voxels/sec (criterion, 21 poses × 32 freq steps).
- **HOMECORE platform runtime completion — secure native/Wasmtime plugins, authenticated HAP IP, expanded Home Assistant APIs, durable restoration/migration, and voice protocols.** `homecore-server` now owns deterministic compiled-in native plugin registration plus explicitly configured, path-bounded, Ed25519 publisher-verified Wasm packages executed through Wasmtime with setup/state-change/teardown lifecycle; arbitrary native dynamic libraries remain intentionally unsupported. The optional HAP server implements persisted accessory identity and controller records, SRP-6a Pair-Setup M1M6, X25519/Ed25519 Pair-Verify M1M4, HKDF-SHA512/ChaCha20-Poly1305 record framing, authenticated/admin endpoint gates, replay/tamper closure, live entity synchronization, and paired-state `_hap._tcp` mDNS updates (45 focused tests; external Apple certification is not claimed). Startup restores device/entity registries and deterministic latest recorder states before plugins, and migration now atomically preserves forward-compatible device/config-entry fields. The HA-compatible surface adds events, templates, config checks, components, registries, history/logbook with SQL-enforced global response bounds, calendar/camera provider routes, and modern WebSocket negotiation while retaining a machine-readable limitations matrix for integration-specific behavior. Assist adds bounded PCM16, async STT/TTS contracts, an end-to-end speech pipeline, and an authenticated satellite session protocol; real deployments still provide the speech engines.
- **`ruview-unified` increment 3 — Gaussian update-loop completion, separable delay-Doppler, and property-tested boundary hardening.** (1) `GaussianMap::merge_overlapping` (ADR-275 step 5: mutual-Mahalanobis + semantic-compatibility dedup catching drift the insert-time gate misses) and lifetime-aware decay (`τ_eff = τ·(1+ln(1+lifetime/τ))` — confirmed structures outlive transients at equal nominal τ). (2) `delay_doppler_map` reimplemented separably (`O(B²S+S²B)`), proven equivalent to the direct reference to <1e-10 and **measured 8.3× faster** (520 µs vs 4.34 ms at 56×8). (3) `tests/security_boundaries.rs` — 8 `proptest` properties over the boundary surfaces (arbitrary values incl. NaN/±inf via `f64::from_bits`) that found and fixed three input-controlled defects: a BLE-CS phase-unwrap infinite loop on non-finite phases and an ~1e299-iteration loop on finite-huge phases (now O(1) modular unwrap + plausibility bound), and a subnormal Gaussian scale overflowing `1/σ²` to NaN density (now physical σ/occupancy bounds). (4) New criterion benches for all increment-2 hot paths (`to_canonical` 38 µs, `ble_cs_range` 481 ns, AoI planner 647 ns/200 regions, coherent fusion 1.5 µs/32 members, factorized pose 521 ns). ruview-unified now 98 tests (87 lib + 3 acceptance + 8 security), 0 failed, clippy-clean.
- **`ruview-unified` increment 2 — native frame contract + programmable perception (ADR-279..282).** (1) `RfFrameV2` becomes the authoritative RF record: native complex IQ with explicit validity masks, declared `PhaseState`, TX/RX poses + antenna geometry in one building frame, calibration/quality state, and a provenance rule enforced at construction — `Synthetic ⇒ L0Simulation` and `Measured ⇒ ≥ L1CapturedReplay` can never alias (the public L0L5 evidence ladder is now a type); the 56-bin canonical tensor is demoted to a derived compatibility view (`to_canonical`, mask-aware gap-filling through the same normalization path as every adapter; native samples proven byte-untouched). (2) Active sensing control plane (`control.rs`): ETSI-ISAC-vocabulary `SensingTask` admission (raw export always refused; identity requires consent), `SensingAction`/`InformationGoal`, an age-of-information `ActiveSensingPlanner` (priority = uncertainty × change rate × criticality ÷ cost; **measured 95% sensing-traffic reduction** vs uniform refresh on a 20-region scenario), fail-closed `CoherentSensorGroup` fusion gates (time/phase/geometry bounds; five denial paths tested), policy-authorized RIS/movable-antenna actuation receipts, and purpose-scoped `TaskSufficientRepresentation` leakage validation. (3) New modality surfaces: BLE Channel Sounding adapter + `ble_cs_range` treating phase-slope and RTT as **separate cross-validated evidence** (exact distance recovery on synthetic tones; relay-style divergence flagged, never averaged), delay-Doppler-native `FieldAxis` + `delay_doppler_map` (unit-peak tone test), IEEE P3162 synthetic-aperture import profile. (4) RePos-factorized pose head (relative skeleton on the content representation, root on the geometry-conditioned one, calibrated per-joint uncertainties): held-out-room MPJPE 0.0003 m vs 0.2534 m for the monolithic baseline in the room-shortcut leakage experiment; ≤2% structured-adapter budget (740 params). (5) Age gate input now `log(1+age_ms)` per the age-aware-CSI recipe (gradient check re-proven); Gaussian primitives gained `first_seen_ns`/`doppler_variance`/bounded `source_receipts` lineage; `PartitionKey` gained a `session` dimension and `SplitManifest` certifies disjointness across all seven dimensions. 87 tests, 0 failed; crate clippy-clean. Docker images unaffected (no shipped binary consumes the crate yet); Python proof re-verified PASS.
@@ -53,4 +53,10 @@ This is deliberately scoped **one level below** ADR-278's RISE/DiffRadar/GeRaF r
- The workspace gains a real (if intentionally scoped-down) coherent-imaging primitive where before there was none — useful groundwork for ADR-278 if that research program proceeds, and a direct, honest answer to "could this repo build a WaveSight-like device" (no, not without the hardware program described in the motivating comparison; yes, this is the reconstruction-algorithm groundwork such a program would need).
- Zero risk to the existing `wifi-densepose-signal::ruvsense::tomography` (RSS-based RTI) code path or any production pipeline — this crate is not referenced by any of them.
- 24 tests (21 unit + 3 integration physics-validation), 0 failed, clippy-clean. Criterion bench: MEASURED 512/4096/32768-voxel backprojection reconstruction throughput (see crate README for the numbers as last recorded).
- 25 tests (22 unit + 3 integration physics-validation), 0 failed, clippy-clean. Criterion bench: MEASURED 512/4096/32768-voxel backprojection reconstruction throughput (see crate README for the numbers as last recorded). The incremental-phasor-rotation optimization (§7) cut reconstruction time ~4.4-4.5x, proven equivalent to the direct per-frequency computation it replaced.
## 7. Follow-up optimization: incremental phasor rotation (2026-07-30, MEASURED)
`focus_at_point` originally called `Complex64::from_polar` (one `sin`/`cos` pair) per (pose, frequency) term. Since [`FrequencySweep::frequencies`](../../v2/crates/wifi-densepose-sar/src/measurement.rs) produces evenly-spaced frequencies by construction, the per-term phase is an arithmetic progression in the frequency index — so the phasor can be evaluated once per pose and advanced by a fixed complex-multiply step per frequency, replacing K trig evaluations with 2. `focus_at_point`'s signature changed from a raw `&[f64]` frequency slice to `&FrequencySweep`, making the evenly-spaced-frequencies precondition this optimization depends on a type-level invariant rather than a caller-observed one.
**MEASURED (criterion regression detection, p < 0.001): ~4.4-4.5x faster** across 512/4096/32768-voxel grids. **Proven equivalent**, not just faster: `reconstruct::tests::backprojection_incremental_rotation_matches_direct_per_frequency_computation` checks the optimized path against an independently reimplemented direct per-frequency reference, across four sweep sizes (including the `n_steps=1` degenerate case) and both on-target and off-target evaluation points, to <1e-9 relative error.
+16 -4
View File
@@ -42,7 +42,7 @@ cargo bench -p wifi-densepose-sar
`tests/physics_validation.rs` checks the reconstruction's actual behavior
against the closed-form formulas in `resolution.rs` (range resolution,
cross-range/synthetic-aperture resolution, and the antenna-pose coherence
budget) rather than merely asserting them: 24 tests (21 unit + 3
budget) rather than merely asserting them: 25 tests (22 unit + 3
integration), 0 failed, clippy-clean.
## Performance (MEASURED)
@@ -53,10 +53,22 @@ machine, release profile:
| Voxels | Median time | Throughput |
|-------:|------------:|-----------:|
| 512 | 1.47 ms | ~348,000 voxels/s |
| 4,096 | 10.4 ms | ~394,000 voxels/s |
| 32,768 | 73.5 ms | ~446,000 voxels/s |
| 512 | 300 µs | ~1.71M voxels/s |
| 4,096 | 1.97 ms | ~2.08M voxels/s |
| 32,768 | 14.5 ms | ~2.26M voxels/s |
Scales as expected: each voxel's cost is independent (`O(poses × freqs)`
per voxel, embarrassingly parallel), so throughput is roughly constant
across grid sizes and total time scales linearly with voxel count.
**Optimization (MEASURED, criterion regression detection, p < 0.001): ~4.4-4.5x
faster** than the first-shipped implementation, across all three grid
sizes. Frequencies in a [`FrequencySweep`](src/measurement.rs) are evenly
spaced by construction, so the per-(pose, frequency) phase term is an
arithmetic progression; `focus_at_point` now evaluates the phasor once per
pose and advances it by a fixed complex-multiply step per frequency,
instead of one `sin`/`cos` pair (`Complex64::from_polar`) per frequency —
K trig evaluations become 2. Proven equivalent (not just faster) to an
independently-reimplemented direct per-frequency reference in
`reconstruct::tests::backprojection_incremental_rotation_matches_direct_per_frequency_computation`,
across several sweep sizes and both on-target and off-target points.
@@ -120,8 +120,7 @@ pub fn backproject(
grid: &VoxelGrid,
) -> ReflectivityImage {
assert_eq!(poses.len(), measurement.n_poses, "pose count must match measurement");
let freqs = sweep.frequencies();
assert_eq!(freqs.len(), measurement.n_freqs, "frequency count must match measurement");
assert_eq!(sweep.n_steps, measurement.n_freqs, "frequency count must match measurement");
let n = grid.len();
@@ -130,7 +129,7 @@ pub fn backproject(
.map(|linear| {
let (i, j, k) = grid.unflatten(linear);
let voxel = grid.voxel_center(i, j, k);
focus_at_point(measurement, poses, &freqs, &voxel)
focus_at_point(measurement, poses, sweep, &voxel)
})
.collect();
@@ -143,11 +142,30 @@ pub fn backproject(
/// (and tests) can measure focus quality exactly at a location of
/// interest -- e.g. a known target position -- rather than only at
/// whatever grid points happen to be sampled.
pub fn focus_at_point(measurement: &Measurement, poses: &[AntennaPose], freqs: &[f64], point: &Point3) -> f64 {
///
/// Takes `sweep` rather than a raw frequency slice specifically so the
/// evenly-spaced-frequencies guarantee ([`FrequencySweep::frequencies`])
/// is a type-level invariant, not a caller-observed precondition: the
/// implementation below relies on it (see the comment inside the pose
/// loop). Passing an arbitrary non-uniform frequency list is not possible
/// through this signature.
pub fn focus_at_point(measurement: &Measurement, poses: &[AntennaPose], sweep: &FrequencySweep, point: &Point3) -> f64 {
assert_eq!(poses.len(), measurement.n_poses, "pose count must match measurement");
assert_eq!(freqs.len(), measurement.n_freqs, "frequency count must match measurement");
assert_eq!(sweep.n_steps, measurement.n_freqs, "frequency count must match measurement");
let n_terms = (measurement.n_poses * measurement.n_freqs) as f64;
let k = sweep.n_steps;
// Frequencies are evenly spaced by construction: f_kf = start_hz + kf *
// delta_f. That makes the per-term phase phase_kf = 4*pi*f_kf*r/c an
// arithmetic progression in kf, so instead of K trig evaluations
// (Complex64::from_polar per frequency step) the phasor is evaluated
// once and advanced by a fixed per-step rotation -- one complex
// multiply per step instead of a sin/cos pair. Proven equivalent to
// the direct per-frequency computation (independently reimplemented,
// not reusing this code) in
// `backprojection_incremental_rotation_matches_direct_per_frequency_computation`.
let delta_f = if k > 1 { (sweep.stop_hz - sweep.start_hz) / (k - 1) as f64 } else { 0.0 };
let mut acc = Complex64::new(0.0, 0.0);
for (m, pose) in poses.iter().enumerate() {
let r = pose.position.distance(point);
@@ -155,9 +173,15 @@ pub fn focus_at_point(measurement: &Measurement, poses: &[AntennaPose], freqs: &
continue;
}
let gain_compensation = r * r;
for (kf, &f) in freqs.iter().enumerate() {
let phase = 4.0 * PI * f * r / SPEED_OF_LIGHT_M_PER_S;
acc += measurement.get(m, kf) * gain_compensation * Complex64::from_polar(1.0, phase);
let base_phase = 4.0 * PI * sweep.start_hz * r / SPEED_OF_LIGHT_M_PER_S;
let step_phase = 4.0 * PI * delta_f * r / SPEED_OF_LIGHT_M_PER_S;
let step = Complex64::from_polar(1.0, step_phase);
let mut rot = Complex64::from_polar(1.0, base_phase);
for kf in 0..k {
acc += measurement.get(m, kf) * gain_compensation * rot;
if kf + 1 < k {
rot *= step;
}
}
}
acc.norm() / n_terms
@@ -211,4 +235,65 @@ mod tests {
assert!(peak_mag > mean_mag * 5.0, "coherent focus at the target must dominate the incoherent background: peak={peak_mag}, mean={mean_mag}");
}
/// Independent reference: the direct per-frequency computation
/// `focus_at_point` used before the incremental-phasor-rotation
/// optimization (one `Complex64::from_polar` per (pose, frequency)
/// term, no recurrence). Deliberately reimplemented here rather than
/// calling any shared helper, so this test cannot pass by construction.
fn focus_at_point_direct_reference(
measurement: &Measurement,
poses: &[AntennaPose],
sweep: &FrequencySweep,
point: &Point3,
) -> f64 {
let freqs = sweep.frequencies();
let n_terms = (measurement.n_poses * measurement.n_freqs) as f64;
let mut acc = Complex64::new(0.0, 0.0);
for (m, pose) in poses.iter().enumerate() {
let r = pose.position.distance(point);
if r < 1e-6 {
continue;
}
let gain_compensation = r * r;
for (kf, &f) in freqs.iter().enumerate() {
let phase = 4.0 * PI * f * r / SPEED_OF_LIGHT_M_PER_S;
acc += measurement.get(m, kf) * gain_compensation * Complex64::from_polar(1.0, phase);
}
}
acc.norm() / n_terms
}
/// PERF PROOF: the incremental-phasor-rotation `focus_at_point` (2
/// trig evaluations/pose instead of K) matches the direct
/// per-frequency reference to within f64 rounding, across several
/// sweep sizes, ranges, and off-axis points (not just the on-target
/// case, where errors could cancel).
#[test]
fn backprojection_incremental_rotation_matches_direct_per_frequency_computation() {
let poses = linear_aperture(Point3::new(-0.7, 0.0, 0.0), Point3::new(0.6, 0.1, 0.0), 17);
let targets = vec![
ScatteringTarget::new(Point3::new(0.1, 2.3, -0.2), 1.0),
ScatteringTarget::new(Point3::new(-0.4, 1.9, 0.3), 0.6),
];
let test_points = [
Point3::new(0.1, 2.3, -0.2), // on a target
Point3::new(-0.4, 1.9, 0.3), // on the other target
Point3::new(0.0, 2.0, 0.0), // off-target
Point3::new(-0.55, 2.6, 0.4), // off-target, far corner
];
for &(n_steps, start_hz, stop_hz) in &[(1usize, 3.0e9, 3.0e9), (2, 2.0e9, 6.0e9), (8, 1.0e9, 9.0e9), (64, 2.4e9, 2.5e9)] {
let sweep = FrequencySweep::new(start_hz, stop_hz, n_steps);
let measurement = simulate_measurement(&poses, &sweep, &targets, 0.0, 42);
for point in test_points {
let fast = focus_at_point(&measurement, &poses, &sweep, &point);
let reference = focus_at_point_direct_reference(&measurement, &poses, &sweep, &point);
let scale = reference.max(1e-12);
assert!(
(fast - reference).abs() / scale < 1e-9,
"incremental rotation diverged from the direct reference at n_steps={n_steps}, point={point:?}: fast={fast}, reference={reference}"
);
}
}
}
}
@@ -179,7 +179,6 @@ fn phase_error_from_pose_jitter_degrades_focus_beyond_pose_budget() {
.collect()
};
let freqs = sweep.frequencies();
let focus_at_epsilon = |epsilon: f64| -> f64 {
let true_poses = jittered_poses_at(epsilon);
// The measurement is recorded at the (jittered) TRUE antenna
@@ -191,7 +190,7 @@ fn phase_error_from_pose_jitter_degrades_focus_beyond_pose_budget() {
// happens to focus slightly better and mask the coherence loss
// this test is measuring).
let measurement = simulate_measurement(&true_poses, &sweep, &[target], 0.0, 1);
focus_at_point(&measurement, &nominal_poses, &freqs, &target.position)
focus_at_point(&measurement, &nominal_poses, &sweep, &target.position)
};
let levels = [0.0, 0.5 * budget, budget, 2.0 * budget, 4.0 * budget];