Skip to content

Commit 6c58836

Browse files
Updated to version 0.2.1, for Win *and* Linux
1 parent fa19bf6 commit 6c58836

File tree

7 files changed

+392
-104
lines changed

7 files changed

+392
-104
lines changed

GS_timing.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""
2+
timing.py
3+
-create some low-level Arduino-like millis() (milliseconds) and micros()
4+
(microseconds) timing functions for Python
5+
By Gabriel Staples
6+
http://www.ElectricRCAircraftGuy.com
7+
-click "Contact me" at the top of my website to find my email address
8+
Started: 11 July 2016
9+
Updated: 7 Sept 2016
10+
11+
History (newest on top):
12+
* 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
13+
* 20160813 - v0.2.0 created - added Linux capability
14+
* 20160711 - v0.1.0 created - functions for Windows *only* (via the QPC timer)
15+
16+
References:
17+
WINDOWS:
18+
-personal (C++ code): GS_PCArduino.h
19+
1) Acquiring high-resolution time stamps (Windows)
20+
-https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
21+
2) QueryPerformanceCounter function (Windows)
22+
-https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
23+
3) QueryPerformanceFrequency function (Windows)
24+
-https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx
25+
4) LARGE_INTEGER union (Windows)
26+
-https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx
27+
28+
-*****http://stackoverflow.com/questions/4430227/python-on-win32-how-to-get-
29+
absolute-timing-cpu-cycle-count
30+
31+
LINUX:
32+
-http://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python
33+
34+
PYTHON (general):
35+
-https://docs.python.org/3.5/library/ctypes.html - ctypes referene page
36+
37+
"""
38+
39+
import ctypes, os
40+
41+
#Constants:
42+
VERSION = '0.2.1'
43+
44+
#-------------------------------------------------------------------
45+
#MODULE FUNCTIONS:
46+
#-------------------------------------------------------------------
47+
#OS-specific low-level timing functions:
48+
if (os.name=='nt'): #for Windows:
49+
def micros():
50+
"return a timestamp in microseconds (us)"
51+
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
52+
freq = ctypes.c_int64()
53+
54+
#get ticks on the internal ~2MHz QPC clock
55+
ctypes.windll.Kernel32.QueryPerformanceCounter(ctypes.byref(tics))
56+
#get the actual freq. of the internal ~2MHz QPC clock
57+
ctypes.windll.Kernel32.QueryPerformanceFrequency(ctypes.byref(freq))
58+
59+
t_us = tics.value*1e6/freq.value
60+
return t_us
61+
62+
def millis():
63+
"return a timestamp in milliseconds (ms)"
64+
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
65+
freq = ctypes.c_int64()
66+
67+
#get ticks on the internal ~2MHz QPC clock
68+
ctypes.windll.Kernel32.QueryPerformanceCounter(ctypes.byref(tics))
69+
#get the actual freq. of the internal ~2MHz QPC clock
70+
ctypes.windll.Kernel32.QueryPerformanceFrequency(ctypes.byref(freq))
71+
72+
t_ms = tics.value*1e3/freq.value
73+
return t_ms
74+
75+
elif (os.name=='posix'): #for Linux:
76+
77+
#Constants:
78+
CLOCK_MONOTONIC_RAW = 4 # see <linux/time.h> here: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
79+
80+
#prepare ctype timespec structure of {long, long}
81+
#-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
82+
class timespec(ctypes.Structure):
83+
_fields_ =\
84+
[
85+
('tv_sec', ctypes.c_long),
86+
('tv_nsec', ctypes.c_long)
87+
]
88+
89+
#Configure Python access to the clock_gettime C library, via ctypes:
90+
#Documentation:
91+
#-ctypes.CDLL: https://docs.python.org/3.2/library/ctypes.html
92+
#-librt.so.1 with clock_gettime: https://docs.oracle.com/cd/E36784_01/html/E36873/librt-3lib.html #-
93+
#-Linux clock_gettime(): http://linux.die.net/man/3/clock_gettime
94+
librt = ctypes.CDLL('librt.so.1', use_errno=True)
95+
clock_gettime = librt.clock_gettime
96+
#specify input arguments and types to the C clock_gettime() function
97+
# (int clock_ID, timespec* t)
98+
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
99+
100+
def monotonic_time():
101+
"return a timestamp in seconds (sec)"
102+
t = timespec()
103+
#(Note that clock_gettime() returns 0 for success, or -1 for failure, in
104+
# which case errno is set appropriately)
105+
#-see here: http://linux.die.net/man/3/clock_gettime
106+
if clock_gettime(CLOCK_MONOTONIC_RAW , ctypes.pointer(t)) != 0:
107+
#if clock_gettime() returns an error
108+
errno_ = ctypes.get_errno()
109+
raise OSError(errno_, os.strerror(errno_))
110+
return t.tv_sec + t.tv_nsec*1e-9 #sec
111+
112+
def micros():
113+
"return a timestamp in microseconds (us)"
114+
return monotonic_time()*1e6 #us
115+
116+
def millis():
117+
"return a timestamp in milliseconds (ms)"
118+
return monotonic_time()*1e3 #ms
119+
120+
#Private module function
121+
#-see here for use of underscore to make "module private": http://stackoverflow.com/questions/1547145/defining-private-module-functions-in-python/1547160#1547160
122+
#-see here for example of constrain function: http://stackoverflow.com/questions/34837677/a-pythonic-way-to-write-a-constrain-function/34837691
123+
def _constrain(val, min_val, max_val):
124+
"constrain a number to be >= min_val and <= max_val"
125+
if (val < min_val):
126+
val = min_val
127+
elif (val > max_val):
128+
val = max_val
129+
return val
130+
131+
#Other timing functions:
132+
def delay(delay_ms):
133+
"delay for delay_ms milliseconds (ms)"
134+
#constrain the commanded delay time to be within valid C type uint32_t limits
135+
delay_ms = _constrain(delay_ms, 0, (1<<32)-1)
136+
t_start = millis()
137+
while ((millis() - t_start)%(1<<32) < delay_ms): #use modulus to force C uint32_t-like underflow behavior
138+
pass #do nothing
139+
return
140+
141+
def delayMicroseconds(delay_us):
142+
"delay for delay_us microseconds (us)"
143+
#constrain the commanded delay time to be within valid C type uint32_t limits
144+
delay_us = _constrain(delay_us, 0, (1<<32)-1)
145+
t_start = micros()
146+
while ((micros() - t_start)%(1<<32) < delay_us): #use modulus to force C uint32_t-like underflow behavior
147+
pass #do nothing
148+
return
149+
150+
#-------------------------------------------------------------------
151+
#EXAMPLES:
152+
#-------------------------------------------------------------------
153+
#Only executute this block of code if running this module directly,
154+
#*not* if importing it
155+
#-see here: http://effbot.org/pyfaq/tutor-what-is-if-name-main-for.htm
156+
if __name__ == "__main__": #if running this module as a stand-alone program
157+
158+
#print loop execution time 100 times, using micros()
159+
tStart = micros() #us
160+
for x in range(0, 100):
161+
tNow = micros() #us
162+
dt = tNow - tStart #us; delta time
163+
tStart = tNow #us; update
164+
print("dt(us) = " + str(dt))
165+
166+
#print loop execution time 100 times, using millis()
167+
print("\n")
168+
tStart = millis() #ms
169+
for x in range(0, 100):
170+
tNow = millis() #ms
171+
dt = tNow - tStart #ms; delta time
172+
tStart = tNow #ms; update
173+
print("dt(ms) = " + str(dt))
174+
175+
#print a counter once per second, for 5 seconds, using delay
176+
print("\nstart")
177+
for i in range(1,6):
178+
delay(1000)
179+
print(i)
180+
181+
#print a counter once per second, for 5 seconds, using delayMicroseconds
182+
print("\nstart")
183+
for i in range(1,6):
184+
delayMicroseconds(1000000)
185+
print(i)
186+
187+
188+

LICENSE renamed to LICENSE.txt

File renamed without changes.

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# PyTiming
22
## Description:
3-
Create some low-level Arduino-like millis() &amp; delay() (for milliseconds) and micros() and delayMicroseconds() (for microseconds) timing functions for Python
3+
Create some low-level Arduino-like millis() & delay() (for milliseconds) and micros() and delayMicroseconds() (for microseconds) timing functions for Python.
4+
5+
Compatible with Python in both Windows *and* Linux. Has ultra-great resolution timestamps (sub-microsecond), even in older versions of Python 3 which don't natively support (in the [time](https://docs.python.org/2.7/library/time.html) module, for instance) high resolutions like this.
46

57
## Website:
68
http://www.electricrcaircraftguy.com/2016/07/arduino-like-millisecond-and-microsecond-timestamps-in-python.html
79

8-
## Comments:
9-
* 22 July 2016: This module works for Python in Windows only so far. If anyone would like to expand it to support Linux too, please do.
10-
11-
Thanks,
10+
## History:
11+
(newest on top)
12+
* 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
13+
* 20160813 - v0.2.0 created - added Linux capability
14+
* 20160711 - v0.1.0 created - functions for Windows *only* (via the QPC timer)
15+
16+
## Author:
1217
Gabriel Staples
13-
www.ElectricRCAircraftGuy.com
18+
www.ElectricRCAircraftGuy.com
19+
-click "Contact me" at the top of my website to find my email address

sleep_test.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
"""
2+
sleep_test.py
3+
-see what the resolution of the time.sleep() function is
4+
By Gabriel Staples
5+
http://www.ElectricRCAircraftGuy.com
6+
-click "Contact me" at the top of my website to find my email address
7+
8+
13 Aug. 2016
9+
"""
10+
11+
import time
12+
import GS_timing as timing
13+
14+
def delayMicroseconds(n):
15+
time.sleep(n / 1000000.)
16+
17+
def delayMillisecond(n):
18+
time.sleep(n / 1000.)
19+
20+
t_start = 0
21+
t_end = 0
22+
23+
#using time.sleep
24+
print('using time.sleep')
25+
print('delayMicroseconds(1)')
26+
for x in range(10):
27+
t_start = timing.micros() #us
28+
delayMicroseconds(1)
29+
t_end = timing.micros() #us
30+
print('dt (us) = ' + str(t_end - t_start))
31+
print('delayMicroseconds(2000)')
32+
for x in range(10):
33+
t_start = timing.micros() #us
34+
delayMicroseconds(2000)
35+
t_end = timing.micros() #us
36+
print('dt (us) = ' + str(t_end - t_start))
37+
38+
#using GS_timing
39+
print('\nusing GS_timing')
40+
print('timing.delayMicroseconds(1)')
41+
for x in range(10):
42+
t_start = timing.micros() #us
43+
timing.delayMicroseconds(1)
44+
t_end = timing.micros() #us
45+
print('dt (us) = ' + str(t_end - t_start))
46+
print('timing.delayMicroseconds(2000)')
47+
for x in range(10):
48+
t_start = timing.micros() #us
49+
timing.delayMicroseconds(2000)
50+
t_end = timing.micros() #us
51+
print('dt (us) = ' + str(t_end - t_start))
52+
53+
"""
54+
SAMPLE RESULTS ON MY WINDOWS 8.1 MACHINE:
55+
56+
using time.sleep
57+
delayMicroseconds(1)
58+
dt (us) = 2872.059814453125
59+
dt (us) = 886.3939208984375
60+
dt (us) = 770.4649658203125
61+
dt (us) = 1138.7698974609375
62+
dt (us) = 1426.027099609375
63+
dt (us) = 734.557861328125
64+
dt (us) = 10617.233642578125
65+
dt (us) = 9594.90576171875
66+
dt (us) = 9155.299560546875
67+
dt (us) = 9520.526611328125
68+
delayMicroseconds(2000)
69+
dt (us) = 8799.3056640625
70+
dt (us) = 9609.2685546875
71+
dt (us) = 9679.5439453125
72+
dt (us) = 9248.145263671875
73+
dt (us) = 9389.721923828125
74+
dt (us) = 9637.994262695312
75+
dt (us) = 9616.450073242188
76+
dt (us) = 9592.853881835938
77+
dt (us) = 9465.639892578125
78+
dt (us) = 7650.276611328125
79+
80+
using GS_timing
81+
timing.delayMicroseconds(1)
82+
dt (us) = 53.3477783203125
83+
dt (us) = 36.93310546875
84+
dt (us) = 36.9329833984375
85+
dt (us) = 34.8812255859375
86+
dt (us) = 35.3941650390625
87+
dt (us) = 40.010986328125
88+
dt (us) = 38.4720458984375
89+
dt (us) = 56.425537109375
90+
dt (us) = 35.9072265625
91+
dt (us) = 36.420166015625
92+
timing.delayMicroseconds(2000)
93+
dt (us) = 2039.526611328125
94+
dt (us) = 2046.195068359375
95+
dt (us) = 2033.8841552734375
96+
dt (us) = 2037.4747314453125
97+
dt (us) = 2032.34521484375
98+
dt (us) = 2086.2059326171875
99+
dt (us) = 2035.4229736328125
100+
dt (us) = 2051.32470703125
101+
dt (us) = 2040.03955078125
102+
dt (us) = 2027.215576171875
103+
104+
105+
SAMPLE RESULTS ON MY RASPBERRY PI VERSION 1 B+:
106+
107+
using time.sleep
108+
delayMicroseconds(1)
109+
dt (us) = 1022.0
110+
dt (us) = 417.0
111+
dt (us) = 407.0
112+
dt (us) = 450.0
113+
dt (us) = 2078.0
114+
dt (us) = 393.0
115+
dt (us) = 1297.0
116+
dt (us) = 878.0
117+
dt (us) = 1135.0
118+
dt (us) = 2896.0
119+
delayMicroseconds(2000)
120+
dt (us) = 2746.0
121+
dt (us) = 2568.0
122+
dt (us) = 2512.0
123+
dt (us) = 2423.0
124+
dt (us) = 2454.0
125+
dt (us) = 2608.0
126+
dt (us) = 2518.0
127+
dt (us) = 2569.0
128+
dt (us) = 2548.0
129+
dt (us) = 2496.0
130+
131+
using GS_timing
132+
timing.delayMicroseconds(1)
133+
dt (us) = 572.0
134+
dt (us) = 673.0
135+
dt (us) = 1084.0
136+
dt (us) = 561.0
137+
dt (us) = 728.0
138+
dt (us) = 576.0
139+
dt (us) = 556.0
140+
dt (us) = 584.0
141+
dt (us) = 576.0
142+
dt (us) = 578.0
143+
timing.delayMicroseconds(2000)
144+
dt (us) = 2741.0
145+
dt (us) = 2466.0
146+
dt (us) = 2522.0
147+
dt (us) = 2810.0
148+
dt (us) = 2589.0
149+
dt (us) = 2681.0
150+
dt (us) = 2546.0
151+
dt (us) = 3090.0
152+
dt (us) = 2600.0
153+
dt (us) = 2400.0
154+
155+
156+
"""

test_import.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
test.py
3+
By Gabriel Staples
4+
http://www.ElectricRCAircraftGuy.com
5+
-click "Contact me" at the top of my website to find my email address
6+
7+
13 Aug. 2016
8+
9+
"""
10+
11+
import GS_timing as timing
12+
13+
print(timing.micros())
14+
print(timing.millis())
15+
print('version = ' + timing.VERSION)
16+
17+
print(timing._constrain(10,11,15)) #11

0 commit comments

Comments
 (0)