How to accurately sample in python - python

At work, I have a need: to do sampling every 0.08 seconds in 10 seconds.
I use while loop but it fails.
import time
start_t =time.time()
while time.time() -start_t <=10:
if float(time.time() -start_t) % float(0.08) == 0:
"""do sample record""
finally, I got no data at all, I think the if float(time.time() -start_t) % float(0.08) == 0: does not work.
I am confused how to set the condition to enter the sampling code.

The easiest way is to use time.sleep:
from time import sleep
for i in range(125):
"""do sample record"""
sleep(0.08)
You probably get no data because you collect the time only at discrete moments. In these moments, they will never be perfect multiples of 0.08.

Q : "How to accurately sample in python"
At work ( Chongqing ),I have a need: to do sampling every 0.08 seconds in 10 seconds.
Given the python is to be used, the such precise sampling will need a pair of signal.signal()-handlers on the unix-systems,
import signal
#------------------------------------------------------------------
# DEFINE HANDLER, responsible for a NON-BLOCKING data-acquisition
#------------------------------------------------------------------
def aSIG_HANDLER( aSigNUM, aPythonStackFRAME ):
... collect data ...
return
#------------------------------------------------------------------
# SET THE SIGNAL->HANDLER MAPPING
#------------------------------------------------------------------
signal.signal( signal.SIGALM, aSIG_HANDLER )
#------------------------------------------------------------------
# SET THE INTERVAL OF SIGNAL-ACTIVATIONS
#------------------------------------------------------------------
signal.setitimer( signal.ITIMER_REAL, seconds = 0, # NOW WAIT ZERO-SECONDS
interval = 0.08 # FIRE EACH 80 [ms]
)
#------------------------------------------------------------------
# ... more or less accurately wait for 10 seconds, doing NOP-s ...
#------------------------------------------------------------------
#----------------------------------------------------------------
# AFTER 10 [s] turn off the signal.ITIMER_REAL activated launcher
#----------------------------------------------------------------
signal.setitimer( signal.ITIMER_REAL, seconds = 0, # NOW WAIT ZERO-SECONDS
interval = 0.0 # STOP SENDING SIGALM-s
)
or,for a Windows-based systems,there is a chance to tweak ( and fine-tune up to a self-correcting, i.e. non-drifting ) Tkinter-based sampler as shown in this answer.
class App():
def __init__( self ):
self.root = tk.Tk()
self.label = tk.Label( text = "init" )
self.label.pack()
self.sampler_get_one() # inital call to set a scheduled sampler
self.root.lower() # hide the Tk-window from GUI-layout
self.root.mainloop()
def sampler_get_one( self ):
# \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
#
# DEMO to show real plasticity of the Tkinter scheduler timing(s)
#
# /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
... review a drift of the activation + adapt the actual delay for a next .after()
# SET .after() vv-----------# re-calculate this value to adapt/avoid drifting
self.root.after( 80, # re-instate a next scheduled call,
self.sampler_get_one
) # .after a given ( self-corrected ) delay in [ms]
#-------------------------------#-NOW--------------------------------------------
... acquire ... data ... # best in a non-blocking manner & leave ASAP

You use float number divide by float number, and time.time() will return a long decimal number so you get no data because your result always 0.00001234 or something like that. I think you should use round to get 2 decimal number
temp = time.time()-start_t
if round(temp,2) % 0.08 == 0:
"""do sample record"""
However, this script will return about 27000 result in 10 second. Because you will have 0.08, 0.081,0.082,etc and they all do your recording work.
So I think you should work with Maximilian Janisch solution (using sleep function) is better. I just want to explain why you reach no solution.
Hope this helpful!
EPILOGUE :
With all due respect, the proposed code is awfully dangerous & mis-leading.Just test how naive it gets : 8.00 % 0.08 yields 0.07999999999999984 that is by no means == 0,while the if-condition ought be served & sample taken, if it were not for the (known) trap in real-numbers IEEE-754 handling.So as to see the scope of the disaster, try :sum( [ round( i * 0.08, 2 ) % 0.08 == 0 for i in range( 126 ) ] )+ compare it with 125-samples the task was defined above to acquire.Get 8 samples instead of 125 # regular, 12.5 [Hz] samplingis nowhere near a solution! – user3666197 22 hours ago
#user3666197 wow, a very clear explanation, I think I should delete this answer to avoid misleading in the future. Thank you! – Toby 5 hours ago
Better do not remove the Answer, as it documents what shall never be done,which is of a specific value to the Community- best to mention the rationale, not to use this kind of approaches in any real-life system.The overall lesson is positive- all learned a next step towards better system designs. I wish you all the best, man! – user3666197 4 mins ago

this will probably never exactly be true as checking for equality with floats like this will need to be very precise.
try doing something like:
start_t =time.time()
looped_t = start_t
while time.time() - start_t <= 10:
if time.time() - looped_t >= 0.08:
looped_t = time.time()
"""do sample record""
The sleep answer from Maximillian is fine as well, except if your sampling takes a significant amount of time (several hundreds of a second) then you will not stay near the 10 second requirement.
It also depends on what you prioritize as this method will at most provide 124 samples instead of the exact 125 you would expect (and do get with the sleep function).

Related

Using tkinter after to produce an animation

Background Information - I'm attempting to create somewhat of animation for a frame object with TKinter with the following code:
from tkinter import Frame, Tk, Label, Button
import time
def runAnim():
for width in range(0, 200):
app.after(5000, lambda width = width: test_label.config(width=width))
app = Tk()
app.geometry("500x500")
test_label = Frame(bg="#222", width=0)
test_label.pack(side="left", fill="y")
test_button = Button(text="toggle", command=lambda: runAnim() )
test_button.pack(side="right")
The problem is that it this is not producing the desired behaviour. My understanding is that this should gradually increase the width every 5 seconds, however the 0-200 range seems to complete within these 5 seconds, rather than it being an increased width of 1 every 5 seconds.
Any solutions would be appreciated!
That after(5000, …) means 5 seconds after right now, as after is being called, not 5 seconds after some future point in time that tkinter can only guess by reading your mind.
So, you're just creating 200 callbacks, and scheduling them all to run 5 seconds from now. That's obviously not what you want, but it's what you're asking for, so that's what you get.
In general, you can't do loops like this in event-based programming. What you need to do is turn the loop inside-out: each step does one iteration, then schedules the next call for the next one.
The fully-general transformation looks like this:
def runAnim():
iterwidth = iter(range(0, 200))
stepAnim(iterwidth)
def stepAnim(iterwidth):
try:
width = next(iterwidth)
except StopIteration:
return
test_label.config(width=width))
app.after(5000, stepAnim, iterwidth)
While that works for any iterable, when you're just iterating over numbers, it's usually a bit nicer to turn the for loop into an explicit counter, which is easier to invert. (Yes, that's the opposite of the "usual for instead of while and += 1 when you're not inverting things. The difference is that here, we can't access the magic of for or while, and while is a lot less magical, and therefore easier to invert.)
def runAnim():
stepAnim(0, 200):
def stepAnim(width, maxWidth):
test_label.config(width=width))
width += 1
if width < maxWidth:
app.after(5000, stepAnim, width, maxWidth)
However, in this particularly simple case, you might be able to get away with scheduling 200 callbacks, ranging from 5 to 1000 seconds into the future:
def runAnim():
for width in range(0, 200):
app.after(5000 * width, lambda width = width: test_label.config(width=width))
This might cause the timer to drift a lot more badly, or it might even choke up the scheduler and add lag to your program, but it's at least worth trying.
Speaking of drift:
Back at the start, I mentioned that after(5000, …) means 5 seconds after right now.
An after can fire a bit late. As the docs say: "Tkinter only guarantees that the callback will not be called earlier than that; if the system is busy, the actual delay may be much longer."
So, what happens if it fires after, say, 5.2 seconds? Then the second tick happens 5 seconds after that, at 10.2 seconds, not at 10 seconds. And if they're all firing a bit late, that adds up, so by the end, we could be 20 seconds behind.
Worse, what if after fires exactly at 5.0 seconds, but the Label.config takes 0.2 seconds to run? Then we're absolutely guaranteed to be 20 seconds behind. (Plus any additional error from after itself.)
If this matters, you need to keep track of the desired "next time", and wait until then, not until 5 seconds from whenever it is now. For example:
import datetime as dt
def runAnim():
stepAnim(0, 200, dt.datetime.now() + dt.timedelta(seconds=5):
def stepAnim(width, maxWidth, nextTick):
test_label.config(width=width))
width += 1
if width < maxWidth:
now = dt.datetime.now()
delay = (nextTick - now).total_seconds() * 1000
nextTick += dt.timedelta(seconds=5)
app.after(delay, stepAnim, width, maxWidth, nextTick)

How to count "times per second" in a correct way?

Goal: I would like to see how many times python is able to print something per 1 second.
For educational purposes I'm trying to make a script that shows how many times per every second a random module will appear in a loop. How to do it in a fastest pythonic way?
At first, to count seconds I wrote this code:
import time
sec = 0
while True:
print(sec)
time.sleep(1)
sec += 1
But this one seems slower than a real seconds.
So I decided to use local seconds. Also, before continue my script I wanted to count how many times python will print 'you fool' manually, so I wrote following code:
import time
def LocalSeconds():
local_seconds = time.gmtime()[5:-3]
local_seconds = int(local_seconds[0])
return local_seconds
while True:
print(LocalSeconds(), 'you fool')
Output:
first second - 14 times per second;
next second - 13 times;
next second - 12 times, etc. Why it goes slower?
Where I end / stuck right now:
import time, random
def RandomNumbers():
return random.randint(3,100)
def LocalSeconds():
local_seconds = time.gmtime()[5:-3]
local_seconds = int(local_seconds[0])
return local_seconds
def LocalSecondsWithPing():
local_seconds_ping = time.gmtime()[5:-3]
local_seconds_ping = int(local_seconds[0:1])
return local_seconds_ping
record_seconds = []
record_seconds_with_ping = []
while True:
record_seconds.append(LocalSeconds())
record_seconds_with_ping.append(LocalSecondsWithPing())
if record_seconds == record_seconds_with_ping:
RandomNumbers()
del record_seconds_with_ping[0]
del record_seconds[-1]
Also, I guess I need to use "for" loop, not "while"? How to do this script?
Counting a single second won't give you a good result. The number of prints in a single second may vary depending on things like other threads currently running on your system (for the OS or other programs) and may be influenced by other unknown factor.
Consider the followind code:
import calendar
import time
NumOfSeconds=100 #Number of seconds we average over
msg='Display this message' #Message to be displayed
count=0 #Number of time message will end up being displayed
#Time since the epoch in seconds + time of seconds in which we count
EndTime=calendar.timegm(time.gmtime()) + NumOfSeconds
while calendar.timegm(time.gmtime())<EndTime: #While we are not at the end point yet
print(msg) #Print message
count=count+1 #Count message printed
print(float(count)/NumOfSeconds) #Average number of prints per second
Here calendar.timegm(time.gmtime()) gives us the time in seconds since the epoch (if you don't know what that is, read this. But basically it's just a fixed point in time most computer system now days use as a reference point.
So we set the EndTime to that point + the number of seconds we want to average over. Then, in a loop, we print the message we want to test and count the number of times we do that, between every iteration checking that we are not past the end time.
Finally we print the average number of times per seconds that we printed the message. This helps with the fact that we may end up start counting near the end of a whole second since the epoch, in which case we won't actually have a whole second to print messages, but just a fraction of that. If we make sure NumOfSeconds is large enough, that error addition will be small (for example, for NumOfSeconds=100 that error is ~1%).
We should note that the actual number would also depend on the fact that we are constantly testing the current time and counting the number of prints, however, while I haven't tested that here, it is usually the case that printing to the screen takes significantly longer time than those operations.

detecting keyboard overrun in tkinter event handler when autorepeating to avoid lag

In my app I want to allow the user to scroll through images by holding down an arrow key. Not surprisingly with larger images the pc can't keep up, and builds up a potentially large buffer that carries on being processed after the key is released.
None of this is unexpected, and my normal answer is just to check the timestamp in the event against the current time and discard any events that are more than (say) .2 seconds old. This way the backlog can never get too large.
But tkinter uses some random timebase of events so that comparing with time.time() is meaningless, and I can't find a function to get hold of tkinter's own clock. I'm sure its in there, it's just most of the pythonised tkinter documentation is a bit naff, and searching for time or clock isn't helping either.
def plotprev(self,p):
if time.time() - p.time > .2:
return
Sadly this test always returns true, where is tkinter's pseudo clock to be found?
Any other method will be complex in comparison.
well it's nor very nice, but it isn't too tedious and seems to work quite well: (with a little bit of monitoring as well)
def checklag(self,p):
if self.lasteventtime is None: #assume first event arrives with no significant delay
self.lasteventtime = p.time
self.lasteventrealtime = time.time()
self.lagok=0
self.lagfail=0
return True
ptdiff = (p.time-self.lasteventtime) / 1000
rtdiff = time.time() - self.lasteventrealtime
lag = rtdiff-ptdiff
if lag < .3:
self.lagok += 1
if self.lagok %20 == 0:
print("lagy? OK: %d, fail: %d" %(self.lagok, self.lagfail))
return True
else:
self.lagfail += 1
return False

condition parameter adjustment

I try to use 'IF' in python in order to achieve the algorithm that can automatically ajust the value of a parameter in 'IF' according to some stock trasactions.
if self.sellcount==0 and int(time.time())-self.programstarttime>600:
if cur_sum/total_sum>0.15:
Other Code
else:
if cur_sum/total_sum>0.35:
Other Code
I try to achieve that if my algorithm do not sell any stock for 10 minutes, the algorithm can automatically change the condition from 0.35 to 0.15. However, the code above will change from 0.15 to 0.35 after selling stocks for one time. I want the code to keep 0.15 after selling stocks for one time.
I'd like to start with a disclaimer to be careful, stock trading is not this easy and you can lose a lot of money with a simple algorithm (just as you can with a complex one)
However, this is also a nice example to understand how to deal with running a program over time in Python and understanding conditional logic.
There are a few basic constructs you'll want to know for this. The first concept is that to keep track of time constantly in your program, you likely want to put your code in an infinite loop. That will keep your programming doing what you want until you are done. This can be done like this:
while True:
Now that you have this setup, we just need to keep track of time. This can be done easily by setting a variable and incrementing it by how long you wait between iterations. However, we still need to track time. Python has a nice sleep function implemented in the time module. This function causes your program to pause for a number of seconds that you desire and then resume going through the rest of your code.
from time import sleep
last_sold_stock_time = 0
wait_time = 1
while True:
# <Condition Code goes here>
# This is in seconds
sleep(wait_time)
# Keep track of how much time has passed.
last_sold_stock_time += wait_time
Now, you just need to change your condition value based on the time. The full code will probably end up looking something like this:
from time import sleep
# The number of seconds since last bought a stock, assumes start is 0
last_sold_stock_time = 0
# This is in seconds
wait_time = 1
# ten minutes in seconds
ten_minutes = 600
while True:
# Figure out these values however you do
cur_sum = 0
total_sum = 1
if last_sold_stock_time <= ten_minutes:
condition_value = 0.35
else:
condition_value = 0.15
if cur_sum/total_sum > condition_value:
# Do something
pass
sleep(wait_time)
last_sold_stock_time += wait_time

Trouble Maintaining Constant Update Rate

Developing a process that should read data at consistent intervals. The time period to read data varies depending on the network. I thought this should be straightforward but I can never get consistent timing. Looking for a more consistent and stable system that responds well to network speed variability.
currently I am using a model that follows
|<--read data-->|<--post process-->|<--sleep x seconds to maintain period-->|
|<------------------------------known data rate---------------------------->|
My code does something like
data_rate = 5 # Hz
while 1:
# read in data
rd_start = time.time()
data = getdata()
rd_stop = time.time()
# Post processing
pp_start = time.time()
rate = 1.0/(rd_start - oldstart) if oldstart else data_rate
old_start = rd_start
print rate
post_process(data)
pp_stop = time.time()
sleep_time = 1.0/data_rate - ((rd_stop-rd_start) + (pp_stop-pp_start))
sleep_time = sleep_time if sleep_time>0 else 0
time.sleep(sleep_time)
I also have some logic that changes the update rate (data_rate) if the network is having trouble meeting that speed (sleep times are consistently negative) but that is working correctly.
For some reason my data rate is never consistent (And runs at about 4.92 Hz when it stabilizes). Also this method is pretty unstable. What is the better way to do this? Threading.Timers() comes to mind?
Could the consistent offset in frequency be caused by errors with time.sleep()?
How accurate is python's time.sleep()?

Categories

Resources