RPi - ntpheat
Home Page Up Setup on Windows Using NTP Windows LAN tips Events Cable modem notes Monitoring with MRTG GPS 18 + FreeBSD GPS 18 + Windows GPS 18x firmware GPS 18x waveforms NTP 4.2.4 vs. 4.2.5 NTP 4.2.7p241 Rapco 1804M notes Raspberry Pi RPi - ntpheat RPi - quick-start RPi - notes RPi - cross-compile RPi vs BBBlack Sure GPS board Timestamp issues TSC Interpolation Vista & Windows-7/8 Wi-Fi warning Windows after reboot Win-7/8 & Internet Win-7 to Win-10 gains New versions

 

NTPheat

My attention was recently draw to the ntpheat program, which is a simple way to turn a Raspberry Pi's crystal into a more stable frequency.  No, not TCXO standard, but considerably better than a naked RPi, particularly in a centrally heated environment, and especially when that RPi is near to a central heating radiator!  It worked very well for me, but it may not help in your particular configuration, but there's no harm in trying it and seeing what it does!

The program itself

NTPheat is written in Python, and requires no installation or extra libraries - at least it didn't on my RPi.  It monitors the CPU temperature (as an approximation to the actual crystal temperature) and uses CPU cycles to heat up the CPU if its temperature is too low.  There's no CPU cooling, of course!  The original ntpheat was part of the NTPsec project and had lines to check for that NTP environment, whereas the program itself is completely general,  I had to remove the NTPsec-specific lines to allow the program to work for reference NTP.  Lines 24-29 and 51-53 were removed, leaving the code below.
   

#!/usr/bin/env python
#
# generate some heat!
#
# Wrap your RasPi in bubble wrap.  Then run ntpheat in the background.
# It will try to stabilize the CPU temperature at 65C by default.

# Sometimes one copy of ntpheat can use 100% of one CPU and
# still not heat up your RasPi as much as you want.  The temptation
# is to add more insulation to your RasPi, but then it will overshoot
# your target temperature if your load factor goes high.
#
# The solution is to run more than one copy of ntpheat.  This is
# easy to do with the -c option.
#
# To run 3 copies of ntpheat: ntpheat -c 3

import argparse
import hashlib
import os
import sys
import time

# Work with argvars
parser = argparse.ArgumentParser(description="make heat")
parser.add_argument('-c', '--copies',
                    default=[1],
                    dest='copies',
                    help="Number of copies to run.  Default is 1",
                    nargs=1,
                    type=int)
parser.add_argument('-t', '--temp',
                    default=[65.0],
                    dest='target_temp',
                    help="Temperature to hold.  Default is 65.0",
                    nargs=1,
                    type=float)
parser.add_argument('-w', '--wait',
                    default=[0.001],
                    dest='wait',
                    help="Set delay time in seconds, default is 0.1",
                    nargs=1,
                    type=float)
args = parser.parse_args()

args.copies[0] -= 1
while args.copies[0]:
    args.copies[0] -= 1
    pid = os.fork()
    if pid:
        # I am the fork
        break

zone0 = '/sys/class/thermal/thermal_zone0/temp'
cnt = 0

m = hashlib.md5()
temp = 0
max_cnt = args.wait[0] * 200000
# on a RasPi 3 the temp steps seem to be about 0.537 to 0.539C
temp_gate = args.target_temp[0]

while True:
    # on a RasPi 3, 200,000 of the m.update() can be one second
    delta = temp_gate - temp
    if 0 < delta:
        # heat it up
        m.update("Nobody inspects the spammish repetition")
    else:
        cnt = max_cnt
        # cools off slower than it heats up.
        # undocumented Python 'feature', no sleep less than 1 milli Sec
        sleep = args.wait[0] * 10.0 * -delta
        if 0.001 > sleep:
            sleep = 0.001
        time.sleep(sleep)

    cnt += 1
    # read the temperature every max_cnt
    if max_cnt < cnt:
        cnt = 0

        zone_data = open(zone0, 'r')
        for line in zone_data:
            temp = float(line) / 1000

        zone_data.close()

  
How well does it perform?

Before...

Showing the timekeeping swing with CPU temperature changes before the program was installed.

 

During...

The day after the program was installed.  You can see that the CPU temperature is being held to a much more stable value of ~65 °C.

 

After...

The program continues to work stabilising the CPU temperature.  The swings during the day are much reduced, although the sudden change when the heating is switched on is still present, but at a lower level (7 µs) compared to the 20+ µs swing before.  The CPU load averaged 34% for the performance shown below.

 

Acknowledgments

My thanks for the authors of this program, and the folk on the [time-nuts] list for highlighting this program and advising how it could be modified.  

Folkert van Heusden has a program which also use the GPU to heat the processor, leaving the CPU cores free for real processing.  Folkert's program is here (it requires https://github.com/mn416/QPULib)

 

 
Copyright © David Taylor, Edinburgh   Last modified: 2020 Mar 30 at 13:03