From 1d1de835a50c65a9fbb9fbc98d82795ccf9501b8 Mon Sep 17 00:00:00 2001 From: Markus Ressel Date: Wed, 3 Mar 2021 21:11:53 +0100 Subject: [PATCH 1/4] use simple float and calculate moving average incrementally --- internal/backend.go | 72 +++++++++++++++++++++------------------------ internal/config.go | 4 +-- internal/data.go | 12 ++++---- 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/internal/backend.go b/internal/backend.go index 7caa833..15d5503 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -9,7 +9,6 @@ import ( "github.com/oklog/run" bolt "go.etcd.io/bbolt" "log" - "math" "os" "os/exec" "os/signal" @@ -158,7 +157,7 @@ func sensorMonitor(ctx context.Context, sensor *Sensor, tick <-chan time.Time) e case <-ctx.Done(): return nil case <-tick: - err := updateSensor(*sensor) + err := updateSensor(sensor) if err != nil { log.Fatal(err) } @@ -206,9 +205,7 @@ func mapConfigToControllers(controllers []*Controller) { if err != nil { log.Fatalf("Error reading sensor %s: %s", sensorConfig.Id, err.Error()) } - for i := 0; i < CurrentConfig.TempRollingWindowSize; i++ { - sensor.Values.Append(float64(currentValue)) - } + sensor.MovingAvg = float64(currentValue) } } } @@ -223,7 +220,7 @@ func measureRpm(fan *Fan) { log.Printf("Measured RPM of %d at PWM %d for fan %s", rpm, pwm, fan.Config.Id) } - fan.RpmInputWindow.Append(float64(rpm)) + fan.RpmMovingAvg = updateSimpleMovingAvg(fan.RpmMovingAvg, CurrentConfig.RpmRollingWindowSize, float64(rpm)) pwmRpmMap := fan.FanCurveData pointWindow, ok := (*pwmRpmMap)[pwm] @@ -276,19 +273,19 @@ func updatePwmBoundaries(fan *Fan) { } // read the current value of a sensor and append it to the moving window -func updateSensor(sensor Sensor) (err error) { +func updateSensor(sensor *Sensor) (err error) { value, err := util.ReadIntFromFile(sensor.Input) if err != nil { return err } - values := sensor.Values - values.Append(float64(value)) - if value > sensor.Config.Max { + var n = CurrentConfig.TempRollingWindowSize + sensor.MovingAvg = updateSimpleMovingAvg(sensor.MovingAvg, n, float64(value)) + if value > int(sensor.Config.Max) { // if the value is higher than the specified max temperature, // insert the value twice into the moving window, // to give it a bigger impact - values.Append(float64(value)) + sensor.MovingAvg = updateSimpleMovingAvg(sensor.MovingAvg, n, float64(value)) } return nil @@ -414,10 +411,9 @@ func calculateTargetSpeed(fan *Fan) int { minTemp := sensor.Config.Min * 1000 // degree to milli-degree maxTemp := sensor.Config.Max * 1000 - var avgTemp int - avgTemp = int(sensor.Values.Reduce(rolling.Avg)) + var avgTemp = sensor.MovingAvg - //log.Printf("Avg temp of %s: %d", sensor.name, avgTemp) + log.Printf("Avg temp of %s: %f", sensor.Config.Id, avgTemp) if avgTemp >= maxTemp { // full throttle if max temp is reached @@ -427,7 +423,7 @@ func calculateTargetSpeed(fan *Fan) int { return 0 } - ratio := (float64(avgTemp) - float64(minTemp)) / (float64(maxTemp) - float64(minTemp)) + ratio := (avgTemp - minTemp) / (maxTemp - minTemp) return int(ratio * 255) } @@ -485,20 +481,16 @@ func createFans(devicePath string) []*Fan { log.Fatal(err) } - rpmWindowSize := int(math.Ceil(float64(CurrentConfig.IncreaseStartPwmAfter.Milliseconds()) / float64(CurrentConfig.RpmPollingRate.Milliseconds()))) - log.Printf("rpmWindowSize: %d", rpmWindowSize) - rpmWindow := rolling.NewWindow(rpmWindowSize) - fan := &Fan{ - Name: file, - Index: index, - PwmOutput: output, - RpmInput: inputs[idx], - RpmInputWindow: rolling.NewPointPolicy(rpmWindow), - StartPwm: MinPwmValue, - MaxPwm: MaxPwmValue, - FanCurveData: &map[int]*rolling.PointPolicy{}, - LastSetPwm: InitialLastSetPwm, + Name: file, + Index: index, + PwmOutput: output, + RpmInput: inputs[idx], + RpmMovingAvg: 0, + StartPwm: MinPwmValue, + MaxPwm: MaxPwmValue, + FanCurveData: &map[int]*rolling.PointPolicy{}, + LastSetPwm: InitialLastSetPwm, } // store original pwm_enabled value @@ -529,10 +521,9 @@ func createSensors(devicePath string) []*Sensor { } sensors = append(sensors, &Sensor{ - Name: file, - Index: index, - Input: input, - Values: rolling.NewPointPolicy(rolling.NewWindow(CurrentConfig.TempRollingWindowSize)), + Name: file, + Index: index, + Input: input, }) } @@ -630,7 +621,7 @@ func setPwm(fan *Fan, pwm int) (err error) { // make sure fans never stop by validating the current RPM // and adjusting the target PWM value upwards if necessary if fan.Config.NeverStop && fan.LastSetPwm == target { - avgRpm := fan.RpmInputWindow.Reduce(rolling.Avg) + avgRpm := fan.RpmMovingAvg if avgRpm <= 0 { if target >= maxPwm { log.Printf("CRITICAL: Fan avg. RPM is %f, even at PWM value %d", avgRpm, target) @@ -639,12 +630,10 @@ func setPwm(fan *Fan, pwm int) (err error) { log.Printf("WARNING: Increasing startPWM of %s from %d to %d, which is supposed to never stop, but RPM is %f", fan.Config.Id, fan.StartPwm, fan.StartPwm+1, avgRpm) fan.StartPwm++ target++ - // initialize the complete window with values > 0 to make sure - // the whole window is refilled before increasing the minPWM value again - measurementCount := int(fan.RpmInputWindow.Reduce(rolling.Count)) - for i := 0; i < measurementCount; i++ { - fan.RpmInputWindow.Append(1) - } + + // set the moving avg to a value > 0 to prevent + // this increase from happening too fast + fan.RpmMovingAvg = 1 } } @@ -667,3 +656,8 @@ func GetRpm(fan *Fan) int { } return value } + +// calculates the new moving average, based on an existing average and buffer size +func updateSimpleMovingAvg(oldAvg float64, n int, newValue float64) float64 { + return oldAvg + (1/float64(n))*(newValue-oldAvg) +} diff --git a/internal/config.go b/internal/config.go index a46ab21..4509369 100644 --- a/internal/config.go +++ b/internal/config.go @@ -20,8 +20,8 @@ type SensorConfig struct { Id string Platform string Index int - Min int - Max int + Min float64 + Max float64 } type FanConfig struct { diff --git a/internal/data.go b/internal/data.go index 683eb8b..047c9fa 100644 --- a/internal/data.go +++ b/internal/data.go @@ -18,7 +18,7 @@ type Fan struct { Name string `json:"name"` Index int `json:"index"` RpmInput string `json:"rpminput"` - RpmInputWindow *rolling.PointPolicy `json:"rpminputwindow"` + RpmMovingAvg float64 `json:"rpmmovingavg"` PwmOutput string `json:"pwmoutput"` Config *FanConfig `json:"config"` StartPwm int `json:"startpwm"` // lowest PWM value where the fans are still spinning @@ -29,9 +29,9 @@ type Fan struct { } type Sensor struct { - Name string `json:"name"` - Index int `json:"index"` - Input string `json:"string"` - Config *SensorConfig `json:"config"` - Values *rolling.PointPolicy `json:"values"` + Name string `json:"name"` + Index int `json:"index"` + Input string `json:"string"` + Config *SensorConfig `json:"config"` + MovingAvg float64 `json:"movingaverage"` } From 9287233495b4d95426e96435d2979873e0072b06 Mon Sep 17 00:00:00 2001 From: Markus Ressel Date: Wed, 3 Mar 2021 21:29:15 +0100 Subject: [PATCH 2/4] removed IncreaseStartPwmAfter config option decreased TempRollingWindowSize to 50 --- README.md | 4 +--- cmd/root.go | 9 +++------ fan2go.yaml | 4 +--- internal/config.go | 1 - 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fea2b38..ec209b0 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,9 @@ dbPath: "/etc/fan2go/fan2go.db" # The rate to poll temperature sensors at. tempSensorPollingRate: 200ms # The number of sensor items to keep in a rolling window array. -rollingWindowSize: 100 +rollingWindowSize: 50 # The rate to poll fan RPM input sensors at. rpmPollingRate: 1s -# Time to wait before increasing the (initially measured) startPWM of a fan -increaseStartPwmAfter: 10s # The rate to update fan speed targets at. controllerAdjustmentTickRate: 200ms diff --git a/cmd/root.go b/cmd/root.go index e44f602..77dbac7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -118,20 +118,17 @@ func readConfigFile() { } func validateConfig() { - config := &internal.CurrentConfig - if config.IncreaseStartPwmAfter <= config.RpmPollingRate { - log.Fatalf("increaseStartPwmAfter must be greater or equal to rpmPollingRate") - } + //config := &internal.CurrentConfig + // nothing yet } func setDefaultValues() { viper.SetDefault("dbpath", "/etc/fan2go/fan2go.db") viper.SetDefault("TempSensorPollingRate", 200*time.Millisecond) - viper.SetDefault("TempRollingWindowSize", 100) + viper.SetDefault("TempRollingWindowSize", 50) viper.SetDefault("RpmPollingRate", 1*time.Second) viper.SetDefault("RpmRollingWindowSize", 10) - viper.SetDefault("IncreaseStartPwmAfter", 10*time.Second) viper.SetDefault("ControllerAdjustmentTickRate", 200*time.Millisecond) viper.SetDefault("sensors", []internal.SensorConfig{}) diff --git a/fan2go.yaml b/fan2go.yaml index e91cb8e..4f438bc 100644 --- a/fan2go.yaml +++ b/fan2go.yaml @@ -3,11 +3,9 @@ dbPath: "/etc/fan2go/fan2go.db" # The rate to poll temperature sensors at. tempSensorPollingRate: 200ms # The number of sensor items to keep in a rolling window array. -rollingWindowSize: 100 +rollingWindowSize: 50 # The rate to poll fan RPM input sensors at. rpmPollingRate: 1s -# Time to wait before increasing the (initially measured) startPWM of a fan -increaseStartPwmAfter: 10s # The rate to update fan speed targets at. controllerAdjustmentTickRate: 200ms diff --git a/internal/config.go b/internal/config.go index 4509369..a4abe36 100644 --- a/internal/config.go +++ b/internal/config.go @@ -11,7 +11,6 @@ type Configuration struct { ControllerAdjustmentTickRate time.Duration TempRollingWindowSize int RpmRollingWindowSize int - IncreaseStartPwmAfter time.Duration Sensors []SensorConfig Fans []FanConfig } From afa961a00aa737a8e23fb1836ea83c6f3c4036d7 Mon Sep 17 00:00:00 2001 From: Markus Ressel Date: Wed, 3 Mar 2021 21:36:46 +0100 Subject: [PATCH 3/4] updated golang version --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9a859f5..dfb0ea9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/markusressel/fan2go -go 1.13 +go 1.16 require ( github.com/asecurityteam/rolling v2.0.4+incompatible From 992f3b3d632635fceafb46f00e2b6acccb1ec9c5 Mon Sep 17 00:00:00 2001 From: Markus Ressel Date: Wed, 3 Mar 2021 21:39:01 +0100 Subject: [PATCH 4/4] updated README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ec209b0..b374dda 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ All of this is saved to a local database, so it is only needed once per fan conf ### Monitoring To monitor changes in temperature sensor values, a goroutine is started which continuously reads the `tempX_input` files -of all sensors specified in the config. Sensor values are stored in a moving window of size `rollingWindowSize` (see +of all sensors specified in the config. Sensor values are stored as a moving average of size `rollingWindowSize` (see configuration). ### Fan Controllers @@ -209,7 +209,6 @@ configuration). To update the fan speed, one goroutine is started **per fan**, which continuously adjusts the PWM value of a given fan based on the sensor data measured by the monitor. This means: -* calculating the average temperature per sensor using the rolling window data * calculating the ratio between the average temp and the max/min values defined in the config * calculating the target PWM of a fan using the previous ratio, taking its startPWM and maxPWM into account * applying the calculated target PWM to the fan