Created
November 29, 2011 10:43
-
-
Save ihaque/1404373 to your computer and use it in GitHub Desktop.
Automatic temperature-based fan-speed control for AMD GPUs in Linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from bisect import bisect_left | |
from subprocess import Popen, PIPE | |
from time import sleep | |
import numpy as np | |
def get_temperature(gpu=0): | |
lines = Popen(["aticonfig", "--adapter=%d" % gpu, "--od-gettemperature"], | |
stdout=PIPE).communicate()[0].split("\n") | |
lines = [xx.strip() for xx in lines if len(xx.strip()) > 0] | |
# Adapter 0 - AMD Radeon HD 6800 Series | |
# Sensor 0: Temperature - 63.00 C | |
# | |
templine = lines[1].strip() | |
fields = templine.split() | |
assert(fields[2] == "Temperature") | |
return float(fields[4]) | |
def set_fan_speed(fanspeed_pct, gpu=0): | |
output = Popen(["aticonfig", "--pplib-cmd", | |
"set fanspeed %d %d" % (gpu, fanspeed_pct)], | |
stdout=PIPE).communicate()[0] | |
assert(output.strip() == "PPLIB command execution is Successful!") | |
return | |
def get_fan_speed(gpu=0): | |
lines = Popen(["aticonfig", "--pplib-cmd", "get fanspeed %d" % gpu], | |
stdout=PIPE).communicate()[0].split("\n") | |
# Fan speed query: | |
# Query Index: 0, Speed in percent | |
# Result: Fan Speed: 20% | |
linefields = [xx.strip().split() for xx in lines if len(xx.strip()) > 0] | |
assert(" ".join(linefields[-1][1:3]) == "Fan Speed:") | |
# Strip the % | |
return int(linefields[-1][-1][:-1]) | |
class LinearInterpolatingFanCurve: | |
def __init__(self, curve): | |
curve.sort() | |
assert(curve[0][1] >= 20) | |
assert(curve[-1][1] == 100) | |
self.temps, self.fans = zip(*curve) | |
self.temps = map(float, self.temps) | |
self.fans = map(float, self.fans) | |
def temp2fan(self, temp): | |
if temp < self.temps[0]: | |
return self.fans[0] | |
elif temp > self.temps[-1]: | |
return self.fans[-1] | |
else: | |
idx = bisect_left(self.temps, temp) | |
tleft = self.temps[idx-1] | |
tright = self.temps[idx] | |
fleft = self.fans[idx-1] | |
fright = self.fans[idx] | |
fanspeed = (temp-tleft)/(tright-tleft) * fright +\ | |
(tright-temp)/(tright-tleft) * fleft | |
fanspeed = int(round(fanspeed)) | |
print "Tl =",tleft,"Tr =",tright,"Fl =",fleft,"Fr =",fright,"T =",temp,"Fs =",fanspeed | |
return fanspeed | |
def main(): | |
print "Manual fan speed control is dangerous and can fry your hardware!" | |
print "Use this program at your own risk! I take no responsibility!" | |
poll_period = 1 | |
buffer_size = 4 | |
hysteresis = 1 | |
# Temperature curve has control points specified by pairs of | |
# (temperature in degC, fan speed in %) | |
curve = [( 0, 20), (65, 20), | |
(70, 40), | |
(80, 70), (90, 100)] | |
fanmap = LinearInterpolatingFanCurve(curve) | |
fanspeed = get_fan_speed() | |
fantemp = get_temperature() | |
temps_buffer = np.empty((buffer_size,)) | |
temps_buffer[:] = fantemp | |
idx = 0 | |
while True: | |
temps_buffer[idx] = get_temperature() | |
idx = (idx + 1) % buffer_size | |
temp = np.mean(temps_buffer) | |
fan = fanmap.temp2fan(temp) | |
if fan != fanspeed and abs(temp-fantemp) > hysteresis: | |
set_fan_speed(fan) | |
fanspeed = fan | |
fantemp = temp | |
print "\tSet fan speed to",get_fan_speed() | |
sleep(poll_period) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment