Overview

PyPi module https://pypi.org/project/accurate-timed-loop.html
git repository https://bitbucket.org/arrizza-public/accurate-timed-loop
git command git clone git@bitbucket.org:arrizza-public/accurate-timed-loop.git
Verification Report https://arrizza.com/web-ver/python-accurate-timed-loop-report.html
Version Info
  • macOS 14.2.1, Python 3.10
  • macOS 14.5, Python 3.10
  • Ubuntu 20.04 focal, Python 3.10
  • Ubuntu 22.04 jammy, Python 3.10
  • Ubuntu 24.04 noble, Python 3.10
  • win32 Windows 10, Python 3.10

Summary

This is a python module that provides a way to have an accurate timed loop.

For example if you need to do an activity every 250ms +/-10ms, this loop will do that.

Sample code

see sample.py for a full example

import accurate_timed_loop

loop_delay = 0.250  # seconds
total_wait = 25.0  # seconds
for elapsed, start_time in accurate_timed_loop.accurate_wait(total_wait, loop_delay):
    # ... do task every 250 mS
    pass

Scripts

Accuracy and Limitations

The sample.py does testing and shows that on Windows MSYS2 the std deviation error is roughly 4mS in a 250mS loop. This means that 95% of loops will be +/-8 mS of the requested loop_delay.

      expected    elapsed  diff1(ms)  actual(s)  diff2(ms)
  1   0.000000   0.000000      0.000   0.000000      0.000
  2   0.250000   0.257294      7.294   0.257294      7.294
<snip>
100  24.750000  24.764093     14.093  24.764093     14.093
101  25.000000  25.015579     15.579  25.015579     15.579


Stats:
loop count     : 101 loops
Error Range    : 0.000 to 24.406 mS
Error Stddev   :      5.009 mS
Error Average  :      8.863 mS
Recommended adj: 0.012200
     sample rc=0
     doit overall rc=0

This value is specific to Windows and to the PC that it is running on.

To make it more accurate for your PC and OS use the fixed_adjustment parameter. Set it so the minimum and maximum are roughly symmetrical around 0. The Stdev and Average error values at that point should be minimal.

import accurate_timed_loop

loop_delay = 0.250  # seconds
total_wait = 25.0  # seconds
adj = 0.009228  # macOS
for elapsed, start_time in accurate_timed_loop.accurate_wait(total_wait, loop_delay, fixed_adjustment=adj):
    # ... do task every 250 mS
    pass

Notes:

  • Re-run this several times, and tweak the fixed adjustment.
  • The sample.py reports a "Recommended adj" that usually results in better accuracy.
  • macOS and Ubuntu tend to be less variant than Windows

This report shows that std deviation is much better.

      expected    elapsed  diff1(ms)  actual(s)  diff2(ms)
  1   0.000000   0.000000      0.000   0.000000      0.000
  2   0.250000   0.251537      1.537   0.251537      1.537
<snip>
101  25.000000  24.989502    -10.498  24.989502    -10.498
102  25.250000  25.241386     -8.614  25.241386     -8.614


Stats:
loop count     : 102 loops
Error Range    : -9.228 to 5.864 mS
Error Stddev   :      1.238 mS
Error Average  :      4.953 mS
Recommended adj: 0.009228
     sample rc=0
     doit overall rc=0

Limitations:

  • there is NO guarantee that the average error will always be that low or that consistent
  • the following runs were on a macOS
# === first run:
Stats:
loop count     : 102 loops
Error Range    : -9.486 to 4.613 mS
Error Stddev   :      1.962 mS
Error Average  :      5.775 mS
Recommended adj: 0.009486

# === second run:
Stats:
loop count     : 102 loops
Error Range    : -9.587 to 3.287 mS
Error Stddev   :      2.163 mS
Error Average  :      6.745 mS
Recommended adj: 0.009587

# === third run:
Stats:
loop count     : 102 loops
Error Range    : -9.472 to 6.782 mS
Error Stddev   :      1.546 mS
Error Average  :      5.597 mS
Recommended adj: 0.009472

# === fourth run:
Stats:
loop count     : 101 loops
Error Range    : -9.518 to 10.365 mS
Error Stddev   :      1.865 mS
Error Average  :      5.410 mS
Recommended adj: 0.009518

# === fifth run:
Stats:
loop count     : 101 loops
Error Range    : -9.369 to 13.726 mS
Error Stddev   :      2.196 mS
Error Average  :      5.614 mS
Recommended adj: 0.009369
  • if you use the adj parameter the incoming "elapsed" parameter will not be after your expected delay. For example these two came in:
    • at 24.749 seconds instead of the expected 24.750 seconds
    • at 24.999 seconds instead of the expected 25.000 seconds
      expected    elapsed  diff1(ms)  actual(s)  diff2(ms)
100  24.750000  24.749573     -0.427  24.749573     -0.427
101  25.000000  24.999601     -0.399  24.999601     -0.399

- John Arrizza