Drift in it’s most raw form is where a sleep is added to the code to be run and the total time of each loop is dependent on the time it takes for the code to run rather than the fixed sleep element.
My very simple timer improves on that by always looking to a preset point in time to do the next iteration, that point in time is defined in each new iteration and can be really quite accurate with little or no drift, this is the common and basic way of timing loops, but the cost of that accuracy is cpu power, to combat the high cpu use, I added a 0.1s sleep so that it isn’t looping to fast, essentially < 0.1s could get added to each iteration causing a drift. If you removed that 0.1s sleep, the drift would (almost) be removed, but the cpu would be as high as your threaded example.
I do have my own way of eliminating drift, I use it a lot and I think it should be used more widespread in the OEM project but I haven’t had time to document why it’s needed and the benefits of using it so I hope it’s relatively self explanatory in this case use.
For now I have been calling this method “harmonized intervals”, it basically ensures all “10 s intervals” are always aligned to a unixtime timestamp at an exact whole 10s interval since 1970-01-01 00:00.
I’ve modded my script to use this “harmonized intervals” method and added a timestamp to the “data sent” print (nothing else is changed)
#!/usr/bin/env python
import requests
import socket
import time
apireadkey = "abc123abc123abc123"
feed_ids = "12300,12301,12302,12303"
#feed_ids = "12300"
# The fetch api works for a singular feed id too so it works in both scenario's
# Uncomment either one of the 2 lines above to try
emon_api_url = "https://emoncms.org/feed/fetch.json?ids=" + feed_ids + "&apikey=" + apireadkey
#socket code___
UDP_IP = "192.168.43.185"
UDP_PORT = 6400
interval = 10
def calc_next_time(time_now, interval):
time_next = (((time_now//interval)*interval)+interval)
return time_next
time_next = calc_next_time(time.time(),interval)
while(True):
# Get the current time
time_now = time.time()
# Check if it's time to do another transmission
if time_now >= time_next:
# Request the data from emonCMS
response = requests.get(emon_api_url)
# Parse the data if the response was good
if response.status_code == 200:
datalist = response.json()
# Convert any single value returned to a list of one value
if not isinstance(datalist,(list)):datalist=[datalist]
# Make a CSV string of floats rounded to 3 decimals max
stringydata = ','.join([str(round(i,3)) for i in datalist])
# Send the data over UDP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.sendto(stringydata.encode('utf-8'), (UDP_IP, UDP_PORT))
print (str(time_now) + "\tData sent: " + stringydata)
# set the time of next loop
time_next = calc_next_time(time_now,interval)
# Don't loop too fast
time.sleep(0.1)
When this runs you should get output like so
1521117490.07 Data sent: 237.72,-77.0,292.0,369.0
1521117500.02 Data sent: 237.77,-165.0,201.0,366.0
1521117510.09 Data sent: 237.71,267.0,631.0,364.0
1521117520.09 Data sent: 237.86,-109.0,254.0,363.0
1521117530.05 Data sent: 237.91,-158.0,201.0,359.0
1521117540.01 Data sent: 237.77,271.0,629.0,358.0
1521117550.1 Data sent: 237.92,-109.0,241.0,350.0
1521117560.09 Data sent: 237.92,-147.0,205.0,352.0
1521117570.05 Data sent: 237.86,269.0,622.0,353.0
1521117580.05 Data sent: 237.67,-117.0,237.0,354.0
1521117590.06 Data sent: 237.77,-157.0,204.0,361.0
1521117600.07 Data sent: 237.77,-157.0,204.0,361.0
1521117610.0 Data sent: 237.68,282.0,646.0,364.0
1521117620.01 Data sent: 238.03,-166.0,210.0,376.0
1521117630.06 Data sent: 238.16,300.0,680.0,380.0
1521117640.03 Data sent: 238.34,-121.0,263.0,384.0
1521117650.03 Data sent: 238.39,-177.0,209.0,386.0
1521117660.1 Data sent: 238.1,272.0,662.0,390.0
1521117670.07 Data sent: 238.14,-107.0,287.0,394.0
1521117680.04 Data sent: 238.24,-177.0,220.0,397.0
1521117690.03 Data sent: 237.95,284.0,693.0,409.0
1521117700.09 Data sent: 238.0,-45.0,354.0,399.0
1521117710.05 Data sent: 238.33,-84.0,326.0,410.0
1521117720.1 Data sent: 238.12,-26.0,371.0,397.0
1521117730.06 Data sent: 238.17,-179.0,224.0,403.0
1521117740.05 Data sent: 237.99,277.0,689.0,412.0
1521117750.05 Data sent: 238.08,-116.0,289.0,405.0
1521117760.01 Data sent: 237.93,-125.0,280.0,405.0
1521117770.09 Data sent: 238.08,-32.0,368.0,400.0
1521117780.09 Data sent: 238.03,210.0,621.0,411.0
1521117790.05 Data sent: 237.95,-44.0,369.0,413.0
1521117800.06 Data sent: 237.72,-45.0,374.0,419.0
1521117810.05 Data sent: 237.66,-36.0,393.0,429.0
That first column is the timestamp and you can see it is almost to a whole 10s interval. The few huntredths of a second are caused by the 0.1s sleep and although they delay the iteration by up to 0.1s they do not add to a “drift” as the next timestamp is set using a resolution of 10s it will ignore anything less than 10s, if a delay at the emoncms server resulted in a loop taking 11 secs, the next payload would be late the one after that would be timed independently and not be effected by that delay.