如何实现高速,一致的采样?

时间:2012-05-23 09:58:23

标签: python

要考虑的那种应用是示波器或高速数据记录器。我有一个功能可以检索所需的信息,我只需要一次又一次地研究它,非常快速,高精度。

time.sleep()有一些限制,我认为不是这样的。

我已经研究了内置事件调度程序,但我认为它不够精确,并不能完全满足我的需求。

对此的要求是:

  • 高速采样。 10毫秒是最常被问到的。
  • 高精度间隔。在10ms时,10%的误差是可以接受的(±1ms)。
  • 相当低的CPU使用率,一些负载在10ms时是可以接受的,但是对于100ms间隔及更长时间,它应该小于~5%。我知道这是主观的,我想我所说的是占用CPU是不可接受的。
  • 理想情况下,计时器将以间隔时间初始化,然后在需要时启动。然后应该以正确的间隔一遍又一遍地调用所需的功能,直到定时器停止为止。
  • 它(不一定)只能在Windows机器上运行。

是否有任何现有的库满足这些要求?我不想重新发明轮子,但如果必须,我可能会使用Windows多媒体计时器(winmm.dll)。对此提出任何意见/建议吗?

2 个答案:

答案 0 :(得分:3)

我知道我在回答自己的问题时已经迟到了,但希望它会对某人有所帮助。

我为Windows多媒体计时器编写了一个纯粹作为测试的包装器。它似乎工作得很好,但代码没有经过全面测试,也没有得到优化。

mmtimer.py:

from ctypes import *
from ctypes.wintypes import UINT
from ctypes.wintypes import DWORD

timeproc = WINFUNCTYPE(None, c_uint, c_uint, DWORD, DWORD, DWORD)
timeSetEvent = windll.winmm.timeSetEvent
timeKillEvent = windll.winmm.timeKillEvent


class mmtimer:
    def Tick(self):
        self.tickFunc()

        if not self.periodic:
            self.stop()

    def CallBack(self, uID, uMsg, dwUser, dw1, dw2):
        if self.running:
            self.Tick()

    def __init__(self, interval, tickFunc, stopFunc=None, resolution=0, periodic=True):
        self.interval = UINT(interval)
        self.resolution = UINT(resolution)
        self.tickFunc = tickFunc
        self.stopFunc = stopFunc
        self.periodic = periodic
        self.id = None
        self.running = False
        self.calbckfn = timeproc(self.CallBack)

    def start(self, instant=False):
        if not self.running:
            self.running = True
            if instant:
                self.Tick()

            self.id = timeSetEvent(self.interval, self.resolution,
                                   self.calbckfn, c_ulong(0),
                                   c_uint(self.periodic))

    def stop(self):
        if self.running:
            timeKillEvent(self.id)
            self.running = False

            if self.stopFunc:
                self.stopFunc()

定期测试代码:

from mmtimer import mmtimer
import time

def tick():
    print("{0:.2f}".format(time.clock() * 1000))

t1 = mmtimer(10, tick)
time.clock()
t1.start(True)
time.sleep(0.1)
t1.stop()

以毫秒为单位的输出:

0.00
10.40
20.15
29.91
39.68
50.43
60.19
69.96
79.72
90.46
100.23

一次性测试代码:

from mmtimer import mmtimer
import time

def tick():
    print("{0:.2f}".format(time.clock() * 1000))

t1 = mmtimer(150, tick, periodic=False)
time.clock()
t1.start()

以毫秒为单位的输出:

150.17

从结果中可以看出,它非常准确。但是,这只是使用time.clock(),所以请用一点盐。

在使用10ms周期性定时器的长时间测试期间,我的旧双码3GHz机器的CPU使用率约为3%或更低。虽然机器闲置时似乎还在使用它,所以我说额外的CPU使用量是最小的。

答案 1 :(得分:2)

编辑:在编写下面的内容之后,我倾向于为python事件调度程序实现类似的测试。我不明白为什么你认为它不够准确。

像Linux这样的东西似乎在我的Linux下工作得很好(我没有理由认为它不适用于Windows)。每隔10ms,调用on_timer_event(),根据实时时钟打印出自上次呼叫以来的时间。这显示了计时器的近似精度。最后,打印出总时间以显示没有漂移。

下面的代码似乎存在一个问题,偶尔会出现虚假(短间隔)的事件。我不知道为什么会这样,但毫无疑问,通过一些游戏,你可以让它变得可靠。我认为这种方法是可行的方法。

import pygame
import time

pygame.init()
TIMER_EVENT = pygame.USEREVENT+1

pygame.time.set_timer(TIMER_EVENT, 10)

timer_count = 0
MAX_TIMER_COUNT = 1000

def on_timer_event():
    global last_time
    global timer_count

    new_time = time.time()

    print new_time - last_time
    last_time = new_time

    timer_count += 1

    if timer_count > MAX_TIMER_COUNT:
        print last_time - initial_time
        pygame.event.post(pygame.event.Event(pygame.QUIT, {}))

initial_time = time.time()
last_time = initial_time
while True:
    event = pygame.event.wait()
    if event.type == TIMER_EVENT:
        on_timer_event()

    elif event.type == pygame.QUIT:
        break