mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-21 06:10:15 -05:00
feat(bfld): runnable e2e BFI decode tool (ADR-365)
Adds `examples/bfi_decode.rs`: read a radiotap pcap, decode every VHT beamforming report, print the report-rate dashboard (decoded/sec, gaps, dupes, failures) and the motion-energy series. Ties capture -> cbr -> features into one runnable front-end. cargo run -p wifi-densepose-bfld --example bfi_decode -- capture.pcap Verified on a 3-report synthetic pcap (decoded 3, 1 seq gap, 6.67/s). Values are only as real as the capture; a real capture is still hardware- gated (see ADR-365). clippy -D warnings clean. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
1 parent
60deed45ed
commit
43daa2958d
1 file changed
+77
@@ -0,0 +1,77 @@
|
||||
//! End-to-end BFI decode tool: read a radiotap pcap, decode every VHT
|
||||
//! Compressed Beamforming Report, and print a report-rate dashboard plus the
|
||||
//! motion-energy time series (ADR-365).
|
||||
//!
|
||||
//! This is the runnable front-end for the BFLD BFI path. It works on any
|
||||
//! `LINKTYPE_IEEE802_11_RADIOTAP` pcap; the values it prints are only as real
|
||||
//! as the capture — a synthetic pcap yields synthetic numbers.
|
||||
//!
|
||||
//! Run with:
|
||||
//! ```sh
|
||||
//! cargo run -p wifi-densepose-bfld --example bfi_decode -- capture.pcap
|
||||
//! ```
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
use wifi_densepose_bfld::capture::decode_pcap;
|
||||
use wifi_densepose_bfld::features::motion_series;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let Some(path) = std::env::args().nth(1) else {
|
||||
eprintln!("usage: bfi_decode <capture.pcap>");
|
||||
return ExitCode::from(2);
|
||||
};
|
||||
let bytes = match std::fs::read(&path) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
eprintln!("cannot read {path}: {e}");
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
};
|
||||
let (reports, summary) = match decode_pcap(&bytes) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("not a usable capture: {e}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
};
|
||||
|
||||
println!("== BFI report-rate dashboard ({path}) ==");
|
||||
println!("frames examined: {}", summary.frames);
|
||||
println!("action frames: {}", summary.action_frames);
|
||||
println!("beamforming decoded: {}", summary.decoded);
|
||||
println!("decode failures: {}", summary.decode_failures);
|
||||
println!("sequence gaps: {}", summary.sequence_gaps);
|
||||
println!("sequence dupes: {}", summary.sequence_dupes);
|
||||
println!("reports/sec: {:.2}", summary.reports_per_sec());
|
||||
|
||||
if let Some(first) = reports.first() {
|
||||
let c = &first.report.control;
|
||||
println!(
|
||||
"first report: Nr{}xNc{} {}MHz Ng{:?} {} avg_snr={:?}",
|
||||
c.nr,
|
||||
c.nc,
|
||||
c.width.mhz(),
|
||||
c.grouping,
|
||||
if c.mu { "MU" } else { "SU" },
|
||||
first.report.avg_snr,
|
||||
);
|
||||
}
|
||||
|
||||
let series = motion_series(&reports);
|
||||
if series.is_empty() {
|
||||
println!("motion series: (need >=2 same-dimension reports)");
|
||||
} else {
|
||||
let peak = series.iter().map(|&(_, e)| e).fold(0.0_f64, f64::max);
|
||||
let mean = series.iter().map(|&(_, e)| e).sum::<f64>() / series.len() as f64;
|
||||
println!("motion samples: {} (mean {mean:.4}, peak {peak:.4})", series.len());
|
||||
}
|
||||
|
||||
if summary.decoded == 0 {
|
||||
eprintln!(
|
||||
"note: zero beamforming reports. On 2.4 GHz there are none (VHT/HE is 5/6 GHz); \
|
||||
confirm the client is on a 5 GHz SSID and the monitor is on its exact channel+width."
|
||||
);
|
||||
}
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Reference in new issue
Block a user