From 90e212a158d275a317b68f2ffa54e134832f1497 Mon Sep 17 00:00:00 2001 From: Stefan Gehr Date: Mon, 19 Jul 2021 22:25:34 +0200 Subject: [PATCH] Fanspeeds as floats, 0 is lowest speed, bugfixes 1. The fanspeeds can now be specified as floats for better granularity 2. Removed 4 as the lowest possible speed. Lowest possible value is now 0.0 3. min and max values are now correctly read for pwm1 and not the rpm values of fan1 anymore 4. Percentage speed now gets correctly converted to the pwm values (usually between 0 and 255) 5. Before the update of the pwm-value was skipped when the current speed was below minimum or above maximum. This got removed. Why was that there in the first place? 6. Simpler algorithm for speed_for_temp 7. in monitor the min value is now actually below MIN and the max value below MAX. Before it was swapped 8. Fixed grammar error "can's" instead of "can't" --- Cargo.lock | 2 - README.md | 16 ++++---- src/config.rs | 102 ++++++++++++++++++-------------------------------- src/main.rs | 62 ++++++++++++++++-------------- 4 files changed, 79 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ac1833..1a25ced 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "aho-corasick" version = "0.7.18" diff --git a/README.md b/README.md index 8f318b9..7ca61c1 100644 --- a/README.md +++ b/README.md @@ -35,35 +35,35 @@ cards = ["card0"] [[speed_matrix]] temp = 4.0 -speed = 4 +speed = 4.0 [[speed_matrix]] temp = 30.0 -speed = 33 +speed = 33.0 [[speed_matrix]] temp = 45.0 -speed = 50 +speed = 50.0 [[speed_matrix]] temp = 60.0 -speed = 66 +speed = 66.0 [[speed_matrix]] temp = 65.0 -speed = 69 +speed = 69.0 [[speed_matrix]] temp = 70.0 -speed = 75 +speed = 75.0 [[speed_matrix]] temp = 75.0 -speed = 89 +speed = 89.0 [[speed_matrix]] temp = 80.0 -speed = 100 +speed = 100.0 ``` ## :bookmark: License diff --git a/src/config.rs b/src/config.rs index 47dd288..e3837d9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -87,7 +87,7 @@ impl Serialize for Card { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct MatrixPoint { pub temp: f64, - pub speed: u32, + pub speed: f64, } #[derive(Serialize, Deserialize, Debug, Copy, Clone)] @@ -119,6 +119,12 @@ impl From for LevelFilter { } } +// linear mapping between two points +fn linear_map(x: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> f64 { + let m = (y2 - y1) / (x2 - x1); + m * (x - x1) + y1 +} + #[derive(Serialize, Deserialize, Debug)] pub struct Config { log_level: LogLevel, @@ -131,54 +137,29 @@ impl Config { &self.cards } - pub fn speed_for_temp(&self, temp: f64) -> u32 { + pub fn speed_for_temp(&self, temp: f64) -> f64 { let idx = match self.speed_matrix.iter().rposition(|p| p.temp <= temp) { Some(idx) => idx, - _ => return 4, + _ => return self.min_speed(), }; - match (idx, self.speed_matrix.len() - 1) { - (0, _) => self.min_speed(), - (current, max) if current == max => self.max_speed(), - _ => { - if self.is_exact_point(idx, temp) { - return self.speed_matrix.get(idx).map(|p| p.speed).unwrap_or(4); - } - let max = match self.speed_matrix.get(idx + 1) { - Some(p) => p, - _ => return 4, - }; - let min = match self.speed_matrix.get(idx) { - Some(p) => p, - _ => return 4, - }; - let speed_diff = max.speed as f64 - min.speed as f64; - let temp_diff = max.temp as f64 - min.temp as f64; - let increase_by = - (((temp as f64 - min.temp as f64) / temp_diff) * speed_diff).round(); - min.speed + increase_by as u32 - } + if idx == self.speed_matrix.len() - 1 { + return self.max_speed(); } + + linear_map(temp, self.speed_matrix[idx].temp, self.speed_matrix[idx].speed, self.speed_matrix[idx+1].temp, self.speed_matrix[idx+1].speed) } pub fn log_level(&self) -> LogLevel { self.log_level } - fn min_speed(&self) -> u32 { - self.speed_matrix.first().map(|p| p.speed).unwrap_or(4) + fn min_speed(&self) -> f64 { + self.speed_matrix.first().map(|p| p.speed).unwrap_or(0f64) } - fn max_speed(&self) -> u32 { - self.speed_matrix.last().map(|p| p.speed).unwrap_or(100) - } - - fn is_exact_point(&self, idx: usize, temp: f64) -> bool { - static DELTA: f64 = 0.001f64; - self.speed_matrix - .get(idx) - .map(|p| p.temp - DELTA < temp && p.temp + DELTA > temp) - .unwrap_or(false) + fn max_speed(&self) -> f64 { + self.speed_matrix.last().map(|p| p.speed).unwrap_or(100f64) } } @@ -190,35 +171,35 @@ impl Default for Config { speed_matrix: vec![ MatrixPoint { temp: 4f64, - speed: 4, + speed: 4f64, }, MatrixPoint { temp: 30f64, - speed: 33, + speed: 33f64, }, MatrixPoint { temp: 45f64, - speed: 50, + speed: 50f64, }, MatrixPoint { temp: 60f64, - speed: 66, + speed: 66f64, }, MatrixPoint { temp: 65f64, - speed: 69, + speed: 69f64, }, MatrixPoint { temp: 70f64, - speed: 75, + speed: 75f64, }, MatrixPoint { temp: 75f64, - speed: 89, + speed: 89f64, }, MatrixPoint { temp: 80f64, - speed: 100, + speed: 100f64, }, ], } @@ -239,24 +220,15 @@ pub fn load_config() -> std::io::Result { } }; - if config.speed_matrix.iter().fold( - 1000, - |n, point| if point.speed < n { point.speed } else { n }, - ) < 4 - { - log::error!("Due to driver bug lowest fan speed must be greater or equal 4"); - return Err(std::io::Error::from(ErrorKind::InvalidData)); - } - let mut last_point: Option<&MatrixPoint> = None; for matrix_point in config.speed_matrix.iter() { - if matrix_point.speed <= 0 { - log::error!("Fan speed can's be below 0 found {}", matrix_point.speed); + if matrix_point.speed < 0f64 { + log::error!("Fan speed can't be below 0.0 found {}", matrix_point.speed); return Err(std::io::Error::from(ErrorKind::InvalidData)); } - if matrix_point.speed > 100 { - log::error!("Fan speed can's be above 100 found {}", matrix_point.speed); + if matrix_point.speed > 100f64 { + log::error!("Fan speed can't be above 100.0 found {}", matrix_point.speed); return Err(std::io::Error::from(ErrorKind::InvalidData)); } if let Some(last_point) = last_point { @@ -317,13 +289,13 @@ mod speed_for_temp { #[test] fn below_minimal() { let config = Config::default(); - assert_eq!(config.speed_for_temp(1f64), 4); + assert_eq!(config.speed_for_temp(1f64), 4f64); } #[test] fn minimal() { let config = Config::default(); - assert_eq!(config.speed_for_temp(4f64), 4); + assert_eq!(config.speed_for_temp(4f64), 4f64); } #[test] @@ -331,7 +303,7 @@ mod speed_for_temp { let config = Config::default(); // 45 -> 50 // 60 -> 66 - assert_eq!(config.speed_for_temp(46f64), 51); + assert_eq!(config.speed_for_temp(46f64).round(), 51f64); } #[test] @@ -339,7 +311,7 @@ mod speed_for_temp { let config = Config::default(); // 45 -> 50 // 60 -> 66 - assert_eq!(config.speed_for_temp(58f64), 64); + assert_eq!(config.speed_for_temp(58f64).round(), 64f64); } #[test] @@ -347,24 +319,24 @@ mod speed_for_temp { let config = Config::default(); // 45 -> 50 // 60 -> 66 - assert_eq!(config.speed_for_temp(59f64), 65); + assert_eq!(config.speed_for_temp(59f64).round(), 65f64); } #[test] fn average() { let config = Config::default(); - assert_eq!(config.speed_for_temp(60f64), 66); + assert_eq!(config.speed_for_temp(60f64), 66f64); } #[test] fn max() { let config = Config::default(); - assert_eq!(config.speed_for_temp(80f64), 100); + assert_eq!(config.speed_for_temp(80f64), 100f64); } #[test] fn above_max() { let config = Config::default(); - assert_eq!(config.speed_for_temp(160f64), 100); + assert_eq!(config.speed_for_temp(160f64), 100f64); } } diff --git a/src/main.rs b/src/main.rs index d470777..bd5f630 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod config; extern crate log; use std::io::ErrorKind; +use std::cmp::{min, max}; use crate::config::{load_config, Card, Config}; use gumdrop::Options; @@ -25,8 +26,8 @@ pub enum AmdFanError { pub struct HwMon { card: Card, name: String, - fan_min: Option, - fan_max: Option, + pwm_min: Option, + pwm_max: Option, } impl HwMon { @@ -34,8 +35,8 @@ impl HwMon { Self { card: card.clone(), name: String::from(name), - fan_min: None, - fan_max: None, + pwm_min: None, + pwm_max: None, } } @@ -51,33 +52,33 @@ impl HwMon { self.read("name") } - pub fn fan_min(&mut self) -> u32 { - if self.fan_min.is_none() { - self.fan_min = Some( - self.read("fan1_min") + pub fn pwm_min(&mut self) -> u32 { + if self.pwm_min.is_none() { + self.pwm_min = Some( + self.read("pwm1_min") .unwrap_or_default() .parse() .unwrap_or(0), ) }; - self.fan_min.unwrap_or(0) + self.pwm_min.unwrap_or(0) } - pub fn fan_max(&mut self) -> u32 { - if self.fan_max.is_none() { - self.fan_max = Some( - self.read("fan1_max") + pub fn pwm_max(&mut self) -> u32 { + if self.pwm_max.is_none() { + self.pwm_max = Some( + self.read("pwm1_max") .unwrap_or_default() .parse() .unwrap_or(255), ) }; - self.fan_max.unwrap_or(255) + self.pwm_max.unwrap_or(255) } - pub fn fan_speed(&self) -> std::io::Result { - self.read("fan1_input")?.parse().map_err(|_e| { - log::warn!("Read from gpu monitor failed. Invalid fan speed"); + pub fn pwm(&self) -> std::io::Result { + self.read("pwm1")?.parse().map_err(|_e| { + log::warn!("Read from gpu monitor failed. Invalid pwm value"); std::io::Error::from(ErrorKind::InvalidInput) }) } @@ -115,11 +116,19 @@ impl HwMon { self.write("pwm1_enable", 2) } - pub fn set_speed(&self, speed: u64) -> std::io::Result<()> { + pub fn set_pwm(&self, value: u32) -> std::io::Result<()> { if self.is_fan_automatic() { self.set_manual()?; } - self.write("pwm1", speed) + self.write("pwm1", value as u64) + } + + pub fn set_speed(&mut self, speed: f64) -> std::io::Result<()> { + let mut pwm = (speed / 100f64 * 255f64).round() as u32; + // stay in the range + pwm = max(pwm, self.pwm_min()); + pwm = min(pwm, self.pwm_max()); + self.set_pwm(pwm) } fn read(&self, name: &str) -> std::io::Result { @@ -289,11 +298,8 @@ fn service(config: Config) -> std::io::Result<()> { let gpu_temp = controller.hw_mon.gpu_temp().unwrap_or_default(); let speed = config.speed_for_temp(gpu_temp); - if controller.hw_mon.fan_min() > speed || controller.hw_mon.fan_max() < speed { - continue; - } - if let Err(e) = controller.hw_mon.set_speed(speed as u64) { + if let Err(e) = controller.hw_mon.set_speed(speed) { log::error!("Failed to change speed to {}. {:?}", speed, e); } controller.last_temp = gpu_temp; @@ -342,15 +348,15 @@ fn monitor_cards(config: Config) -> std::io::Result<()> { print!("{esc}[2J{esc}[1;1H", esc = 27 as char); for card in controllers.iter_mut() { println!( - "Card {:3} | Temp | fan speed | MAX | MIN ", + "Card {:3} | Temp | current PWM | MIN | MAX ", card.hw_mon.card.0 ); println!( - " | {:>5.2} | {:>9} | {:>4} | {:>4}", + " | {:>5.2} | {:>11} | {:>4} | {:>4}", card.hw_mon.gpu_temp().unwrap_or_default(), - card.hw_mon.fan_speed().unwrap_or_default(), - card.hw_mon.fan_min(), - card.hw_mon.fan_max(), + card.hw_mon.pwm().unwrap_or_default(), + card.hw_mon.pwm_min(), + card.hw_mon.pwm_max(), ); } std::thread::sleep(std::time::Duration::from_secs(4));