File: Timing.rst

package info (click to toggle)
python-expyriment 0.7.0%2Bgit34-g55a4e7e-3.2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,504 kB
  • ctags: 2,094
  • sloc: python: 12,766; makefile: 150
file content (220 lines) | stat: -rw-r--r-- 9,912 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
Timing and empirical testing of Expyriment
==========================================

**How accurate is the timing in Expyriment?**

In general, the Expyriment clock can feature up to 1 ms accuracy. The exact 
timing, however, is subject to various factors, which we discuss here.

Time and compatibility issues can be conveniently tested using the 
:doc:`Expyriment test suite <Testsuite>`.


Stimulus presentation
---------------------

Visual
~~~~~~
Computer screens are updated according to their refresh rate. What happens then 
is that the whole screen is redrawn line by line all the time. For example, 
with a refresh rate of 60Hz, the screen is redrawn 60 times per second (1000ms) 
and the duration it takes to redraw it line by line is 16.66 ms (1000/60).
When attempting to redraw the screen while it is currently already being 
updated (the lines are drawn) the result might lead to artifacts, since the 
update occurs immediately, leading to parts of both, the new and the old screen 
content, being visible. What is even worse is that you will never know in which 
phase of the redrawing the new redraw was started. Thus, you cannot be sure 
when exactly the new content is fully visible on screen.

The first step towards getting around this problem is to synchronize the actual 
redraw to the vertical retrace of the screen. This means that a change in 
content will never happen immediately, but always only when the retrace is at 
the top left position. When synchronizing to the vertical retrace, the graphic 
card is told to update the screen the next time it starts redrawing the first 
line. While this will solve the problem of artifacts, you will still face the 
problem of not knowing when exactly something was visible on the screen, since 
the graphic card handles this synchronization itself in the background.

Solving this problem is the exact (and only) reason why Expyriment uses OpenGL 
by default. It allows to wait for the vertical retrace to actually happen 
before proceeding with the code that tells the graphics card to update the 
screen (this is also known as blocking on the vertical retrace). This means 
that whenever something should be presented on screen, no matter in which line 
the redraw is at this point in time, the graphic card will wait for the redraw 
to be in the first line and then present the stimulus. Since the code is 
blocking, the time Expyriment reports the stimulus to be presented on screen 
will always be the time when the redraw is starting at the first line. Coming 
back to the example of the small dot in the center of the screen: Expyriment 
will correctly report a longer presentation time when the redraw has been just 
over the center line when the screen update was issued.

*It is important to set your graphic card's driver settings to support 
synchronizing to the vertical retrace ("Sync to VBlank" or "V-sync") and to 
switch off any power saving schemes on the graphic card.*

**Test results**

We tested the visual timing by presented a white and a black fullscreen 
rectangle directly after each other. The brightness of the left upper edge of 
the screen was recorded using an optic sensor attached to an oscilloscope.  
After each screen presentation, a marker was send via the serial port to the 
oscilloscope. Testing was done on an Intel Core Duo PC with an Nvidia Quadro 
NVS 290 graphics card, running Microsoft Windows XP SP3. The monitor used was a 
Samsung SyncMaster 2233. Expyriment was running in OpenGL mode (default).

The results revealed:

* When presenting a stimulus, updating the screen does successfully block code 
  execution until the vertical retrace actually happens. This can be seen in 
  Figure 1, where the marker (yellow) lines up with the onset and offset of an 
  increase in brightness (turquoise) which represent the onset of the white and 
  the onset of the black screen, respectively.
* Presenting (preloaded) stimuli can accurately be done each refresh rate 
  (Figure 2).

Visual stimulus presentation is time locked to the vertical retrace.

.. image:: timing_visual1.png
   
Visual stimuli can be presented each refresh rate.

.. image:: timing_visual2.png


Audio
-----
Playing back audio is handled by PyGame. The present() and play() methods of 
auditory stimuli will return immediately. Since the audio stream has to be sent 
to the hardware, there will still be a delay before the audio can be heard.  
Unfortunately, the latency of the sound onset is not known by Expyriment.  
However, it is assumed to be relatively stable over time. Setting the audio 
buffersize to a smaller value than the default can decrease the delay, but 
might result in distorted audio.

*It is important to set your samplerate, bitdepth and audio buffersize 
correctly. Setting the buffersize too low will result in distorted audio!*

**Test results**

We tested the audio timing by repeatedly playing back a beep tone (a 1 second 
sine wave). The output of the sound card was measured by an oscilloscope.  
Before starting playback of the beep, a marker was send via the serial port to 
the oscilloscope.  Testing was done on an Intel Core Duo PC with a Soundblaster 
Audigy sound card, running Microsoft Windows XP SP3. In Expyriment, the 
samplerate was set to 44100 Hz, bitdepth to 16 bit and the buffersize equaled 128.

The results revealed:

* Audio playback was subject to a latency of maximally 20 ms. Figure 1 shows 
  the maximal measured latency between the start of the playback (yellow) and 
  the onset of the sound (turquoise).
* This latency was relatively stable with a jitter of 5 ms. Figure 2 shows the 
  minimal latency we could measure.

Maximal measured audio latency.

.. image:: timing_audio2.png

Minimal measured audio latency

.. image:: timing_audio1.png

Video
~~~~~

Video presentation is a tricky subject. In Expyriment, the present() method of 
a video stimulus will start playback and present the first (current) frame on 
the screen. Thus, visual onset of this frame will be synchronized with the 
vertical retrace (see visual stimulus presentation above). Each following frame 
has to be plotted on the screen and the screen has to be updated. The 
wait_end() method of a video stimulus will automatically present each frame on 
the screen until the video is over. When Expyriment is in OpenGL mode, the 
process of plotting each frame might take longer than one refresh rate which 
will result in dropping frames (e.g. frames not being presented at all). To 
control for this, the wait_end() method will report and log if any frames were 
dropped during video playback.

*Unfortunetly, right now, Expyriment can only handle MPEG-1 encoded videos!*

Measuring user input
--------------------

In Expyriment all inputs (keybard, mouse, gameport, serial port, parallel port) 
can be checked by directly polling them (via the wait() methods of the 
corresponding io object). This allows for the most accurate timing possible.  
Since Python wraps C functions for getting the system time, the accuracy is 
even more precise than milliseconds (which is the unit Expyriment uses).

Expyriment does *not* have a main event loop (i.e. it will not automatically 
check for any incoming events in the background)! This was a design decision, 
since we think that in 99% of all cases the time of the user input is specified 
in the design and thus know beforehand (e.g. a response after a stimulus 
onset). Adding an event loop would make things unnecessarily more complicated 
for those 99%.

However, we also thought of those cases that need to check user input during 
other operations: All events can manually be pushed from either Pygame's event 
cue (keyboard, mouse, joystick) or the operating system's buffer (serial port, 
parallel port) into an EventBuffer object. Doing this regularly is up to the 
user.

Keyboard
~~~~~~~~
Keyboards (PS2 and USB) are known to have poor timing accuracy. Usually these 
are in the range of several 100th of a second.

**Test results**

We tested the timing of a Logitec USB keyboard in Windows XP SP3 using optical 
tracking.  

Our results revealed:

* A timing accuracy between 20 and 26 ms.

Mouse
~~~~~
On most operating systems, USB mice are polled at a rate of 8 ms. Mice with 
special drivers might be set to poll more often.

**Test results**

We tested the mouse accuracy of a standard USB mouse on Windows XP SP3 by 
measuring the time between reported position changes.

Our results revealed:

* The expected standard accuracy of 8 ms.
* Using a Logitec G700 USB mouse with a dedicated driver, polling rates could A  
  be reduced, leading to an increased accuracy of 1 ms.

Serial port
~~~~~~~~~~~
The serial port is very accurate and thus suited for timing accurate 
measurements. If a computer does not have a serial port, USB-to-serial 
converter can be used (e.g. from Sweex or Keyspan). However, the timing 
accuracy of these depends on the implementation and drivers used!

*It is important to deactivate any additional FIFO buffers or delays, provided 
by the port driver!*

**Test results**

We tested the timing of a UART 16550A serial port (a real one, not a 
USB-to-serial converter!) on Windows XP SP3 by sending a byte to a connected 
loopback device which immediately sends the byte back. We then measured the 
time between sending and receiving. We repeated this process 1000 times.

Our results revealed:

* With a baudrate of 115200, the maximal measured time between sending and 
  receiving a byte was 0.283894736842 ms.
* With a baudrate of 19200, the maximal measured time between sending and 
  receiving a byte was 0.689593984962 ms.

Parallel port
~~~~~~~~~~~~~
The parallel port works by directly applying a current (writing) and measuring 
if a current is applied (sending) to several pins on the connector. Expyriment 
is only able to read from Acknowledge, Paper-Out and Selected pins!