use crate::engine::models::LatencyStats;
use super::statistics;
const TRIM_FRACTION: f64 = 0.1;
#[derive(Debug, Clone, Default)]
pub struct LatencyCalculator {
samples: Vec<f64>,
failures: u32,
}
impl LatencyCalculator {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn record(&mut self, latency_ms: f64) {
if latency_ms.is_finite() && latency_ms >= 0.0 {
self.samples.push(latency_ms);
}
}
pub fn record_failure(&mut self) {
self.failures += 1;
}
#[must_use]
pub fn sample_count(&self) -> usize {
self.samples.len()
}
#[must_use]
pub fn stats(&self) -> Option<LatencyStats> {
let trimmed = statistics::trimmed(&self.samples, TRIM_FRACTION);
let average_ms = statistics::mean(&trimmed)?;
let jitter_ms = statistics::successive_diff_mean(&self.samples).unwrap_or(0.0);
let min_ms = self.samples.iter().copied().fold(f64::INFINITY, f64::min);
let max_ms = self
.samples
.iter()
.copied()
.fold(f64::NEG_INFINITY, f64::max);
let total = self.samples.len() as f64 + f64::from(self.failures);
let packet_loss_pct = if total > 0.0 {
f64::from(self.failures) / total * 100.0
} else {
0.0
};
Some(LatencyStats {
average_ms,
jitter_ms,
min_ms,
max_ms,
samples: self.samples.len(),
packet_loss_pct,
})
}
}