feat(v0.4): чистый ThermalPolicy (банды + гистерезис, A12/B10)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
This commit is contained in:
@@ -3,5 +3,6 @@
|
||||
|
||||
pub mod fsm;
|
||||
pub mod service;
|
||||
pub mod thermal;
|
||||
|
||||
pub use service::PowerService;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//! Тепловая подсистема (A12/B10, спека v0.4 §5). `ThermalPolicy` — чистая (без I/O), с гистерезисом.
|
||||
//! Источники/throttler/монитор — P8.5 (этот же файл).
|
||||
|
||||
/// Уровень теплового состояния. `Throttle(u8)` — банд (v0.4 использует уровень 1; мульти-банд — RK3588).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ThermalLevel {
|
||||
Normal,
|
||||
Warn,
|
||||
Throttle(u8),
|
||||
Critical,
|
||||
}
|
||||
|
||||
impl ThermalLevel {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
ThermalLevel::Normal => "normal",
|
||||
ThermalLevel::Warn => "warn",
|
||||
ThermalLevel::Throttle(_) => "throttle",
|
||||
ThermalLevel::Critical => "critical",
|
||||
}
|
||||
}
|
||||
fn rank(&self) -> u8 {
|
||||
match self {
|
||||
ThermalLevel::Normal => 0,
|
||||
ThermalLevel::Warn => 1,
|
||||
ThermalLevel::Throttle(_) => 2,
|
||||
ThermalLevel::Critical => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Пороги — placeholder-константы (°C). Тюнинг на RK3588 (hardware §1a; Tjmax ~100 °C).
|
||||
pub const WARN_C: i32 = 75;
|
||||
pub const THROTTLE_C: i32 = 85;
|
||||
pub const CRITICAL_C: i32 = 95;
|
||||
pub const HYST_C: i32 = 5;
|
||||
|
||||
/// Чистая политика: `(предыдущий уровень, температура) → уровень` с гистерезисом (Schmitt по бандам).
|
||||
pub struct ThermalPolicy;
|
||||
|
||||
impl ThermalPolicy {
|
||||
fn band_by_entry(t: i32) -> ThermalLevel {
|
||||
if t >= CRITICAL_C {
|
||||
ThermalLevel::Critical
|
||||
} else if t >= THROTTLE_C {
|
||||
ThermalLevel::Throttle(1)
|
||||
} else if t >= WARN_C {
|
||||
ThermalLevel::Warn
|
||||
} else {
|
||||
ThermalLevel::Normal
|
||||
}
|
||||
}
|
||||
fn band_by_exit(t: i32) -> ThermalLevel {
|
||||
// нижние (гистерезисные) пороги = entry − HYST
|
||||
if t >= CRITICAL_C - HYST_C {
|
||||
ThermalLevel::Critical
|
||||
} else if t >= THROTTLE_C - HYST_C {
|
||||
ThermalLevel::Throttle(1)
|
||||
} else if t >= WARN_C - HYST_C {
|
||||
ThermalLevel::Warn
|
||||
} else {
|
||||
ThermalLevel::Normal
|
||||
}
|
||||
}
|
||||
|
||||
/// Подъём — по entry-порогам; спуск — по exit-порогам (entry − HYST) → нет осцилляции на границе.
|
||||
pub fn next(prev: ThermalLevel, temp_c: i32) -> ThermalLevel {
|
||||
let up = Self::band_by_entry(temp_c);
|
||||
if up.rank() >= prev.rank() {
|
||||
up
|
||||
} else {
|
||||
Self::band_by_exit(temp_c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod policy_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn rises_by_entry_thresholds() {
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Normal, 70), ThermalLevel::Normal);
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Normal, 75), ThermalLevel::Warn);
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Warn, 85), ThermalLevel::Throttle(1));
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Throttle(1), 95), ThermalLevel::Critical);
|
||||
// прыжок вверх через банды
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Normal, 99), ThermalLevel::Critical);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hysteresis_holds_until_below_exit() {
|
||||
// critical держится до < 90 (95−5)
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Critical, 92), ThermalLevel::Critical);
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Critical, 89), ThermalLevel::Throttle(1));
|
||||
// warn держится до < 70
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Warn, 73), ThermalLevel::Warn);
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Warn, 69), ThermalLevel::Normal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_oscillation_at_boundary() {
|
||||
// на 84 (чуть ниже entry throttle=85): зависит от prev (Schmitt), не дёргается
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Throttle(1), 84), ThermalLevel::Throttle(1));
|
||||
assert_eq!(ThermalPolicy::next(ThermalLevel::Warn, 84), ThermalLevel::Warn);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user