#[derive(Debug, Clone, Copy)]
pub struct ThermalSample {
pub temperature_c: f64,
pub timestamp_sec: f64,
pub latency_us: Option<f64>,
}
impl ThermalSample {
pub fn new(temperature_c: f64, timestamp_sec: f64) -> Self {
Self {
temperature_c,
timestamp_sec,
latency_us: None,
}
}
pub fn with_latency(temperature_c: f64, timestamp_sec: f64, latency_us: f64) -> Self {
Self {
temperature_c,
timestamp_sec,
latency_us: Some(latency_us),
}
}
}
#[derive(Debug, Clone)]
pub struct ThermalPrediction {
pub predicted_temp_c: f64,
pub horizon_sec: f64,
pub trend_slope: f64,
pub confidence: f64,
pub sample_count: usize,
}
impl ThermalPrediction {
pub fn will_throttle(&self, threshold_c: f64) -> bool {
self.predicted_temp_c >= threshold_c
}
pub fn time_to_throttle(&self, current_temp: f64, threshold_c: f64) -> Option<f64> {
if self.trend_slope <= 0.0 {
return None; }
let delta = threshold_c - current_temp;
if delta <= 0.0 {
return Some(0.0); }
Some(delta / self.trend_slope)
}
}
#[derive(Debug, Clone)]
pub struct ThrottleRisk {
pub probability: f64,
pub current_temp_c: f64,
pub threshold_c: f64,
pub margin_c: f64,
pub category: RiskCategory,
}
impl ThrottleRisk {
pub fn assess(current_temp: f64, threshold: f64, trend_slope: f64, horizon_sec: f64) -> Self {
let margin = threshold - current_temp;
let _predicted_temp = current_temp + trend_slope * horizon_sec;
let proximity_risk = if margin <= 0.0 {
1.0
} else {
1.0 - (margin / threshold).clamp(0.0, 1.0)
};
let trend_risk = if trend_slope > 0.0 {
let time_to_threshold = margin / trend_slope;
if time_to_threshold <= horizon_sec {
1.0
} else {
(horizon_sec / time_to_threshold).clamp(0.0, 1.0)
}
} else {
0.0 };
let probability = (0.4 * proximity_risk + 0.6 * trend_risk).clamp(0.0, 1.0);
let category = RiskCategory::from_probability(probability);
Self {
probability,
current_temp_c: current_temp,
threshold_c: threshold,
margin_c: margin,
category,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RiskCategory {
Low,
Moderate,
High,
Critical,
}
impl RiskCategory {
pub fn from_probability(prob: f64) -> Self {
if prob < 0.25 {
Self::Low
} else if prob < 0.50 {
Self::Moderate
} else if prob < 0.75 {
Self::High
} else {
Self::Critical
}
}
pub fn name(&self) -> &'static str {
match self {
Self::Low => "low",
Self::Moderate => "moderate",
Self::High => "high",
Self::Critical => "critical",
}
}
}
#[derive(Debug, Clone)]
pub struct CooldownRecommendation {
pub duration_sec: f64,
pub target_temp_c: f64,
pub current_temp_c: f64,
pub cooling_rate: f64,
}
impl CooldownRecommendation {
pub fn calculate(current_temp: f64, target_temp: f64, cooling_rate: f64) -> Self {
let temp_delta = current_temp - target_temp;
let duration = if temp_delta > 0.0 && cooling_rate > 0.0 {
temp_delta / cooling_rate
} else {
0.0
};
Self {
duration_sec: duration.max(0.0),
target_temp_c: target_temp,
current_temp_c: current_temp,
cooling_rate,
}
}
pub fn is_needed(&self) -> bool {
self.duration_sec > 0.0
}
}
#[derive(Debug, Clone)]
pub struct ThermalCorrelation {
pub pearson_r: f64,
pub sample_count: usize,
pub is_significant: bool,
pub latency_per_degree: f64,
}
impl ThermalCorrelation {
pub fn has_thermal_impact(&self) -> bool {
self.pearson_r > 0.3 && self.is_significant
}
}
#[derive(Debug, Clone)]
pub struct ThermalVariance {
pub contribution_percent: f64,
pub temp_range_c: f64,
pub avg_temp_c: f64,
}