use std::time::{Instant, Duration};
use std::collections::VecDeque;
pub struct GoogleCongestionControl {
state: GccState,
arrival_filter: ArrivalTimeFilter,
overuse_detector: OverUseDetector,
rate_controller: RemoteRateController,
current_estimate: u32,
last_update: Option<Instant>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GccState {
Increase,
Hold,
Decrease,
}
struct ArrivalTimeFilter {
deltas: VecDeque<i64>,
smoothed_value: f64,
variance: f64,
}
impl ArrivalTimeFilter {
fn new() -> Self {
Self {
deltas: VecDeque::new(),
smoothed_value: 0.0,
variance: 0.0,
}
}
fn update(&mut self, delta_ms: i64) {
self.deltas.push_back(delta_ms);
while self.deltas.len() > 60 {
self.deltas.pop_front();
}
if !self.deltas.is_empty() {
let sum: i64 = self.deltas.iter().sum();
let mean = sum as f64 / self.deltas.len() as f64;
self.smoothed_value = self.smoothed_value * 0.9 + mean * 0.1;
let variance_sum: f64 = self.deltas
.iter()
.map(|&x| {
let diff = x as f64 - mean;
diff * diff
})
.sum();
self.variance = variance_sum / self.deltas.len() as f64;
}
}
fn value(&self) -> f64 {
self.smoothed_value
}
fn variance(&self) -> f64 {
self.variance
}
}
struct OverUseDetector {
threshold: f64,
estimate: f64,
error_covariance: f64,
process_noise: f64,
measurement_noise: f64,
hypothesis: OverUseHypothesis,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OverUseHypothesis {
Normal,
OverUsing,
UnderUsing,
}
impl OverUseDetector {
fn new() -> Self {
Self {
threshold: 12.5, estimate: 0.0,
error_covariance: 100.0,
process_noise: 1e-3,
measurement_noise: 1e-1,
hypothesis: OverUseHypothesis::Normal,
}
}
fn update(&mut self, arrival_delta: f64, timestamp_delta: f64) {
if timestamp_delta <= 0.0 {
return;
}
self.error_covariance += self.process_noise;
let innovation = arrival_delta - self.estimate;
let kalman_gain = self.error_covariance / (self.error_covariance + self.measurement_noise);
self.estimate += kalman_gain * innovation;
self.error_covariance *= 1.0 - kalman_gain;
let threshold = self.threshold;
self.hypothesis = if self.estimate > threshold {
OverUseHypothesis::OverUsing
} else if self.estimate < -threshold {
OverUseHypothesis::UnderUsing
} else {
OverUseHypothesis::Normal
};
}
fn hypothesis(&self) -> OverUseHypothesis {
self.hypothesis
}
fn estimate(&self) -> f64 {
self.estimate
}
}
struct RemoteRateController {
state: GccState,
current_rate: u32,
last_update: Instant,
increase_rate: f64,
decrease_factor: f64,
min_rate: u32,
max_rate: u32,
}
impl RemoteRateController {
fn new(initial_rate: u32) -> Self {
Self {
state: GccState::Hold,
current_rate: initial_rate,
last_update: Instant::now(),
increase_rate: 1.08, decrease_factor: 0.85, min_rate: 30_000, max_rate: 50_000_000, }
}
fn update(&mut self, hypothesis: OverUseHypothesis, incoming_rate: u32) -> u32 {
let now = Instant::now();
let time_delta = now.duration_since(self.last_update).as_millis() as f64;
self.last_update = now;
match hypothesis {
OverUseHypothesis::OverUsing => {
self.state = GccState::Decrease;
self.current_rate = (self.current_rate as f64 * self.decrease_factor) as u32;
}
OverUseHypothesis::UnderUsing => {
self.state = GccState::Increase;
if time_delta > 100.0 { let increase = (incoming_rate as f64 * 0.05).max(1000.0); self.current_rate = (self.current_rate as f64 + increase) as u32;
}
}
OverUseHypothesis::Normal => {
self.state = GccState::Hold;
}
}
self.current_rate = self.current_rate.clamp(self.min_rate, self.max_rate);
self.current_rate
}
fn current_rate(&self) -> u32 {
self.current_rate
}
fn state(&self) -> GccState {
self.state
}
}
impl GoogleCongestionControl {
pub fn new(initial_bitrate: u32) -> Self {
Self {
state: GccState::Hold,
arrival_filter: ArrivalTimeFilter::new(),
overuse_detector: OverUseDetector::new(),
rate_controller: RemoteRateController::new(initial_bitrate),
current_estimate: initial_bitrate,
last_update: None,
}
}
pub fn update_with_feedback(&mut self, packets: &[PacketFeedback]) -> u32 {
if packets.is_empty() {
return self.current_estimate;
}
for window in packets.windows(2) {
let delta_arrival = window[1].arrival_time_ms - window[0].arrival_time_ms;
let delta_timestamp = window[1].send_time_ms - window[0].send_time_ms;
self.arrival_filter.update(delta_arrival);
self.overuse_detector.update(
self.arrival_filter.value(),
delta_timestamp as f64,
);
}
let incoming_rate = self.calculate_incoming_rate(packets);
self.current_estimate = self.rate_controller.update(
self.overuse_detector.hypothesis(),
incoming_rate,
);
self.state = self.rate_controller.state();
self.last_update = Some(Instant::now());
self.current_estimate
}
fn calculate_incoming_rate(&self, packets: &[PacketFeedback]) -> u32 {
if packets.len() < 2 {
return self.current_estimate;
}
let time_span = packets.last().unwrap().arrival_time_ms - packets[0].arrival_time_ms;
if time_span <= 0 {
return self.current_estimate;
}
let total_size: u32 = packets.iter().map(|p| p.size_bytes).sum();
let rate_bps = (total_size * 8 * 1000) / time_span as u32;
rate_bps
}
pub fn current_estimate(&self) -> u32 {
self.current_estimate
}
pub fn state(&self) -> GccState {
self.state
}
pub fn overuse_estimate(&self) -> f64 {
self.overuse_detector.estimate()
}
}
#[derive(Debug, Clone)]
pub struct PacketFeedback {
pub sequence_number: u16,
pub send_time_ms: i64,
pub arrival_time_ms: i64,
pub size_bytes: u32,
}
pub struct SimpleBandwidthEstimator {
throughput_samples: VecDeque<ThroughputSample>,
current_estimate: u32,
smoothing_factor: f64,
min_estimate: u32,
max_estimate: u32,
}
#[derive(Debug, Clone)]
struct ThroughputSample {
timestamp: Instant,
bytes_per_second: u32,
rtt_ms: u32,
loss_rate: f32,
}
impl SimpleBandwidthEstimator {
pub fn new(initial_estimate: u32) -> Self {
Self {
throughput_samples: VecDeque::new(),
current_estimate: initial_estimate,
smoothing_factor: 0.1,
min_estimate: 64_000, max_estimate: 100_000_000, }
}
pub fn update(&mut self, bytes_received: u32, time_window_ms: u32, rtt_ms: u32, loss_rate: f32) {
if time_window_ms == 0 {
return;
}
let bytes_per_second = (bytes_received * 1000) / time_window_ms;
let congestion_factor = self.calculate_congestion_factor(rtt_ms, loss_rate);
let adjusted_throughput = (bytes_per_second as f64 * congestion_factor) as u32;
self.current_estimate = (
self.current_estimate as f64 * (1.0 - self.smoothing_factor) +
adjusted_throughput as f64 * self.smoothing_factor
) as u32;
self.current_estimate = self.current_estimate.clamp(self.min_estimate, self.max_estimate);
self.throughput_samples.push_back(ThroughputSample {
timestamp: Instant::now(),
bytes_per_second: adjusted_throughput,
rtt_ms,
loss_rate,
});
let cutoff = Instant::now() - Duration::from_secs(30);
while let Some(sample) = self.throughput_samples.front() {
if sample.timestamp < cutoff {
self.throughput_samples.pop_front();
} else {
break;
}
}
}
fn calculate_congestion_factor(&self, rtt_ms: u32, loss_rate: f32) -> f64 {
let mut factor = 1.0;
if rtt_ms > 100 {
factor *= 1.0 - ((rtt_ms - 100) as f64 / 1000.0).min(0.5);
}
if loss_rate > 0.01 { factor *= 1.0 - (loss_rate as f64 * 5.0).min(0.8);
}
factor.max(0.1) }
pub fn current_estimate(&self) -> u32 {
self.current_estimate
}
pub fn confidence(&self) -> f32 {
if self.throughput_samples.len() < 3 {
return 0.3; }
let recent_samples: Vec<u32> = self.throughput_samples
.iter()
.rev()
.take(10)
.map(|s| s.bytes_per_second)
.collect();
if recent_samples.len() < 2 {
return 0.5;
}
let mean = recent_samples.iter().sum::<u32>() as f64 / recent_samples.len() as f64;
let variance = recent_samples
.iter()
.map(|&x| {
let diff = x as f64 - mean;
diff * diff
})
.sum::<f64>() / recent_samples.len() as f64;
let coefficient_of_variation = variance.sqrt() / mean;
(1.0 - coefficient_of_variation.min(1.0)).max(0.1) as f32
}
}
pub struct QualityAssessment {
loss_weight: f32,
jitter_weight: f32,
rtt_weight: f32,
bandwidth_weight: f32,
}
impl Default for QualityAssessment {
fn default() -> Self {
Self {
loss_weight: 0.4,
jitter_weight: 0.25,
rtt_weight: 0.2,
bandwidth_weight: 0.15,
}
}
}
impl QualityAssessment {
pub fn new(loss_weight: f32, jitter_weight: f32, rtt_weight: f32, bandwidth_weight: f32) -> Self {
let total = loss_weight + jitter_weight + rtt_weight + bandwidth_weight;
Self {
loss_weight: loss_weight / total,
jitter_weight: jitter_weight / total,
rtt_weight: rtt_weight / total,
bandwidth_weight: bandwidth_weight / total,
}
}
pub fn calculate_quality(&self, metrics: &QualityMetrics) -> f32 {
let loss_score = (1.0 - (metrics.loss_rate * 20.0)).clamp(0.0, 1.0); let jitter_score = (1.0 - (metrics.jitter_ms / 100.0)).clamp(0.0, 1.0); let rtt_score = (1.0 - (metrics.rtt_ms / 500.0)).clamp(0.0, 1.0); let bandwidth_score = metrics.bandwidth_utilization.clamp(0.0, 1.0);
loss_score * self.loss_weight +
jitter_score * self.jitter_weight +
rtt_score * self.rtt_weight +
bandwidth_score * self.bandwidth_weight
}
pub fn quality_to_mos(&self, quality_score: f32) -> f32 {
1.0 + quality_score * 4.0
}
pub fn requires_feedback(&self, quality_score: f32, threshold: f32) -> bool {
quality_score < threshold
}
}
#[derive(Debug, Clone)]
pub struct QualityMetrics {
pub loss_rate: f32,
pub jitter_ms: f32,
pub rtt_ms: f32,
pub bandwidth_utilization: f32,
}