More like:
Script to reproduce:Output of the script, for previous chart:
The temperature from vcgencmd is in red. A cpu stress was initiated at midtime of the experiment.Script to reproduce:
Code:
import subprocessfrom time import sleepfrom math import pifrom statistics import mean, pstdevdef get_temps(): ''' two measurements of the cpu's temperature returned as a tuple: temp_sysfs: float, derived from an integer value read from /sys temp_vcgen: float, derived from the output of the command vcgencmd There is NO guarantee that the two measurements are from the same output of the SoC's ADC, in fact it's hardly ever the case. This routine is around 2ms long on a RPi5 in idle conditions, this only gives a boundary of the time lag between the two measurements. The only reasonable assumption is that the lag is sufficiently small with respect to the dynamics of the underlying thermal behaviour. ''' with open('/sys/class/thermal/thermal_zone0/temp') as f: cputemp = f.readline() temp_sysfs = int(cputemp.strip('\n')) / 1000 ret = subprocess.run(["vcgencmd", "measure_temp"], capture_output=True) retstring = ret.stdout.decode() temp_vcgen = float(retstring.lstrip("temp=").rstrip("'C\n")) return temp_sysfs, temp_vcgenclass filter_critic(): ''' IIR filter, lowpass, order 2, critically damped designed with bilinear transform s -> 2*fs*(z-1)/(z+1) there is no prewarping ''' def __init__(self, fs, tr): ''' fs: sampling frequency, in Hz tr: 5% response time, in seconds ''' assert tr > 0 # A response time can be defined in more than one way, but is always inversely proportional to the -6dB corner frequency # Here it's the time it takes for a step response to reach 95% of the input step fc = 0.7550095 / tr # this magic constant is valid for the previous definition only assert fc < fs / 2 # Shannon A = 2 * pi * fc / fs # dimensionless rat1 = (A - 2) / (A + 2) rat2 = A / (A + 2) self.coef_y_1 = 2 * rat1 self.coef_y_2 = rat1 * rat1 self.coef_com = rat2 * rat2 self.x_1 = 0.0 # x[n-1] self.x_2 = 0.0 # x[n-2] self.y_1 = 0.0 # y[n-1] self.y_2 = 0.0 # y[n-2] def __call__(self, x): ''' x: float, input at instant n returns: float, output at instant n ''' y = self.coef_com * (x + 2 * self.x_1 + self.x_2) - self.coef_y_1 * self.y_1 - self.coef_y_2 * self.y_2 # update the filter's memory, preparing for next call self.x_2 = self.x_1 self.x_1 = x self.y_2 = self.y_1 self.y_1 = y return ydef main(sampling_freq=20, resp_time=3, dur=60): ''' sampling_freq: sample rate of measurements, in Hz resp_time: response time (5%) of the filters, in seconds dur: duration of the experiment, in seconds The duration of the function is actually more than this, because some extra time is provisioned for the startup transients ''' assert 0 < sampling_freq < 25 # trying a higher rate would't be reasonable, because vcgencmd is so slow N = int(sampling_freq * dur) I = int(1.5 * sampling_freq * resp_time) # allow 1.5 * response time for filters to settle ofile = '/tmp/temp_comparison.csv' print('acquiring temperatures...') vector = [] dt = 1 / sampling_freq for _ in range(N + I): temps = get_temps() vector.append( temps ) sleep(dt) # offline filtering filter_sysfs = filter_critic(sampling_freq, resp_time) filter_vcgen = filter_critic(sampling_freq, resp_time) fvector = [] for n in range(N + I): x_sysfs, x_vcgen = vector[n] y_sysfs = filter_sysfs(x_sysfs) y_vcgen = filter_vcgen(x_vcgen) fvector.append( (y_sysfs, y_vcgen, y_vcgen - y_sysfs) ) fvector = fvector[I:] # discard init data to ensure statistical significance vector = vector[I:] with open(ofile,'w') as f: f.write('sample\ttemp_sysfs\ttemp_vcgen\tsysfs_filtered\tvcgen_filtered\tdelta\n') for n in range(N): xn_sysfs, xn_vcgen = vector[n] yn_sysfs, yn_vcgen, delta = fvector[n] f.write(f'{n}\t{xn_sysfs}\t{xn_vcgen}\t{yn_sysfs}\t{yn_vcgen}\t{delta}\n') print(f'...measurements and filter outputs are in: {ofile}\n') deltas = [fdata[2] for fdata in fvector] print('statistics for the offset between the two curves:') print(f'mean: {mean(deltas):.3f}°C standard deviation: {pstdev(deltas):.3f}°C')if __name__ == '__main__': main() # temperatures are acquired for 60 + 4.5 seconds # sample rate is 20Hz # filtering is done with a 3s response time
Code:
acquiring temperatures......measurements and filter outputs are in: /tmp/temp_comparison.csvstatistics for the offset between the two curves:mean: 0.339°C standard deviation: 0.048°C
Statistics: Posted by pragma — Sat Jan 20, 2024 6:10 am