Last active
October 23, 2016 00:17
-
-
Save ElectricRCAircraftGuy/38175a4a85c2ac97a934d85db28df395 to your computer and use it in GitHub Desktop.
UPDATE: LATEST VERSION FOUND HERE NOW (https://github.com/ElectricRCAircraftGuy/PyTiming/); A Windows and Linux-compatible Python module for ultra-great resolution timestamps (sub-microsecond), even in older versions of Python 3 which don't natively support (in the time module, for instance) high resolutions like this. Website: http://www.electr…
This file contains 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
""" | |
timing.py | |
-create some low-level Arduino-like millis() (milliseconds) and micros() | |
(microseconds) timing functions for Python | |
By Gabriel Staples | |
http://www.ElectricRCAircraftGuy.com | |
-click "Contact me" at the top of my website to find my email address | |
Started: 11 July 2016 | |
Updated: 7 Sept 2016 | |
History (newest on top): | |
20160907 - v0.2.1 created - updated delay functions to use modulus operator to guarantee proper C uint32_t-like underflow subtraction behavior when the timer rolls over | |
20160813 - v0.2.0 created - added Linux capability | |
20160711 - v0.1.0 created - functions for Windows *only* (via the QPC timer) | |
References: | |
WINDOWS: | |
-personal (C++ code): GS_PCArduino.h | |
1) Acquiring high-resolution time stamps (Windows) | |
-https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx | |
2) QueryPerformanceCounter function (Windows) | |
-https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx | |
3) QueryPerformanceFrequency function (Windows) | |
-https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx | |
4) LARGE_INTEGER union (Windows) | |
-https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx | |
-*****http://stackoverflow.com/questions/4430227/python-on-win32-how-to-get- | |
absolute-timing-cpu-cycle-count | |
LINUX: | |
-http://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python | |
PYTHON (general): | |
-https://docs.python.org/3.5/library/ctypes.html - ctypes referene page | |
""" | |
import ctypes, os | |
#Constants: | |
VERSION = '0.2.1' | |
#------------------------------------------------------------------- | |
#MODULE FUNCTIONS: | |
#------------------------------------------------------------------- | |
#OS-specific low-level timing functions: | |
if (os.name=='nt'): #for Windows: | |
def micros(): | |
"return a timestamp in microseconds (us)" | |
tics = ctypes.c_int64() #use *signed* 64-bit variables; see the "QuadPart" variable here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx | |
freq = ctypes.c_int64() | |
#get ticks on the internal ~2MHz QPC clock | |
ctypes.windll.Kernel32.QueryPerformanceCounter(ctypes.byref(tics)) | |
#get the actual freq. of the internal ~2MHz QPC clock | |
ctypes.windll.Kernel32.QueryPerformanceFrequency(ctypes.byref(freq)) | |
t_us = tics.value*1e6/freq.value | |
return t_us | |
def millis(): | |
"return a timestamp in milliseconds (ms)" | |
tics = ctypes.c_int64() #use *signed* 64-bit variables; see the "QuadPart" variable here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx | |
freq = ctypes.c_int64() | |
#get ticks on the internal ~2MHz QPC clock | |
ctypes.windll.Kernel32.QueryPerformanceCounter(ctypes.byref(tics)) | |
#get the actual freq. of the internal ~2MHz QPC clock | |
ctypes.windll.Kernel32.QueryPerformanceFrequency(ctypes.byref(freq)) | |
t_ms = tics.value*1e3/freq.value | |
return t_ms | |
elif (os.name=='posix'): #for Linux: | |
#Constants: | |
CLOCK_MONOTONIC_RAW = 4 # see <linux/time.h> here: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h | |
#prepare ctype timespec structure of {long, long} | |
#-NB: use c_long (generally signed 32-bit) variables within the timespec C struct, per the definition here: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h | |
class timespec(ctypes.Structure): | |
_fields_ =\ | |
[ | |
('tv_sec', ctypes.c_long), | |
('tv_nsec', ctypes.c_long) | |
] | |
#Configure Python access to the clock_gettime C library, via ctypes: | |
#Documentation: | |
#-ctypes.CDLL: https://docs.python.org/3.2/library/ctypes.html | |
#-librt.so.1 with clock_gettime: https://docs.oracle.com/cd/E36784_01/html/E36873/librt-3lib.html #- | |
#-Linux clock_gettime(): http://linux.die.net/man/3/clock_gettime | |
librt = ctypes.CDLL('librt.so.1', use_errno=True) | |
clock_gettime = librt.clock_gettime | |
#specify input arguments and types to the C clock_gettime() function | |
# (int clock_ID, timespec* t) | |
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] | |
def monotonic_time(): | |
"return a timestamp in seconds (sec)" | |
t = timespec() | |
#(Note that clock_gettime() returns 0 for success, or -1 for failure, in | |
# which case errno is set appropriately) | |
#-see here: http://linux.die.net/man/3/clock_gettime | |
if clock_gettime(CLOCK_MONOTONIC_RAW , ctypes.pointer(t)) != 0: | |
#if clock_gettime() returns an error | |
errno_ = ctypes.get_errno() | |
raise OSError(errno_, os.strerror(errno_)) | |
return t.tv_sec + t.tv_nsec*1e-9 #sec | |
def micros(): | |
"return a timestamp in microseconds (us)" | |
return monotonic_time()*1e6 #us | |
def millis(): | |
"return a timestamp in milliseconds (ms)" | |
return monotonic_time()*1e3 #ms | |
#Private module function | |
#-see here for use of underscore to make "module private": http://stackoverflow.com/questions/1547145/defining-private-module-functions-in-python/1547160#1547160 | |
#-see here for example of constrain function: http://stackoverflow.com/questions/34837677/a-pythonic-way-to-write-a-constrain-function/34837691 | |
def _constrain(val, min_val, max_val): | |
"constrain a number to be >= min_val and <= max_val" | |
if (val < min_val): | |
val = min_val | |
elif (val > max_val): | |
val = max_val | |
return val | |
#Other timing functions: | |
def delay(delay_ms): | |
"delay for delay_ms milliseconds (ms)" | |
#constrain the commanded delay time to be within valid C type uint32_t limits | |
delay_ms = _constrain(delay_ms, 0, (1<<32)-1) | |
t_start = millis() | |
while ((millis() - t_start)%(1<<32) < delay_ms): #use modulus to force C uint32_t-like underflow behavior | |
pass #do nothing | |
return | |
def delayMicroseconds(delay_us): | |
"delay for delay_us microseconds (us)" | |
#constrain the commanded delay time to be within valid C type uint32_t limits | |
delay_us = _constrain(delay_us, 0, (1<<32)-1) | |
t_start = micros() | |
while ((micros() - t_start)%(1<<32) < delay_us): #use modulus to force C uint32_t-like underflow behavior | |
pass #do nothing | |
return | |
#------------------------------------------------------------------- | |
#EXAMPLES: | |
#------------------------------------------------------------------- | |
#Only executute this block of code if running this module directly, | |
#*not* if importing it | |
#-see here: http://effbot.org/pyfaq/tutor-what-is-if-name-main-for.htm | |
if __name__ == "__main__": #if running this module as a stand-alone program | |
#print loop execution time 100 times, using micros() | |
tStart = micros() #us | |
for x in range(0, 100): | |
tNow = micros() #us | |
dt = tNow - tStart #us; delta time | |
tStart = tNow #us; update | |
print("dt(us) = " + str(dt)) | |
#print loop execution time 100 times, using millis() | |
print("\n") | |
tStart = millis() #ms | |
for x in range(0, 100): | |
tNow = millis() #ms | |
dt = tNow - tStart #ms; delta time | |
tStart = tNow #ms; update | |
print("dt(ms) = " + str(dt)) | |
#print a counter once per second, for 5 seconds, using delay | |
print("\nstart") | |
for i in range(1,6): | |
delay(1000) | |
print(i) | |
#print a counter once per second, for 5 seconds, using delayMicroseconds | |
print("\nstart") | |
for i in range(1,6): | |
delayMicroseconds(1000000) | |
print(i) | |
This file contains 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
""" | |
sleep_test.py | |
-see what the resolution of the time.sleep() function is | |
By Gabriel Staples | |
http://www.ElectricRCAircraftGuy.com | |
-click "Contact me" at the top of my website to find my email address | |
13 Aug. 2016 | |
""" | |
import time | |
import GS_timing as timing | |
def delayMicroseconds(n): | |
time.sleep(n / 1000000.) | |
def delayMillisecond(n): | |
time.sleep(n / 1000.) | |
t_start = 0 | |
t_end = 0 | |
#using time.sleep | |
print('using time.sleep') | |
print('delayMicroseconds(1)') | |
for x in range(10): | |
t_start = timing.micros() #us | |
delayMicroseconds(1) | |
t_end = timing.micros() #us | |
print('dt (us) = ' + str(t_end - t_start)) | |
print('delayMicroseconds(2000)') | |
for x in range(10): | |
t_start = timing.micros() #us | |
delayMicroseconds(2000) | |
t_end = timing.micros() #us | |
print('dt (us) = ' + str(t_end - t_start)) | |
#using GS_timing | |
print('\nusing GS_timing') | |
print('timing.delayMicroseconds(1)') | |
for x in range(10): | |
t_start = timing.micros() #us | |
timing.delayMicroseconds(1) | |
t_end = timing.micros() #us | |
print('dt (us) = ' + str(t_end - t_start)) | |
print('timing.delayMicroseconds(2000)') | |
for x in range(10): | |
t_start = timing.micros() #us | |
timing.delayMicroseconds(2000) | |
t_end = timing.micros() #us | |
print('dt (us) = ' + str(t_end - t_start)) | |
""" | |
SAMPLE RESULTS ON MY WINDOWS 8.1 MACHINE: | |
using time.sleep | |
delayMicroseconds(1) | |
dt (us) = 2872.059814453125 | |
dt (us) = 886.3939208984375 | |
dt (us) = 770.4649658203125 | |
dt (us) = 1138.7698974609375 | |
dt (us) = 1426.027099609375 | |
dt (us) = 734.557861328125 | |
dt (us) = 10617.233642578125 | |
dt (us) = 9594.90576171875 | |
dt (us) = 9155.299560546875 | |
dt (us) = 9520.526611328125 | |
delayMicroseconds(2000) | |
dt (us) = 8799.3056640625 | |
dt (us) = 9609.2685546875 | |
dt (us) = 9679.5439453125 | |
dt (us) = 9248.145263671875 | |
dt (us) = 9389.721923828125 | |
dt (us) = 9637.994262695312 | |
dt (us) = 9616.450073242188 | |
dt (us) = 9592.853881835938 | |
dt (us) = 9465.639892578125 | |
dt (us) = 7650.276611328125 | |
using GS_timing | |
timing.delayMicroseconds(1) | |
dt (us) = 53.3477783203125 | |
dt (us) = 36.93310546875 | |
dt (us) = 36.9329833984375 | |
dt (us) = 34.8812255859375 | |
dt (us) = 35.3941650390625 | |
dt (us) = 40.010986328125 | |
dt (us) = 38.4720458984375 | |
dt (us) = 56.425537109375 | |
dt (us) = 35.9072265625 | |
dt (us) = 36.420166015625 | |
timing.delayMicroseconds(2000) | |
dt (us) = 2039.526611328125 | |
dt (us) = 2046.195068359375 | |
dt (us) = 2033.8841552734375 | |
dt (us) = 2037.4747314453125 | |
dt (us) = 2032.34521484375 | |
dt (us) = 2086.2059326171875 | |
dt (us) = 2035.4229736328125 | |
dt (us) = 2051.32470703125 | |
dt (us) = 2040.03955078125 | |
dt (us) = 2027.215576171875 | |
SAMPLE RESULTS ON MY RASPBERRY PI VERSION 1 B+: | |
using time.sleep | |
delayMicroseconds(1) | |
dt (us) = 1022.0 | |
dt (us) = 417.0 | |
dt (us) = 407.0 | |
dt (us) = 450.0 | |
dt (us) = 2078.0 | |
dt (us) = 393.0 | |
dt (us) = 1297.0 | |
dt (us) = 878.0 | |
dt (us) = 1135.0 | |
dt (us) = 2896.0 | |
delayMicroseconds(2000) | |
dt (us) = 2746.0 | |
dt (us) = 2568.0 | |
dt (us) = 2512.0 | |
dt (us) = 2423.0 | |
dt (us) = 2454.0 | |
dt (us) = 2608.0 | |
dt (us) = 2518.0 | |
dt (us) = 2569.0 | |
dt (us) = 2548.0 | |
dt (us) = 2496.0 | |
using GS_timing | |
timing.delayMicroseconds(1) | |
dt (us) = 572.0 | |
dt (us) = 673.0 | |
dt (us) = 1084.0 | |
dt (us) = 561.0 | |
dt (us) = 728.0 | |
dt (us) = 576.0 | |
dt (us) = 556.0 | |
dt (us) = 584.0 | |
dt (us) = 576.0 | |
dt (us) = 578.0 | |
timing.delayMicroseconds(2000) | |
dt (us) = 2741.0 | |
dt (us) = 2466.0 | |
dt (us) = 2522.0 | |
dt (us) = 2810.0 | |
dt (us) = 2589.0 | |
dt (us) = 2681.0 | |
dt (us) = 2546.0 | |
dt (us) = 3090.0 | |
dt (us) = 2600.0 | |
dt (us) = 2400.0 | |
""" |
This file contains 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
""" | |
test.py | |
By Gabriel Staples | |
http://www.ElectricRCAircraftGuy.com | |
-click "Contact me" at the top of my website to find my email address | |
13 Aug. 2016 | |
""" | |
import GS_timing as timing | |
print(timing.micros()) | |
print(timing.millis()) | |
print('version = ' + timing.VERSION) | |
print(timing._constrain(10,11,15)) #11 |
This file contains 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
""" | |
test.py | |
By Gabriel Staples | |
http://www.ElectricRCAircraftGuy.com | |
-click "Contact me" at the top of my website to find my email address | |
13 Aug. 2016 | |
""" | |
from GS_timing import * | |
print(micros()) | |
print(millis()) | |
print('version = ' + VERSION) | |
#SHOULD PRODUCE AN ERROR, since _constrain is module private | |
#-see here: http://stackoverflow.com/questions/1547145/defining-private-module-functions-in-python/1547160#1547160 | |
print(_constrain(10,11,15)) #YEP! PRODUCES ERROR! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment