2012-11-02 5 views
5

Sto leggendo i dati seriali con un ciclo while. Tuttavia, non ho alcun controllo sulla frequenza di campionamento.Come posso assicurarmi che un ciclo while di Python impieghi una quantità di tempo particolare per l'esecuzione?

Il codice stesso sembra richiedere 0,2 secondi per l'esecuzione, quindi so che non potrò andare più veloce di così. Ma mi piacerebbe essere in grado di controllare con precisione quanto più lento campionamento.

Mi sento come se potessi farlo usando "sleep", ma il problema è che c'è il potenziale che in punti diversi il ciclo stesso impiegherà più tempo a leggere (a seconda esattamente di ciò che viene trasmesso sui dati seriali), quindi il codice dovrebbe fare il bilancio.

Ad esempio, supponiamo di voler campionare ogni 1 s e il ciclo richiede da 0,2 a 0,3 secondi per l'esecuzione. Il mio codice deve essere abbastanza intelligente per dormire per 0,8 secondi (se il ciclo richiede 0,2 secondi) o 0,7 secondi (se il ciclo richiede 0,3 secondi).

import serial 
import csv 
import time 

#open serial stream 
    while True: 

     #read and print a line 
     sample_value=ser.readline() 
     sample_time=time.time()-zero 
     sample_line=str(sample_time)+','+str(sample_value) 
     outfile.write(sample_line) 
     print 'time: ',sample_time,', value: ',sample_value 
+0

Ho fatto questo genere di cose prima di usare/dev/rtc come trigger in un reattore asincrono. Si innesca a intervalli precisi indipendentemente dal tempo di misurazione. Ma potresti anche usare itimer. – Keith

risposta

8

Basta misurare il tempo di esecuzione il codice prende ogni iterazione del ciclo, e sleep di conseguenza:

import time 

while True: 
    now = time.time()   # get the time 
    do_something()    # do your stuff 
    elapsed = time.time() - now # how long was it running? 
    time.sleep(1.-elapsed)  # sleep accordingly so the full iteration takes 1 second 

Naturalmente non al 100% perfetto (forse fuori un millisecondo o l'altro di volta in volta) , ma immagino sia abbastanza buono.


Un altro approccio bello sta usando twisted s' LoopingCall:

from twisted.internet import task 
from twisted.internet import reactor 

def do_something(): 
    pass # do your work here 

task.LoopingCall(do_something).start(1.0) 
reactor.run() 
1

All'inizio del ciclo controllare se è trascorso il tempo appropriato. In caso contrario, continue.

# Set up initial conditions for sample_time outside the loop 
sample_period = ??? 
next_min_time = 0 
while True: 
    sample_time=time.time()-zero 
    if sample_time < next_min_time: 
     continue 
    #read and print a line 
    sample_value=ser.readline() 
    sample_line=str(sample_time)+','+str(sample_value) 
    outfile.write(sample_line) 
    print 'time: ',sample_time,', value: ',sample_value 
    next_min_time = sample_time + sample_period 
+2

Stai riempiendo il tempo di attesa con un ciclo vuoto. Piuttosto usa 'time.sleep()' per consentire allo scheduler di riempire il tempo di attesa con l'elaborazione di altri thread/processi. –

1

Un metodo piuttosto elegante, è che si sta lavorando su UNIX: utilizzare la libreria di segnale

Il codice:

import signal 


def _handle_timeout(): 
    print "timeout hit" # Do nothing here 

def second(count): 
    signal.signal(signal.SIGALRM, _handle_timeout) 
    signal.alarm(1) 
    try: 
     count += 1 # put your function here 
     signal.pause() 

    finally: 
     signal.alarm(0) 
     return count 


if __name__ == '__main__': 

    count = 0 
    count = second(count) 
    count = second(count) 
    count = second(count) 
    count = second(count) 
    count = second(count) 

    print count 

E i tempi:

[email protected]:~/Bureau$ time python timer.py 
5 

real 0m5.081s 
user 0m0.068s 
sys 0m0.004s 

Due avvertimenti però: funziona solo su * nix e non è multithread-safe.

Problemi correlati