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..c9bec04 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)] @@ -131,54 +131,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(); } + + 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 { 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 +165,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 +214,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 +283,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 +297,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 +305,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 +313,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 59f57e1..2aa19b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,12 +21,16 @@ 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, name: String, - fan_min: Option, - fan_max: Option, pwm_min: Option, pwm_max: Option, } @@ -36,8 +40,6 @@ impl HwMon { Self { card: card.clone(), name: String::from(name), - fan_min: None, - fan_max: None, pwm_min: None, pwm_max: None, } @@ -55,37 +57,6 @@ 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") - .unwrap_or_default() - .parse() - .unwrap_or(0), - ) - }; - self.fan_min.unwrap_or_default() - } - - pub fn fan_max(&mut self) -> u32 { - if self.fan_max.is_none() { - self.fan_max = Some( - self.read("fan1_max") - .unwrap_or_default() - .parse() - .unwrap_or(1500), - ) - }; - self.fan_max.unwrap_or(1500) - } - - 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"); - std::io::Error::from(ErrorKind::InvalidInput) - }) - } - pub fn pwm_min(&mut self) -> u32 { if self.pwm_min.is_none() { self.pwm_min = Some( @@ -110,6 +81,13 @@ impl HwMon { self.pwm_max.unwrap_or(255) } + 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) + }) + } + pub fn pwm_speed(&self) -> std::io::Result { self.read("pwm1")?.parse().map_err(|_e| { log::warn!("Read from gpu monitor failed. Invalid fan speed"); @@ -132,13 +110,7 @@ impl HwMon { pub fn is_amd(&self) -> bool { std::fs::read_to_string(format!("{}/{}/device/vendor", ROOT_DIR, self.card)) .map_err(|_| AmdFanError::FailedReadVendor) - .map(|vendor| { - if vendor.trim() == "0x1002" { - true - } else { - false - } - }) + .map(|vendor| vendor.trim() == "0x1002") .unwrap_or_default() } @@ -150,11 +122,18 @@ impl HwMon { self.write("pwm1_enable", 2) } - pub fn set_speed(&self, pwm: 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", pwm) + self.write("pwm1", value as u64) + } + + pub fn set_speed(&mut self, speed: f64) -> std::io::Result<()> { + 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) } fn read(&self, name: &str) -> std::io::Result { @@ -329,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 as f64) { log::error!("Failed to change speed to {}. {:?}", target_pwm, e); } controller.last_temp = gpu_temp; @@ -384,9 +363,9 @@ 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.fan_min(), - card.hw_mon.fan_max(), + 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(), (card.hw_mon.pwm_speed().unwrap_or_default() as f32 / 2.55).round(), );