0

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.

user3666197
  • 1
  • 6
  • 50
  • 92
user9624737
  • 125
  • 1
  • 7

4 Answers4

3

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.

  • Could you kindly explain, how this approach ( proposing to use an unboundedly, out-of-control freely-drifting, TimeDOMAIN un-locked, absolute serial add-on delay ) adds to the **explicit requirement of : *"accurate"* sampling ?** – user3666197 Apr 13 '20 at 21:15
1

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] sampling
is 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

user3666197
  • 1
  • 6
  • 50
  • 92
CuCaRot
  • 1,208
  • 7
  • 23
  • 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] sampling is nowhere near a solution! – user3666197 Apr 14 '20 at 14:06
  • 1
    @user3666197 wow, a very clear explanation, I think I should delete this answer to avoid misleading in the future. Thank you! – CuCaRot Apr 15 '20 at 04:13
  • 1
    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 Apr 15 '20 at 09:54
1

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 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
user3666197
  • 1
  • 6
  • 50
  • 92
0

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).

Marc
  • 1,539
  • 8
  • 14
  • With all due respect, the `sleep()`-based proposal is an **awfully bad anti-pattern**, if signal-processing or control-system requirements are to get met. Using a similarly naive set of SLOC-s is possible in responsibility-free blog-posts, or in low grade school book examples, yet the O/P has explicitly asked for an **accurate sampling** which the proposed serial-chained `sleep()` is principally never capable to deliver. – user3666197 Apr 13 '20 at 21:23
  • @user3666197, I guess you're right about that. but seeing the context of the question it is quite well possible that this is the level of accuracy that was needed. Also the OP has the opportunity to change his question or comment if the answers are not to his liking ;) – Marc Apr 14 '20 at 08:10
  • Well, whether something is wrong *( or is not )* obviously **does not depend** on the ability of the person, who has asked the Question or who reads the Answer, does it? – user3666197 Apr 14 '20 at 08:24
  • I does not depend on the ability of the person, but depending on the type of program you create a solution can be correct given the circumstances. When someone just wants to read a value 100 times in 10 seconds and it does not matter if if the measurement timing is off by 0.1 seconds (can still be counted as accurate to user) the sleep solution can be the correct one. Accuracy comes in various levels right? – Marc Apr 14 '20 at 11:00
  • No, Sir, the **context** is important - the O/P context is a **domain of Control**, where the **Theory of Control** ( be it a set of instruments in automated experiment / data acquisition, the worse if not only sensors, but also efectors are to get sampled / commanded inside a Control-loop in certain 80 +/- 0 [ms] sample rate ). Software, as any other sort of design, I mean **responsible and only responsible** designs, cannot abstract from what gets rotten, if design diverges from a safe-mode, be it landing an Apollo crew on Moon, or collecting sampled data-points from a repeatable experiment. – user3666197 Apr 14 '20 at 11:49
  • I think you're missing my point that some people are not using this for applications that need "responsible and only responsible" designs. Your answer is probably a good one for someone that needs the most accurate manner to do things in python, so thank you for that information. However for someone that wants to do something simple your solution can be complicated to implement with respect to their very simple problem. – Marc Apr 14 '20 at 12:06
  • You will hardly accept to receive just 75.000 $, instead of contracted 100.000 $, will you? A 25 % difference is not receiving 1 $ from each 4 $. The very the same way your opinion moves away 25 % of mandatory samples. The task is clear - collect samples for 10 [s] at 80 [ms] == 12.5 [Hz] & no other sampling rate, i.e. 125 samples, evenly distributed, 80 [ms] one after another - **not** *a 100 or so* samples, *anywhere* within about that duration. This is a game of trying to downplay the facts & definition of the experiment, not a serious way to implement all defined properties in due fashion. – user3666197 Apr 14 '20 at 12:49
  • But I also don't want to calculate what my friends owe me up to 10 digits precisely when I pay for them at a restaurant, in which case a 10% loss would be fine with me. Again, for applications that depend on extreme accuracy you are right and people can find your answer, but not every application has this requirement. – Marc Apr 14 '20 at 13:24
  • :) sure, no one can or will speak about your own preferences towards any friends of yours, yet, **the task is clear & sound**: Sample @12.5 [Hz] next 10 [s]. Your approach is out of question principally wrong & dangerous to use - as an example, given the ["do sample record"]-part takes to execute who-knows-how-long (you might have noticed, that it is outside of your scope of control, isn't it?) be it 80+, 160+, 320+ [ms] end-to-end, the proposed code is more an attempt to commit an assisted suicide, than a fair & robust-enough solution (~having at least a minimum fair amount of responsibility) – user3666197 Apr 14 '20 at 13:49
  • Well not exactly. If you want to be precise about it "sample every 0.08 in 10 seconds" allows for the sampling to be in the range of ±11.76 to ±13.15 Hz for ±9.5 to ±10.5 seconds without further specification what is required. One can (easily) assume this is not what is meant, but that would be an assumption nonetheless. Also when someone (OP) proposes a method, you could derive some behaviour from the program from that attempt. Which in this case would mean it would definitely not be longer than 0.08 seconds. Also maybe not compare coding to suicide? – Marc Apr 14 '20 at 14:29