I was given some very good hints in this forum about how to code a clock object in Python 2. I've got some code working now. It's a clock that 'ticks' at 60 FPS:
import sys
import time
class Clock(object):
def __init__(self):
self.init_os()
self.fps = 60.0
self._tick = 1.0 / self.fps
print "TICK", self._tick
self.check_min_sleep()
self.t = self.timestamp()
def init_os(self):
if sys.platform == "win32":
self.timestamp = time.clock
self.wait = time.sleep
def timeit(self, f, args):
t1 = self.timestamp()
f(*args)
t2 = self.timestamp()
return t2 - t1
def check_min_sleep(self):
"""checks the min sleep time on the system"""
runs = 1000
times = [self.timeit(self.wait, (0.001, )) for n in xrange(runs)]
average = sum(times) / runs
print "average min sleep time:", round(average, 6)
sort = sorted(times)
print "fastest, slowest", sort[0], sort[-1]
def tick(self):
next_tick = self.t + self._tick
t = self.timestamp()
while t < next_tick:
t = self.timestamp()
self.t = t
if __name__ == "__main__":
clock = Clock()
The clock does not do too bad, but in order to avoid a busy loop I'd like Windows to sleep less than the usual about 15 milliseconds. On my system (64-bit Windows 10), it returns me an average of about 15 / 16 msecs when starting the clock if Python is the only application that's running. That's way too long for a min sleep to avoid a busy loop.
Does anybody know how I can get Windows to sleep less than that value?
You can temporarily lower the timer period to the wPeriodMin value returned by timeGetDevCaps. The following defines a timer_resolution context manager that calls the timeBeginPeriod and timeEndPeriod functions.
import timeit
import contextlib
import ctypes
from ctypes import wintypes
winmm = ctypes.WinDLL('winmm')
class TIMECAPS(ctypes.Structure):
_fields_ = (('wPeriodMin', wintypes.UINT),
('wPeriodMax', wintypes.UINT))
def _check_time_err(err, func, args):
if err:
raise WindowsError('%s error %d' % (func.__name__, err))
return args
winmm.timeGetDevCaps.errcheck = _check_time_err
winmm.timeBeginPeriod.errcheck = _check_time_err
winmm.timeEndPeriod.errcheck = _check_time_err
#contextlib.contextmanager
def timer_resolution(msecs=0):
caps = TIMECAPS()
winmm.timeGetDevCaps(ctypes.byref(caps), ctypes.sizeof(caps))
msecs = min(max(msecs, caps.wPeriodMin), caps.wPeriodMax)
winmm.timeBeginPeriod(msecs)
yield
winmm.timeEndPeriod(msecs)
def min_sleep():
setup = 'import time'
stmt = 'time.sleep(0.001)'
return timeit.timeit(stmt, setup, number=1000)
Example
>>> min_sleep()
15.6137827
>>> with timer_resolution(msecs=1): min_sleep()
...
1.2827173000000016
The original timer resolution is restored after the with block:
>>> min_sleep()
15.6229814
Related
I’m working in python on a raspberry pi. I’m trying to send out a signal on a motor controller, and then receive a signal with a sensing hat after it pass through my plant (an RC filter in this case).
The important thing is I want to generate the output and read the input as close to simultaneously as possible. I was hoping to use multiprocessing to have a thread send the signal while the other read the incoming signal. But I keep getting confused on how threads work in python.
In short is it possible to do 2 different tasks with multiprocessing and then repeat those tasks (sending and reading a signal) until a condition is met. (like in a while loop)
(Edited with Code)
from __future__ import print_function
from PyQt5.QtWidgets import QAction
from pyqtgraph.Qt import QtGui, QtCore
from adafruit_motorkit import MotorKit
import pyqtgraph as pg
import sys
from sys import stdout
import numpy as np
from daqhats import mcc118, OptionFlags, HatIDs, HatError
from daqhats_utils import select_hat_device, enum_mask_to_string, \
chan_list_to_mask
from decimal import *
import math
import time
getcontext().prec = 3
total_samples_read = 0
READ_ALL_AVAILABLE = -1
channelData = np.zeros(4, dtype=float)
CURSOR_BACK_2 = '\x1b[2D'
ERASE_TO_END_OF_LINE = '\x1b[0K'
# for plotting data
########################################
scan_rate = 1000 # scan rate in hz
maxtime = 30 # second s to run for
Datatime = np.zeros(maxtime * scan_rate, dtype=float)#List of times when smaples are taken
Data1 = np.zeros(maxtime * scan_rate, dtype=float) #sampels taken
data_index = 0 # Maximum index of data points taken
dt = Decimal(1 / scan_rate) # difference in time between indexes of Datatime
display_index = 0 # maximum index of Data being displayed on plot
#################################
# variables for Data logger
##########################
is_scanning = False
channels = [0]
channel_mask = chan_list_to_mask(channels)
num_channels = len(channels)
samples_per_channel = 0
options = OptionFlags.CONTINUOUS
######################################
startedTime = 0 # time at program start
myTime = 0 # time since program started
try:
address = select_hat_device(HatIDs.MCC_118)
hat = mcc118(address)
except (HatError, ValueError) as err:
print('\n', err)
class MainWindow(pg.GraphicsWindow):
def __init__(self, *args, **kwargs):
super(pg.GraphicsWindow, self).__init__(*args, **kwargs)
self.delay = 30 #ms
self.quit = QAction("Quit", self)
self.quit.triggered.connect(self.clean_close)
self.timer = QtCore.QTimer()
self.timer.setInterval(self.delay)
self.timer.timeout.connect(self.update_plot)
# plots data and runs calibrate between trials
def update_plot(self):
global display_index, Datatime, Data1
kit.motor1.throttle = .4 + .2 * math.cos((time.time()-startedTime)* 2 * np.pi* 1) # 1hz sinusiod out of motor
if data_index < len(Data1):
Collect_Data()
plot.setXRange(0, 20, padding=0)
plot.setXRange(0, 20, padding=0)
curve.setData(Datatime[:display_index], Data1[:display_index])
display_index += 1
app.processEvents()
def clean_close(self):
self.close()
# starts data collection
def Collect_Data():
global is_scanning
"""
This function is executed automatically when the module is run directly.
"""
# Store the channels in a list and convert the list to a channel mask that
# can be passed as a parameter to the MCC 118 functions.
try:
# Select an MCC 118 HAT device to use.
# actual_scan_rate = hat.a_in_scan_actual_rate(num_channels, scan_rate)
# Configure and start the scan.
# Since the continuous option is being used, the samples_per_channel
# parameter is ignored if the value is less than the default internal
# buffer size (10000 * num_channels in this case). If a larger internal
# buffer size is desired, set the value of this parameter accordingly.
if not is_scanning:
hat.a_in_scan_start(channel_mask, samples_per_channel, scan_rate,
options)
is_scanning = True
try:
read_and_display_data(hat, num_channels)
except KeyboardInterrupt:
# Clear the '^C' from the display.
print(CURSOR_BACK_2, ERASE_TO_END_OF_LINE, '\n')
print('Stopping')
hat.a_in_scan_stop()
hat.a_in_scan_cleanup()
except (HatError, ValueError) as err:
print('\n', err)
# reads Data off of Hat and adds to Data1
def read_and_display_data(hat, num_channels):
global channelData, data_index, Datatime, Data1
total_samples_read = 0
read_request_size = READ_ALL_AVAILABLE
# When doing a continuous scan, the timeout value will be ignored in the
# call to a_in_scan_read because we will be requesting that all available
# samples (up to the default buffer size) be returned.
timeout = 5.0
# Read all of the available samples (up to the size of the read_buffer which
# is specified by the user_buffer_size). Since the read_request_size is set
# to -1 (READ_ALL_AVAILABLE), this function returns immediately with
# whatever samples are available (up to user_buffer_size) and the timeout
# parameter is ignored.
trigger = True
while trigger == True:
read_result = hat.a_in_scan_read(read_request_size, timeout)
# Check for an overrun error
if read_result.hardware_overrun:
print('\n\nHardware overrun\n')
break
elif read_result.buffer_overrun:
print('\n\nBuffer overrun\n')
break
samples_read_per_channel = int(len(read_result.data) / num_channels)
total_samples_read += samples_read_per_channel
# adds all data in buffer to data to be plotted.
count = 0
if samples_read_per_channel > 0:
index = samples_read_per_channel * num_channels - num_channels
while count < samples_read_per_channel:
for i in range(num_channels):
channelData[i] = read_result.data[index + i]
if data_index < len(Data1):
Data1[data_index] = channelData[0]
Datatime[data_index] = float(dt * Decimal(data_index))
data_index += 1
count += 1
trigger = False
stdout.flush()
if __name__ == '__main__':
app = QtGui.QApplication([])
win = MainWindow() # display window
plot = win.addPlot(1, 0)
curve = plot.plot()
win.show()
kit = MotorKit() # implements motor driver
kit.motor1.throttle = .4 # values 1 is 5v and 0 is 0 volts
startedTime = time.time()
# u = .2*math.cos(t * 2*np.pi*1)
win.timer.start()
sys.exit(app.exec_())
I have two small files called LoopTest.pyx as follows:
import time
t0= time.process_time()
class Loop():
def __init__(self, n0):
self.n0 = n0
def test(self):
cdef long long int a = 0
for i in range(self.n0):
a += i
self.a = a
self.t1 = time.process_time() - t0
print("Total is %f: and elapsed time is %f" %(self.a,self.t1))
and LoopTestPy.py as follows:
import time
t0= time.process_time()
class Loop():
def __init__(self, n0):
self.n0 = n0
def test(self):
a = 0
for i in range(self.n0):
a += i
self.a = a
self.t1 = time.process_time() - t0
print("Total is %f: and elapsed time is %f" %(self.a,self.t1))
I created a setup.py file as follows:
from distutils.core import setup
from Cython.Build import cythonize
setup(name='TestLoop',
ext_modules=cythonize("LoopTest.pyx"))
and built by running python setup.py build_ext --inplace.
when running the following test script:
from LoopTest import Loop
from LoopTestPy import Loop as PyLoop
n = 500000;
LPPy = PyLoop(n);
LP = Loop(n);
print('Python Loop:')
LPPy.test();
print('\n\nCython Loop:')
LP.test();
I get the following results:
Python Loop:
Total is 124999750000.000000: and elapsed time is 0.031250
Cython Loop:
Total is 124999750000.000000: and elapsed time is 0.046875
It looks as if Python loop is faster than Cython loop?! What am I doing wrong?
Thanks.
I am writing a simple python script that I need to scale to many threads. For simplicity, I have replaced the actual function I need to use with a matrix matrix multiply. I am having trouble getting my code to scale with the number of processors. Any advice to help me get the correct speedup would be helpful! My code and results are as follows:
import numpy as np
import time
import math
from multiprocessing.dummy import Pool
res = 4
#we must iterate over all of these values
wavektests = np.linspace(.1,2.5,res)
omegaratios = np.linspace(.1,2.5,res)
wavekmat,omegamat = np.meshgrid(wavektests,omegaratios)
def solve_for_omegaratio( ind ):
#obtain the indices for this run
x_ind = ind % res
y_ind = math.floor(ind / res)
#obtain the value for this run
wavek = wavektests[x_ind]
omega = omegaratios[y_ind]
#do some work ( I have replaced the real function with this)
randmat = np.random.rand(4000,4000)
nop = np.linalg.matrix_power(randmat,3)
#obtain a scalar value
value = x_ind + y_ind**2.0
return value
list_ind = range(res**2)
#Serial code execution
t0_proc = time.clock()
t0_wall = time.time()
threads = 0
dispersion = map( solve_for_omegaratio , list_ind)
displist = list(dispersion)
t1_proc = time.clock()
t1_wall = time.time()
print('serial execution')
print('wall clock time = ',t1_wall-t0_wall)
print('processor clock time = ',t1_proc-t0_proc)
print('------------------------------------------------')
#Using pool defaults
t0_proc = time.clock()
t0_wall = time.time()
if __name__ == '__main__':
pool = Pool()
dispersion = pool.map( solve_for_omegaratio , list_ind)
displist = list(dispersion)
t1_proc = time.clock()
t1_wall = time.time()
pool.close
print('num of threads = default')
print('wall clock time = ',t1_wall-t0_wall)
print('processor clock time = ',t1_proc-t0_proc)
print('------------------------------------------------')
# Using 4 threads
t0_proc = time.clock()
t0_wall = time.time()
threads = 4
if __name__ == '__main__':
pool = Pool(threads)
dispersion = pool.map( solve_for_omegaratio , list_ind)
displist = list(dispersion)
t1_proc = time.clock()
t1_wall = time.time()
pool.close
print('num of threads = ' + str(threads))
print('wall clock time = ',t1_wall-t0_wall)
print('processor clock time = ',t1_proc-t0_proc)
print('------------------------------------------------')
Results:
serial execution
wall clock time = 66.1561758518219
processor clock time = 129.16376499999998
------------------------------------------------
num of threads = default
wall clock time = 81.86436200141907
processor clock time = 263.45369
------------------------------------------------
num of threads = 4
wall clock time = 77.63390111923218
processor clock time = 260.66285300000004
------------------------------------------------
Because python has a GIL https://wiki.python.org/moin/GlobalInterpreterLock , "python-native" threads can't run execute truly concurrently and thus can't improve the performance of CPU-bound tasks like math. They can be used to parallelize IO bound tasks effectively (eg API calls which spend almost all their time waiting for network I/O). Forking separate processes with multiprocessing rather than dummy's thread-backed implementation will create multiple processes, not threads, which will be able to run concurrently ( at cost of significant memory overhead).
I found those three ways to get the Mouse Coordinates in Windows:
from ctypes import windll, Structure, c_long, byref
class POINT(Structure):
_fields_ = [("x", c_long), ("y", c_long)]
def MousePosition_ctypes():
pos = POINT()
windll.user32.GetCursorPos(byref(pos))
return {"x":pos.x,"y":pos.y}
import win32api
def MousePosition_win32api():
pos = win32api.GetCursorPos()
return {"x":pos[0],"y":pos[1]}
import win32gui
def MousePosition_win32gui():
flags, hcursor, (x,y) = win32gui.GetCursorInfo()
return {"x":x,"y":y}
What is the difference between them? Is there an advantage in using ctypes? Is pywin32 doing the same as ctypes here?
I made a timing test with 10^6 runs.
import timeit
print("MousePosition_ctypes ",timeit.repeat("MousePosition_ctypes()", "from __main__ import MousePosition_windll",number=10**6))
print("MousePosition_win32api",timeit.repeat("MousePosition_win32api()", "from __main__ import MousePosition_win32api",number=10**6))
print("MousePosition_win32gui",timeit.repeat("MousePosition_win32gui()", "from __main__ import MousePosition_win32gui",number=10**6))
Results:
MousePosition_ctypes [3.171214059203684, 3.0459668639906723, 3.0454502913267243]
MousePosition_win32api [1.9730332863218454, 1.9517156492346004, 2.072004962338813]
MousePosition_win32gui [1.787176519159173, 1.8011713842243182, 1.8127041221688245]
So the win32gui Version would be the fastest..
I also tested the usecase where it is good to get many cursor coordinates per second. So I measured the mean points per seconds over a minute for each method:
def store_mouse_track_ctypes(t):
t0 = time.time()
list = []
dt = 0
while dt < t:
t1 = time.time()
position = MousePosition_ctypes()
t2 = time.time()
dt = t2 - t0
list.append([t1,t2,position["x"],position["y"]])
return list
a = 60
l = store_mouse_track_ctypes(a)
print("store_mouse_track_ctypes",len(l)/a,"points per second.")
Results:
store_mouse_track_ctypes 169850.01666666666 points per second.
store_mouse_track_win32api 225232.46666666667 points per second.
store_mouse_track_win32gui 231186.85 points per second.
Again win32gui Version is a bit ahead.
Why is the win32gui version the "best"?
I'm trying to convert MIDI ticks/delta time to milliseconds and have found a few helpful resources already:
MIDI Delta Time Ticks to Seconds
How to convert midi timeline into the actual timeline that should be played
MIDI Time Code spec
MTC
The problem is I don't think I'm using this information correctly.
I've tried applying the formula Nik expanded:
[ 1 min 60 sec 1 beat Z clocks ]
| ------- * ------ * -------- * -------- | = seconds
[ X beats 1 min Y clocks 1 ]
using the metadata from this test MIDI file:
<meta message set_tempo tempo=576923 time=0>
<meta message key_signature key='Ab' time=0>
<meta message time_signature numerator=4 denominator=4 clocks_per_click=24 notated_32nd_notes_per_beat=8 time=0>
Like so:
self.toSeconds = 60.0 * self.t[0][2].clocks_per_click / (self.t[0][0].tempo * self.t[0][2].denominator) * 10
This initially looks ok, but then it seems to drift.
Here is a basic runnable example using Mido and pygame (assuming pygame plays back correctly):
import threading
import pygame
from pygame.locals import *
from mido import MidiFile,MetaMessage
music_file = "Bee_Gees_-_Stayin_Alive-Voice.mid"
#audio setup
freq = 44100 # audio CD quality
bitsize = -16 # unsigned 16 bit
channels = 2 # 1 is mono, 2 is stereo
buffer = 1024 # number of samples
pygame.mixer.init(freq, bitsize, channels, buffer)
pygame.mixer.music.set_volume(0.8)
class MIDIPlayer(threading.Thread):
def __init__(self,music_file):
try:
#MIDI parsing
self.mid = MidiFile(music_file)
self.t = self.mid.tracks
for i, track in enumerate(self.mid.tracks):
print('Track {}: {}'.format(i, track.name))
for message in track:
if isinstance(message, MetaMessage):
if message.type == 'time_signature' or message.type == 'set_tempo' or message.type == 'key_signature':
print message
self.t0 = self.t[0][3:len(self.t[0])-1]
self.t0l = len(self.t0)
self.toSeconds = 60.0 * self.t[0][2].clocks_per_click / (self.t[0][0].tempo * self.t[0][2].denominator) * 10
print "self.toSeconds",self.toSeconds
#timing setup
self.event_id = 0
self.now = pygame.time.get_ticks()
self.play_music(music_file)
except KeyboardInterrupt:
pygame.mixer.music.fadeout(1000)
pygame.mixer.music.stop()
raise SystemExit
def play_music(self,music_file):
clock = pygame.time.Clock()
try:
pygame.mixer.music.load(music_file)
print "Music file %s loaded!" % music_file
except pygame.error:
print "File %s not found! (%s)" % (music_file, pygame.get_error())
return
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
# check if playback has finished
millis = pygame.time.get_ticks()
deltaMillis = self.t0[self.event_id].time * self.toSeconds * 1000
# print millis,deltaMillis
if millis - self.now >= deltaMillis:
print self.t0[self.event_id].text
self.event_id = (self.event_id + 1) % self.t0l
self.now = millis
clock.tick(30)
MIDIPlayer(music_file)
What the above code should do is print the correct lyric at the correct time based on the midi file, yet it drifts over time.
What's the correct way of converting MIDI delta time to seconds/milliseconds ?
Update
Based on CL's helpful answer I've updated the code to use ticks_per_beat from the header. Since there is a single set_tempo meta message, I am using this value throughout:
import threading
import pygame
from pygame.locals import *
from mido import MidiFile,MetaMessage
music_file = "Bee_Gees_-_Stayin_Alive-Voice.mid"
#audio setup
freq = 44100 # audio CD quality
bitsize = -16 # unsigned 16 bit
channels = 2 # 1 is mono, 2 is stereo
buffer = 1024 # number of samples
pygame.mixer.init(freq, bitsize, channels, buffer)
pygame.mixer.music.set_volume(0.8)
class MIDIPlayer(threading.Thread):
def __init__(self,music_file):
try:
#MIDI parsing
self.mid = MidiFile(music_file)
self.t = self.mid.tracks
for i, track in enumerate(self.mid.tracks):
print('Track {}: {}'.format(i, track.name))
for message in track:
# print message
if isinstance(message, MetaMessage):
if message.type == 'time_signature' or message.type == 'set_tempo' or message.type == 'key_signature' or message.type == 'ticks_per_beat':
print message
self.t0 = self.t[0][3:len(self.t[0])-1]
self.t0l = len(self.t0)
self.toSeconds = 60.0 * self.t[0][2].clocks_per_click / (self.t[0][0].tempo * self.t[0][2].denominator) * 10
print "self.toSeconds",self.toSeconds
# append delta delays in milliseconds
self.delays = []
tempo = self.t[0][0].tempo
ticks_per_beat = self.mid.ticks_per_beat
last_event_ticks = 0
microseconds = 0
for event in self.t0:
delta_ticks = event.time - last_event_ticks
last_event_ticks = event.time
delta_microseconds = tempo * delta_ticks / ticks_per_beat
microseconds += delta_microseconds
print event.text,microseconds/1000000.0
self.delays.append(microseconds/1000)
#timing setup
self.event_id = 0
self.now = pygame.time.get_ticks()
self.play_music(music_file)
except KeyboardInterrupt:
pygame.mixer.music.fadeout(1000)
pygame.mixer.music.stop()
raise SystemExit
def play_music(self,music_file):
clock = pygame.time.Clock()
try:
pygame.mixer.music.load(music_file)
print "Music file %s loaded!" % music_file
except pygame.error:
print "File %s not found! (%s)" % (music_file, pygame.get_error())
return
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
# check if playback has finished
millis = pygame.time.get_ticks()
# deltaMillis = self.t0[self.event_id].time * self.toSeconds * 1000
deltaMillis = self.delays[self.event_id]
# print millis,deltaMillis
if millis - self.now >= deltaMillis:
print self.t0[self.event_id].text
self.event_id = (self.event_id + 1) % self.t0l
self.now = millis
clock.tick(30)
MIDIPlayer(music_file)
The timing of the messages I print based on the time converted to milliseconds looks much better. However, after a few seconds it still drifts.
Am I correctly converting MIDI ticks to milliseconds and keep track of passed milliseconds in the update while loop ?
This how the conversion is made:
self.delays = []
tempo = self.t[0][0].tempo
ticks_per_beat = self.mid.ticks_per_beat
last_event_ticks = 0
microseconds = 0
for event in self.t0:
delta_ticks = event.time - last_event_ticks
last_event_ticks = event.time
delta_microseconds = tempo * delta_ticks / ticks_per_beat
microseconds += delta_microseconds
print event.text,microseconds/1000000.0
self.delays.append(microseconds/1000)
and this is how the check if a 'cue' was encountered as time passes:
millis = pygame.time.get_ticks()
deltaMillis = self.delays[self.event_id]
if millis - self.now >= deltaMillis:
print self.t0[self.event_id].text
self.event_id = (self.event_id + 1) % self.t0l
self.now = millis
clock.tick(30)
I'm not sure if this implementation converts MIDI delta ticks to milliseconds incorrectly, incorrectly check if millisecond based delays pass or both.
First, you have to merge all tracks, to ensure that the tempo change events are processed properly. (This is probably easier if you convert delta times to absolute tick values first; otherwise, you'd have to recompute the delta times whenever an event is inserted between events of another track.)
Then you have to compute, for each event, the relative time to the last event, like in the following pseudocode. It is important that the computation must use relative times because the tempo could have changed at any time:
tempo = 500000 # default: 120 BPM
ticks_per_beat = ... # from the file header
last_event_ticks = 0
microseconds = 0
for each event:
delta_ticks = event.ticks - last_event_ticks
last_event_ticks = event.ticks
delta_microseconds = tempo * delta_ticks / ticks_per_beat
microseconds += delta_microseconds
if event is a tempo event:
tempo = event.new_tempo
# ... handle event ...
You might want to increase the frame rate. On my system, increasing clock.tick(30) to clock.tick(300) gives good results. You can measure this by printing how much your timing is off:
print self.t0[self.event_id].text, millis - self.now - deltaMillis
With 30 ticks the cues are lagging 20 to 30 millisecond behind. With 300 ticks they are at most 2 milliseconds behind. You might want to increase this even further.
Just to be safe you should run python with the -u switch to prevent stdout from buffering (this might be unnecessary, since lines end with newline).
I have a hard time determining the timing, but judging from the "Ah ha ha ha"'s it seems to be correct with these changes.