mirror of
https://github.com/ruvnet/RuView
synced 2026-07-30 18:41:42 +00:00
128 lines
4.6 KiB
Rust
128 lines
4.6 KiB
Rust
//! Bounded summaries for ADR-269 Qualcomm MIMO CSI frames.
|
|
|
|
use serde::Serialize;
|
|
use wifi_densepose_hardware::qualcomm_csi::{CsiFlags, CsiFrame, CsiPayload, ReportKind};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
|
pub(crate) struct QualcommCsiSnapshot {
|
|
pub event_type: &'static str,
|
|
pub source: &'static str,
|
|
pub report_kind: &'static str,
|
|
pub sequence: u32,
|
|
pub timestamp_us: u64,
|
|
pub device_id: String,
|
|
pub chipset: &'static str,
|
|
pub center_freq_khz: u32,
|
|
pub bandwidth_mhz: u16,
|
|
pub tx_count: u8,
|
|
pub rx_count: u8,
|
|
pub subcarrier_count: u16,
|
|
pub element_count: usize,
|
|
pub ppdu_type: String,
|
|
pub rssi_dbm: Vec<i8>,
|
|
pub noise_floor_dbm: i8,
|
|
pub calibrated: bool,
|
|
pub synthetic: bool,
|
|
pub saturated: bool,
|
|
pub time_synchronized: bool,
|
|
pub dropped_predecessor: bool,
|
|
pub calibration_id: u32,
|
|
pub subcarrier_spacing_hz: f32,
|
|
pub mean_amplitude: Option<f32>,
|
|
pub peak_amplitude: Option<f32>,
|
|
}
|
|
|
|
impl QualcommCsiSnapshot {
|
|
pub(crate) fn from_frame(frame: &CsiFrame) -> Self {
|
|
let synthetic = frame.flags.contains(CsiFlags::SYNTHETIC);
|
|
let (mean_amplitude, peak_amplitude) = amplitude_summary(frame);
|
|
Self {
|
|
event_type: "qualcomm_csi",
|
|
source: if synthetic {
|
|
"qualcomm:simulated"
|
|
} else {
|
|
"qualcomm"
|
|
},
|
|
report_kind: match frame.report_kind {
|
|
ReportKind::Csi => "csi",
|
|
ReportKind::Capabilities => "capabilities",
|
|
},
|
|
sequence: frame.sequence,
|
|
timestamp_us: frame.timestamp_us,
|
|
device_id: format!("{:016x}", frame.device_id),
|
|
chipset: frame.chipset.name(),
|
|
center_freq_khz: frame.center_freq_khz,
|
|
bandwidth_mhz: frame.bandwidth_mhz,
|
|
tx_count: frame.tx_count,
|
|
rx_count: frame.rx_count,
|
|
subcarrier_count: frame.subcarrier_count,
|
|
element_count: frame.payload.len(),
|
|
ppdu_type: format!("{:?}", frame.ppdu_type).to_ascii_lowercase(),
|
|
rssi_dbm: frame.payload.rssi_dbm().to_vec(),
|
|
noise_floor_dbm: frame.noise_floor_dbm,
|
|
calibrated: frame.flags.contains(CsiFlags::CALIBRATED),
|
|
synthetic,
|
|
saturated: frame.flags.contains(CsiFlags::SATURATED),
|
|
time_synchronized: frame.flags.contains(CsiFlags::TIME_SYNCHRONIZED),
|
|
dropped_predecessor: frame.flags.contains(CsiFlags::DROPPED_PREDECESSOR),
|
|
calibration_id: frame.calibration_id,
|
|
subcarrier_spacing_hz: frame.subcarrier_spacing_hz,
|
|
mean_amplitude,
|
|
peak_amplitude,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn amplitude_summary(frame: &CsiFrame) -> (Option<f32>, Option<f32>) {
|
|
let amplitudes: Vec<f32> = match &frame.payload {
|
|
CsiPayload::ComplexI16 { values, .. } => values
|
|
.iter()
|
|
.map(|[i, q]| (*i as f32).hypot(*q as f32) * frame.scale)
|
|
.collect(),
|
|
CsiPayload::ComplexF32 { values, .. } => values
|
|
.iter()
|
|
.map(|[i, q]| i.hypot(*q) * frame.scale)
|
|
.collect(),
|
|
CsiPayload::Bytes(_) => return (None, None),
|
|
};
|
|
if amplitudes.is_empty() {
|
|
return (None, None);
|
|
}
|
|
let mean = amplitudes.iter().sum::<f32>() / amplitudes.len() as f32;
|
|
let peak = amplitudes.into_iter().max_by(f32::total_cmp);
|
|
(Some(mean), peak)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use wifi_densepose_hardware::qualcomm_csi::simulator::{QualcommCsiSimulator, SimulatorConfig};
|
|
|
|
#[test]
|
|
fn simulator_summary_preserves_dimensions_and_provenance() {
|
|
let mut sim = QualcommCsiSimulator::new(SimulatorConfig::default()).unwrap();
|
|
let snapshot = QualcommCsiSnapshot::from_frame(&sim.next_frame());
|
|
assert_eq!(snapshot.source, "qualcomm:simulated");
|
|
assert_eq!(
|
|
(
|
|
snapshot.tx_count,
|
|
snapshot.rx_count,
|
|
snapshot.subcarrier_count
|
|
),
|
|
(2, 3, 114)
|
|
);
|
|
assert_eq!(snapshot.element_count, 684);
|
|
assert!(snapshot.mean_amplitude.unwrap() > 0.0);
|
|
assert!(snapshot.peak_amplitude.unwrap() >= snapshot.mean_amplitude.unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn capability_summary_does_not_invent_signal_statistics() {
|
|
let sim = QualcommCsiSimulator::new(SimulatorConfig::default()).unwrap();
|
|
let snapshot = QualcommCsiSnapshot::from_frame(&sim.capabilities_frame());
|
|
assert_eq!(snapshot.report_kind, "capabilities");
|
|
assert_eq!(snapshot.mean_amplitude, None);
|
|
assert!(snapshot.rssi_dbm.is_empty());
|
|
}
|
|
}
|