From 90e212a158d275a317b68f2ffa54e134832f1497 Mon Sep 17 00:00:00 2001 From: Stefan Gehr Date: Mon, 19 Jul 2021 22:25:34 +0200 Subject: [PATCH 1/5] 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)); From 6b17d3815f009e2e55802624030dec8c9347c524 Mon Sep 17 00:00:00 2001 From: Stefan Gehr Date: Tue, 20 Jul 2021 09:53:22 +0200 Subject: [PATCH 2/5] Use pwm_min, pwm_max for converting speed to pwm Don't hardcode 0 and 255 anymore. Instead use pwm_min and pwm_max as boundaries when converting from a speed (0 to 100) to a pwm value. 0 gets mapped to pwm_min, 100 gets mapped to pwm_max everything inbetween gets linearly interpoltated. --- src/config.rs | 8 +------- src/main.rs | 14 +++++++++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/config.rs b/src/config.rs index e3837d9..c9bec04 100644 --- a/src/config.rs +++ b/src/config.rs @@ -119,12 +119,6 @@ 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, @@ -147,7 +141,7 @@ impl Config { 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) + crate::linear_map(temp, self.speed_matrix[idx].temp, self.speed_matrix[idx+1].temp, self.speed_matrix[idx].speed, self.speed_matrix[idx+1].speed) } pub fn log_level(&self) -> LogLevel { diff --git a/src/main.rs b/src/main.rs index bd5f630..892370c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ mod config; extern crate log; use std::io::ErrorKind; -use std::cmp::{min, max}; use crate::config::{load_config, Card, Config}; use gumdrop::Options; @@ -22,6 +21,12 @@ pub enum AmdFanError { FailedReadVendor, } +// linear mapping from the xrange to the yrange +fn linear_map(x: f64, x1: f64, x2: f64, y1: f64, y2: f64) -> f64 { + let m = (y2 - y1) / (x2 - x1); + m * (x - x1) + y1 +} + #[derive(Debug)] pub struct HwMon { card: Card, @@ -124,10 +129,9 @@ impl HwMon { } 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()); + let min = self.pwm_min() as f64; + let max = self.pwm_max() as f64; + let pwm = linear_map(speed, 0f64, 100f64, min, max).round() as u32; self.set_pwm(pwm) } From c54bdc0af0d1bfc505ecd5940206b339d14d8b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Thu, 29 Jul 2021 10:21:26 +0200 Subject: [PATCH 3/5] Update main.rs --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5c36838..19e6087 100644 --- a/src/main.rs +++ b/src/main.rs @@ -308,7 +308,7 @@ fn service(config: Config) -> std::io::Result<()> { continue; } - if let Err(e) = controller.hw_mon.set_speed(target_pwm as u64) { + if let Err(e) = controller.hw_mon.set_speed(target_pwm) { log::error!("Failed to change speed to {}. {:?}", target_pwm, e); } controller.last_temp = gpu_temp; @@ -363,7 +363,7 @@ fn monitor_cards(config: Config) -> std::io::Result<()> { println!( " | {:>5.2} | {:>4} | {:>4} | {:>4} | {:>4} | {:>3}", card.hw_mon.gpu_temp().unwrap_or_default(), - card.hw_mon.fan_speed().unwrap_or_default(), + card.hw_mon.pwm_speed().unwrap_or_default(), card.hw_mon.pwm_min(), card.hw_mon.pwm_max(), card.hw_mon.pwm_speed().unwrap_or_default(), From e9985d50b5eb1e43822ee33e7597e52da54def57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Thu, 29 Jul 2021 10:28:13 +0200 Subject: [PATCH 4/5] Update main.rs --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 19e6087..1d1ef0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -308,7 +308,7 @@ fn service(config: Config) -> std::io::Result<()> { continue; } - if let Err(e) = controller.hw_mon.set_speed(target_pwm) { + if let Err(e) = controller.hw_mon.set_speed(target_pwm as f32) { log::error!("Failed to change speed to {}. {:?}", target_pwm, e); } controller.last_temp = gpu_temp; From 20214db390fc1e75a2d0cccd9a13c5fce8b3bbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Thu, 29 Jul 2021 10:32:21 +0200 Subject: [PATCH 5/5] Update main.rs --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 1d1ef0b..2aa19b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -308,7 +308,7 @@ fn service(config: Config) -> std::io::Result<()> { continue; } - if let Err(e) = controller.hw_mon.set_speed(target_pwm as f32) { + if let Err(e) = controller.hw_mon.set_speed(target_pwm as f64) { log::error!("Failed to change speed to {}. {:?}", target_pwm, e); } controller.last_temp = gpu_temp;