Files
wifi-densepose/rust-port/wifi-densepose-rs/crates/wifi-densepose-mat/benches/detection_bench.rs
Claude 6b20ff0c14 feat: Add wifi-Mat disaster detection enhancements
Implement 6 optional enhancements for the wifi-Mat module:

1. Hardware Integration (csi_receiver.rs + hardware_adapter.rs)
   - ESP32 CSI support via serial/UDP
   - Intel 5300 BFEE file parsing
   - Atheros CSI Tool integration
   - Live UDP packet streaming
   - PCAP replay capability

2. CLI Commands (wifi-densepose-cli/src/mat.rs)
   - `wifi-mat scan` - Run disaster detection scan
   - `wifi-mat status` - Check event status
   - `wifi-mat zones` - Manage scan zones
   - `wifi-mat survivors` - List detected survivors
   - `wifi-mat alerts` - View and acknowledge alerts
   - `wifi-mat export` - Export data in various formats

3. REST API (wifi-densepose-mat/src/api/)
   - Full CRUD for disaster events
   - Zone management endpoints
   - Survivor and alert queries
   - WebSocket streaming for real-time updates
   - Comprehensive DTOs and error handling

4. WASM Build (wifi-densepose-wasm/src/mat.rs)
   - Browser-based disaster dashboard
   - Real-time survivor tracking
   - Zone visualization
   - Alert management
   - JavaScript API bindings

5. Detection Benchmarks (benches/detection_bench.rs)
   - Single survivor detection
   - Multi-survivor detection
   - Full pipeline benchmarks
   - Signal processing benchmarks
   - Hardware adapter benchmarks

6. ML Models for Debris Penetration (ml/)
   - DebrisModel for material analysis
   - VitalSignsClassifier for triage
   - FFT-based feature extraction
   - Bandpass filtering
   - Monte Carlo dropout for uncertainty

All 134 unit tests pass. Compilation verified for:
- wifi-densepose-mat
- wifi-densepose-cli
- wifi-densepose-wasm (with mat feature)
2026-01-13 18:23:03 +00:00

907 lines
30 KiB
Rust

//! Performance benchmarks for wifi-densepose-mat detection algorithms.
//!
//! Run with: cargo bench --package wifi-densepose-mat
//!
//! Benchmarks cover:
//! - Breathing detection at various signal lengths
//! - Heartbeat detection performance
//! - Movement classification
//! - Full detection pipeline
//! - Localization algorithms (triangulation, depth estimation)
//! - Alert generation
use criterion::{
black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
};
use std::f64::consts::PI;
use wifi_densepose_mat::{
// Detection types
BreathingDetector, BreathingDetectorConfig,
HeartbeatDetector, HeartbeatDetectorConfig,
MovementClassifier, MovementClassifierConfig,
DetectionConfig, DetectionPipeline, VitalSignsDetector,
// Localization types
Triangulator, DepthEstimator,
// Alerting types
AlertGenerator,
// Domain types exported at crate root
BreathingPattern, BreathingType, VitalSignsReading,
MovementProfile, ScanZoneId, Survivor,
};
// Types that need to be accessed from submodules
use wifi_densepose_mat::detection::CsiDataBuffer;
use wifi_densepose_mat::domain::{
ConfidenceScore, SensorPosition, SensorType,
DebrisProfile, DebrisMaterial, MoistureLevel, MetalContent,
};
use chrono::Utc;
// =============================================================================
// Test Data Generators
// =============================================================================
/// Generate a clean breathing signal at specified rate
fn generate_breathing_signal(rate_bpm: f64, sample_rate: f64, duration_secs: f64) -> Vec<f64> {
let num_samples = (sample_rate * duration_secs) as usize;
let freq = rate_bpm / 60.0;
(0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
(2.0 * PI * freq * t).sin()
})
.collect()
}
/// Generate a breathing signal with noise
fn generate_noisy_breathing_signal(
rate_bpm: f64,
sample_rate: f64,
duration_secs: f64,
noise_level: f64,
) -> Vec<f64> {
let num_samples = (sample_rate * duration_secs) as usize;
let freq = rate_bpm / 60.0;
(0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
let signal = (2.0 * PI * freq * t).sin();
// Simple pseudo-random noise based on sample index
let noise = ((i as f64 * 12345.6789).sin() * 2.0 - 1.0) * noise_level;
signal + noise
})
.collect()
}
/// Generate heartbeat signal with micro-Doppler characteristics
fn generate_heartbeat_signal(rate_bpm: f64, sample_rate: f64, duration_secs: f64) -> Vec<f64> {
let num_samples = (sample_rate * duration_secs) as usize;
let freq = rate_bpm / 60.0;
(0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
let phase = 2.0 * PI * freq * t;
// Heartbeat is more pulse-like than sinusoidal
0.3 * phase.sin() + 0.1 * (2.0 * phase).sin() + 0.05 * (3.0 * phase).sin()
})
.collect()
}
/// Generate combined breathing + heartbeat signal
fn generate_combined_vital_signal(
breathing_rate: f64,
heart_rate: f64,
sample_rate: f64,
duration_secs: f64,
) -> (Vec<f64>, Vec<f64>) {
let num_samples = (sample_rate * duration_secs) as usize;
let br_freq = breathing_rate / 60.0;
let hr_freq = heart_rate / 60.0;
let amplitudes: Vec<f64> = (0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
// Breathing dominates amplitude
(2.0 * PI * br_freq * t).sin()
})
.collect();
let phases: Vec<f64> = (0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
// Phase captures both but heartbeat is more prominent
let breathing = 0.3 * (2.0 * PI * br_freq * t).sin();
let heartbeat = 0.5 * (2.0 * PI * hr_freq * t).sin();
breathing + heartbeat
})
.collect();
(amplitudes, phases)
}
/// Generate multi-person scenario with overlapping signals
fn generate_multi_person_signal(
person_count: usize,
sample_rate: f64,
duration_secs: f64,
) -> Vec<f64> {
let num_samples = (sample_rate * duration_secs) as usize;
// Different breathing rates for each person
let base_rates: Vec<f64> = (0..person_count)
.map(|i| 12.0 + (i as f64 * 3.5)) // 12, 15.5, 19, 22.5... BPM
.collect();
(0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
base_rates.iter()
.enumerate()
.map(|(idx, &rate)| {
let freq = rate / 60.0;
let amplitude = 1.0 / (idx + 1) as f64; // Distance-based attenuation
let phase_offset = idx as f64 * PI / 4.0; // Different phases
amplitude * (2.0 * PI * freq * t + phase_offset).sin()
})
.sum::<f64>()
})
.collect()
}
/// Generate movement signal with specified characteristics
fn generate_movement_signal(
movement_type: &str,
sample_rate: f64,
duration_secs: f64,
) -> Vec<f64> {
let num_samples = (sample_rate * duration_secs) as usize;
match movement_type {
"gross" => {
// Large, irregular movements
let mut signal = vec![0.0; num_samples];
for i in (num_samples / 4)..(num_samples / 2) {
signal[i] = 2.0;
}
for i in (3 * num_samples / 4)..(4 * num_samples / 5) {
signal[i] = -1.5;
}
signal
}
"tremor" => {
// High-frequency tremor (8-12 Hz)
(0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
0.3 * (2.0 * PI * 10.0 * t).sin()
})
.collect()
}
"periodic" => {
// Low-frequency periodic (breathing-like)
(0..num_samples)
.map(|i| {
let t = i as f64 / sample_rate;
0.5 * (2.0 * PI * 0.25 * t).sin()
})
.collect()
}
_ => vec![0.0; num_samples], // No movement
}
}
/// Create test sensor positions in a triangular configuration
fn create_test_sensors(count: usize) -> Vec<SensorPosition> {
(0..count)
.map(|i| {
let angle = 2.0 * PI * i as f64 / count as f64;
SensorPosition {
id: format!("sensor_{}", i),
x: 10.0 * angle.cos(),
y: 10.0 * angle.sin(),
z: 1.5,
sensor_type: SensorType::Transceiver,
is_operational: true,
}
})
.collect()
}
/// Create test debris profile
fn create_test_debris() -> DebrisProfile {
DebrisProfile {
primary_material: DebrisMaterial::Mixed,
void_fraction: 0.25,
moisture_content: MoistureLevel::Dry,
metal_content: MetalContent::Low,
}
}
/// Create test survivor for alert generation
fn create_test_survivor() -> Survivor {
let vitals = VitalSignsReading {
breathing: Some(BreathingPattern {
rate_bpm: 18.0,
amplitude: 0.8,
regularity: 0.9,
pattern_type: BreathingType::Normal,
}),
heartbeat: None,
movement: MovementProfile::default(),
timestamp: Utc::now(),
confidence: ConfidenceScore::new(0.85),
};
Survivor::new(ScanZoneId::new(), vitals, None)
}
// =============================================================================
// Breathing Detection Benchmarks
// =============================================================================
fn bench_breathing_detection(c: &mut Criterion) {
let mut group = c.benchmark_group("breathing_detection");
let detector = BreathingDetector::with_defaults();
let sample_rate = 100.0; // 100 Hz
// Benchmark different signal lengths
for duration in [5.0, 10.0, 30.0, 60.0] {
let signal = generate_breathing_signal(16.0, sample_rate, duration);
let num_samples = signal.len();
group.throughput(Throughput::Elements(num_samples as u64));
group.bench_with_input(
BenchmarkId::new("clean_signal", format!("{}s", duration as u32)),
&signal,
|b, signal| {
b.iter(|| detector.detect(black_box(signal), black_box(sample_rate)))
},
);
}
// Benchmark different noise levels
for noise_level in [0.0, 0.1, 0.3, 0.5] {
let signal = generate_noisy_breathing_signal(16.0, sample_rate, 30.0, noise_level);
group.bench_with_input(
BenchmarkId::new("noisy_signal", format!("noise_{}", (noise_level * 10.0) as u32)),
&signal,
|b, signal| {
b.iter(|| detector.detect(black_box(signal), black_box(sample_rate)))
},
);
}
// Benchmark different breathing rates
for rate in [8.0, 16.0, 25.0, 35.0] {
let signal = generate_breathing_signal(rate, sample_rate, 30.0);
group.bench_with_input(
BenchmarkId::new("rate_variation", format!("{}bpm", rate as u32)),
&signal,
|b, signal| {
b.iter(|| detector.detect(black_box(signal), black_box(sample_rate)))
},
);
}
// Benchmark with custom config (high sensitivity)
let high_sensitivity_config = BreathingDetectorConfig {
min_rate_bpm: 2.0,
max_rate_bpm: 50.0,
min_amplitude: 0.05,
window_size: 1024,
window_overlap: 0.75,
confidence_threshold: 0.2,
};
let sensitive_detector = BreathingDetector::new(high_sensitivity_config);
let signal = generate_noisy_breathing_signal(16.0, sample_rate, 30.0, 0.3);
group.bench_with_input(
BenchmarkId::new("high_sensitivity", "30s_noisy"),
&signal,
|b, signal| {
b.iter(|| sensitive_detector.detect(black_box(signal), black_box(sample_rate)))
},
);
group.finish();
}
// =============================================================================
// Heartbeat Detection Benchmarks
// =============================================================================
fn bench_heartbeat_detection(c: &mut Criterion) {
let mut group = c.benchmark_group("heartbeat_detection");
let detector = HeartbeatDetector::with_defaults();
let sample_rate = 1000.0; // 1 kHz for micro-Doppler
// Benchmark different signal lengths
for duration in [5.0, 10.0, 30.0] {
let signal = generate_heartbeat_signal(72.0, sample_rate, duration);
let num_samples = signal.len();
group.throughput(Throughput::Elements(num_samples as u64));
group.bench_with_input(
BenchmarkId::new("clean_signal", format!("{}s", duration as u32)),
&signal,
|b, signal| {
b.iter(|| detector.detect(black_box(signal), black_box(sample_rate), None))
},
);
}
// Benchmark with known breathing rate (improves filtering)
let signal = generate_heartbeat_signal(72.0, sample_rate, 30.0);
group.bench_with_input(
BenchmarkId::new("with_breathing_rate", "72bpm_known_br"),
&signal,
|b, signal| {
b.iter(|| {
detector.detect(
black_box(signal),
black_box(sample_rate),
black_box(Some(16.0)), // Known breathing rate
)
})
},
);
// Benchmark different heart rates
for rate in [50.0, 72.0, 100.0, 150.0] {
let signal = generate_heartbeat_signal(rate, sample_rate, 10.0);
group.bench_with_input(
BenchmarkId::new("rate_variation", format!("{}bpm", rate as u32)),
&signal,
|b, signal| {
b.iter(|| detector.detect(black_box(signal), black_box(sample_rate), None))
},
);
}
// Benchmark enhanced processing config
let enhanced_config = HeartbeatDetectorConfig {
min_rate_bpm: 30.0,
max_rate_bpm: 200.0,
min_signal_strength: 0.02,
window_size: 2048,
enhanced_processing: true,
confidence_threshold: 0.3,
};
let enhanced_detector = HeartbeatDetector::new(enhanced_config);
let signal = generate_heartbeat_signal(72.0, sample_rate, 10.0);
group.bench_with_input(
BenchmarkId::new("enhanced_processing", "2048_window"),
&signal,
|b, signal| {
b.iter(|| enhanced_detector.detect(black_box(signal), black_box(sample_rate), None))
},
);
group.finish();
}
// =============================================================================
// Movement Classification Benchmarks
// =============================================================================
fn bench_movement_classification(c: &mut Criterion) {
let mut group = c.benchmark_group("movement_classification");
let classifier = MovementClassifier::with_defaults();
let sample_rate = 100.0;
// Benchmark different movement types
for movement_type in ["none", "gross", "tremor", "periodic"] {
let signal = generate_movement_signal(movement_type, sample_rate, 10.0);
let num_samples = signal.len();
group.throughput(Throughput::Elements(num_samples as u64));
group.bench_with_input(
BenchmarkId::new("movement_type", movement_type),
&signal,
|b, signal| {
b.iter(|| classifier.classify(black_box(signal), black_box(sample_rate)))
},
);
}
// Benchmark different signal lengths
for duration in [2.0, 5.0, 10.0, 30.0] {
let signal = generate_movement_signal("gross", sample_rate, duration);
group.bench_with_input(
BenchmarkId::new("signal_length", format!("{}s", duration as u32)),
&signal,
|b, signal| {
b.iter(|| classifier.classify(black_box(signal), black_box(sample_rate)))
},
);
}
// Benchmark with custom sensitivity
let sensitive_config = MovementClassifierConfig {
movement_threshold: 0.05,
gross_movement_threshold: 0.3,
window_size: 200,
periodicity_threshold: 0.2,
};
let sensitive_classifier = MovementClassifier::new(sensitive_config);
let signal = generate_movement_signal("tremor", sample_rate, 10.0);
group.bench_with_input(
BenchmarkId::new("high_sensitivity", "tremor_detection"),
&signal,
|b, signal| {
b.iter(|| sensitive_classifier.classify(black_box(signal), black_box(sample_rate)))
},
);
group.finish();
}
// =============================================================================
// Full Detection Pipeline Benchmarks
// =============================================================================
fn bench_detection_pipeline(c: &mut Criterion) {
let mut group = c.benchmark_group("detection_pipeline");
group.sample_size(50); // Reduce sample size for slower benchmarks
let sample_rate = 100.0;
// Standard pipeline (breathing + movement)
let standard_config = DetectionConfig {
sample_rate,
enable_heartbeat: false,
min_confidence: 0.3,
..Default::default()
};
let standard_pipeline = DetectionPipeline::new(standard_config);
// Full pipeline (breathing + heartbeat + movement)
let full_config = DetectionConfig {
sample_rate: 1000.0,
enable_heartbeat: true,
min_confidence: 0.3,
..Default::default()
};
let full_pipeline = DetectionPipeline::new(full_config);
// Benchmark standard pipeline at different data sizes
for duration in [5.0, 10.0, 30.0] {
let (amplitudes, phases) = generate_combined_vital_signal(16.0, 72.0, sample_rate, duration);
let mut buffer = CsiDataBuffer::new(sample_rate);
buffer.add_samples(&amplitudes, &phases);
group.throughput(Throughput::Elements(amplitudes.len() as u64));
group.bench_with_input(
BenchmarkId::new("standard_pipeline", format!("{}s", duration as u32)),
&buffer,
|b, buffer| {
b.iter(|| standard_pipeline.detect(black_box(buffer)))
},
);
}
// Benchmark full pipeline
for duration in [5.0, 10.0] {
let (amplitudes, phases) = generate_combined_vital_signal(16.0, 72.0, 1000.0, duration);
let mut buffer = CsiDataBuffer::new(1000.0);
buffer.add_samples(&amplitudes, &phases);
group.bench_with_input(
BenchmarkId::new("full_pipeline", format!("{}s", duration as u32)),
&buffer,
|b, buffer| {
b.iter(|| full_pipeline.detect(black_box(buffer)))
},
);
}
// Benchmark multi-person scenarios
for person_count in [1, 2, 3, 5] {
let signal = generate_multi_person_signal(person_count, sample_rate, 30.0);
let mut buffer = CsiDataBuffer::new(sample_rate);
buffer.add_samples(&signal, &signal);
group.bench_with_input(
BenchmarkId::new("multi_person", format!("{}_people", person_count)),
&buffer,
|b, buffer| {
b.iter(|| standard_pipeline.detect(black_box(buffer)))
},
);
}
group.finish();
}
// =============================================================================
// Triangulation Benchmarks
// =============================================================================
fn bench_triangulation(c: &mut Criterion) {
let mut group = c.benchmark_group("triangulation");
let triangulator = Triangulator::with_defaults();
// Benchmark with different sensor counts
for sensor_count in [3, 4, 5, 8, 12] {
let sensors = create_test_sensors(sensor_count);
// Generate RSSI values (simulate target at center)
let rssi_values: Vec<(String, f64)> = sensors.iter()
.map(|s| {
let distance = (s.x * s.x + s.y * s.y).sqrt();
let rssi = -30.0 - 20.0 * distance.log10(); // Path loss model
(s.id.clone(), rssi)
})
.collect();
group.bench_with_input(
BenchmarkId::new("rssi_position", format!("{}_sensors", sensor_count)),
&(sensors.clone(), rssi_values.clone()),
|b, (sensors, rssi)| {
b.iter(|| {
triangulator.estimate_position(black_box(sensors), black_box(rssi))
})
},
);
}
// Benchmark ToA-based positioning
for sensor_count in [3, 4, 5, 8] {
let sensors = create_test_sensors(sensor_count);
// Generate ToA values (time in nanoseconds)
let toa_values: Vec<(String, f64)> = sensors.iter()
.map(|s| {
let distance = (s.x * s.x + s.y * s.y).sqrt();
// Round trip time: 2 * distance / speed_of_light
let toa_ns = 2.0 * distance / 299_792_458.0 * 1e9;
(s.id.clone(), toa_ns)
})
.collect();
group.bench_with_input(
BenchmarkId::new("toa_position", format!("{}_sensors", sensor_count)),
&(sensors.clone(), toa_values.clone()),
|b, (sensors, toa)| {
b.iter(|| {
triangulator.estimate_from_toa(black_box(sensors), black_box(toa))
})
},
);
}
// Benchmark with noisy measurements
let sensors = create_test_sensors(5);
for noise_pct in [0, 5, 10, 20] {
let rssi_values: Vec<(String, f64)> = sensors.iter()
.enumerate()
.map(|(i, s)| {
let distance = (s.x * s.x + s.y * s.y).sqrt();
let rssi = -30.0 - 20.0 * distance.log10();
// Add noise based on index for determinism
let noise = (i as f64 / 10.0) * noise_pct as f64 / 100.0 * 10.0;
(s.id.clone(), rssi + noise)
})
.collect();
group.bench_with_input(
BenchmarkId::new("noisy_rssi", format!("{}pct_noise", noise_pct)),
&(sensors.clone(), rssi_values.clone()),
|b, (sensors, rssi)| {
b.iter(|| {
triangulator.estimate_position(black_box(sensors), black_box(rssi))
})
},
);
}
group.finish();
}
// =============================================================================
// Depth Estimation Benchmarks
// =============================================================================
fn bench_depth_estimation(c: &mut Criterion) {
let mut group = c.benchmark_group("depth_estimation");
let estimator = DepthEstimator::with_defaults();
let debris = create_test_debris();
// Benchmark single-path depth estimation
for attenuation in [10.0, 20.0, 40.0, 60.0] {
group.bench_with_input(
BenchmarkId::new("single_path", format!("{}dB", attenuation as u32)),
&attenuation,
|b, &attenuation| {
b.iter(|| {
estimator.estimate_depth(
black_box(attenuation),
black_box(5.0), // 5m horizontal distance
black_box(&debris),
)
})
},
);
}
// Benchmark different debris types
let debris_types = [
("snow", DebrisMaterial::Snow),
("wood", DebrisMaterial::Wood),
("light_concrete", DebrisMaterial::LightConcrete),
("heavy_concrete", DebrisMaterial::HeavyConcrete),
("mixed", DebrisMaterial::Mixed),
];
for (name, material) in debris_types {
let debris = DebrisProfile {
primary_material: material,
void_fraction: 0.25,
moisture_content: MoistureLevel::Dry,
metal_content: MetalContent::Low,
};
group.bench_with_input(
BenchmarkId::new("debris_type", name),
&debris,
|b, debris| {
b.iter(|| {
estimator.estimate_depth(
black_box(30.0),
black_box(5.0),
black_box(debris),
)
})
},
);
}
// Benchmark multipath depth estimation
for path_count in [1, 2, 4, 8] {
let reflected_paths: Vec<(f64, f64)> = (0..path_count)
.map(|i| {
(
30.0 + i as f64 * 5.0, // attenuation
1e-9 * (i + 1) as f64, // delay in seconds
)
})
.collect();
group.bench_with_input(
BenchmarkId::new("multipath", format!("{}_paths", path_count)),
&reflected_paths,
|b, paths| {
b.iter(|| {
estimator.estimate_from_multipath(
black_box(25.0),
black_box(paths),
black_box(&debris),
)
})
},
);
}
// Benchmark debris profile estimation
for (variance, multipath, moisture) in [
(0.2, 0.3, 0.2),
(0.5, 0.5, 0.5),
(0.7, 0.8, 0.8),
] {
group.bench_with_input(
BenchmarkId::new("profile_estimation", format!("v{}_m{}", (variance * 10.0) as u32, (multipath * 10.0) as u32)),
&(variance, multipath, moisture),
|b, &(v, m, mo)| {
b.iter(|| {
estimator.estimate_debris_profile(
black_box(v),
black_box(m),
black_box(mo),
)
})
},
);
}
group.finish();
}
// =============================================================================
// Alert Generation Benchmarks
// =============================================================================
fn bench_alert_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("alert_generation");
// Benchmark basic alert generation
let generator = AlertGenerator::new();
let survivor = create_test_survivor();
group.bench_function("generate_basic_alert", |b| {
b.iter(|| generator.generate(black_box(&survivor)))
});
// Benchmark escalation alert
group.bench_function("generate_escalation_alert", |b| {
b.iter(|| {
generator.generate_escalation(
black_box(&survivor),
black_box("Vital signs deteriorating"),
)
})
});
// Benchmark status change alert
use wifi_densepose_mat::domain::TriageStatus;
group.bench_function("generate_status_change_alert", |b| {
b.iter(|| {
generator.generate_status_change(
black_box(&survivor),
black_box(&TriageStatus::Minor),
)
})
});
// Benchmark with zone registration
let mut generator_with_zones = AlertGenerator::new();
for i in 0..100 {
generator_with_zones.register_zone(ScanZoneId::new(), format!("Zone {}", i));
}
group.bench_function("generate_with_zones_lookup", |b| {
b.iter(|| generator_with_zones.generate(black_box(&survivor)))
});
// Benchmark batch alert generation
let survivors: Vec<Survivor> = (0..10).map(|_| create_test_survivor()).collect();
group.bench_function("batch_generate_10_alerts", |b| {
b.iter(|| {
survivors.iter()
.map(|s| generator.generate(black_box(s)))
.collect::<Vec<_>>()
})
});
group.finish();
}
// =============================================================================
// CSI Buffer Operations Benchmarks
// =============================================================================
fn bench_csi_buffer(c: &mut Criterion) {
let mut group = c.benchmark_group("csi_buffer");
let sample_rate = 100.0;
// Benchmark buffer creation and addition
for sample_count in [1000, 5000, 10000, 30000] {
let amplitudes: Vec<f64> = (0..sample_count)
.map(|i| (i as f64 / 100.0).sin())
.collect();
let phases: Vec<f64> = (0..sample_count)
.map(|i| (i as f64 / 50.0).cos())
.collect();
group.throughput(Throughput::Elements(sample_count as u64));
group.bench_with_input(
BenchmarkId::new("add_samples", format!("{}_samples", sample_count)),
&(amplitudes.clone(), phases.clone()),
|b, (amp, phase)| {
b.iter(|| {
let mut buffer = CsiDataBuffer::new(sample_rate);
buffer.add_samples(black_box(amp), black_box(phase));
buffer
})
},
);
}
// Benchmark incremental addition (simulating real-time data)
let chunk_size = 100;
let total_samples = 10000;
let amplitudes: Vec<f64> = (0..chunk_size).map(|i| (i as f64 / 100.0).sin()).collect();
let phases: Vec<f64> = (0..chunk_size).map(|i| (i as f64 / 50.0).cos()).collect();
group.bench_function("incremental_add_100_chunks", |b| {
b.iter(|| {
let mut buffer = CsiDataBuffer::new(sample_rate);
for _ in 0..(total_samples / chunk_size) {
buffer.add_samples(black_box(&amplitudes), black_box(&phases));
}
buffer
})
});
// Benchmark has_sufficient_data check
let mut buffer = CsiDataBuffer::new(sample_rate);
let amplitudes: Vec<f64> = (0..3000).map(|i| (i as f64 / 100.0).sin()).collect();
let phases: Vec<f64> = (0..3000).map(|i| (i as f64 / 50.0).cos()).collect();
buffer.add_samples(&amplitudes, &phases);
group.bench_function("check_sufficient_data", |b| {
b.iter(|| buffer.has_sufficient_data(black_box(10.0)))
});
group.bench_function("calculate_duration", |b| {
b.iter(|| black_box(&buffer).duration())
});
group.finish();
}
// =============================================================================
// Criterion Groups and Main
// =============================================================================
criterion_group!(
name = detection_benches;
config = Criterion::default()
.warm_up_time(std::time::Duration::from_millis(500))
.measurement_time(std::time::Duration::from_secs(2));
targets =
bench_breathing_detection,
bench_heartbeat_detection,
bench_movement_classification
);
criterion_group!(
name = pipeline_benches;
config = Criterion::default()
.warm_up_time(std::time::Duration::from_millis(500))
.measurement_time(std::time::Duration::from_secs(3))
.sample_size(50);
targets = bench_detection_pipeline
);
criterion_group!(
name = localization_benches;
config = Criterion::default()
.warm_up_time(std::time::Duration::from_millis(500))
.measurement_time(std::time::Duration::from_secs(2));
targets =
bench_triangulation,
bench_depth_estimation
);
criterion_group!(
name = alerting_benches;
config = Criterion::default()
.warm_up_time(std::time::Duration::from_millis(300))
.measurement_time(std::time::Duration::from_secs(1));
targets = bench_alert_generation
);
criterion_group!(
name = buffer_benches;
config = Criterion::default()
.warm_up_time(std::time::Duration::from_millis(300))
.measurement_time(std::time::Duration::from_secs(1));
targets = bench_csi_buffer
);
criterion_main!(
detection_benches,
pipeline_benches,
localization_benches,
alerting_benches,
buffer_benches
);