- 59.4 cm width, 57 cm distance
- 1920 x 1080, 60 Hz, 92 ppi
- 18 deg (one-sided) vertical max
What are you trying to achieve?:
- A circle (3x3), moving at 0.2 deg / frame vertically, that is capable of switching directions when hitting edge of screen. Its size makes it hit the edge at 15 deg, thus it must switch directions when
Target.pos >= 15 or Target.pos <= -15
What did you try to make it work?:
moveSpeed = 0.2
###begin routine; can ignore, purely randomizing target
if Position == [-19.5, 0]:
Target = leftAnnuli
leftStop = 1200
rightStop = 60
if Position == [19.5, 0]:
Target = rightAnnuli
rightStop = 1200
leftStop = 60
#positions of R/L annuli
movingUp = None
direction = randint(0, 2)
if direction == 0:
moveSpeed *= -1
###each frame - the guts
if frameN >= 61:
Target.pos += moveSpeed
Target._needVertexUpdate = True
if Target.pos >= 15 or Target.pos <= -15:
moveSpeed *= -1
print('Switching frame - ', frameN)
if Target.pos == 3:
print('3 = ', frameN)
if Target.pos == 9:
print('9 = ', frameN)
if Target.pos == 15:
print('15 = ', frameN)
What specifically went wrong when you tried that?:
- Through logging its positions, it seems that the movement is out of sync with the frames. Not sure if that’s a good analysis of the problem - don’t know how else to word it.
It should be hitting 3 deg at the 75th frame, 9 deg at 105th, 15 deg at 135th. Instead, it seems to be out of sync slightly in the beginning, then later the incongruence is greatly magnified, likely through switching directions. Note that it never logs 3 deg or 15 deg, since it seems to skip over those.
This doesn’t occur if I set the speed to 0.25 deg / frame. See these logs:
Could it be that my frame rate isn’t truly 60 Hz? Data file indicates that it flies around 59.9xx. Anyone know how to deal with this?
There is a general principle here that computers aren’t perfect at dealing with floating point numbers. They are natively built to deal with integers (at heart, binary). So while you can always compare two integers exactly, you can’t expect to reliably compare two floating point values.
i.e. a computer will store an integer of
3 exactly. So you can do tests like this reliably:
if number_of_targets == 3: # comparing two integers
# do something every time
But a floating point value of
3.0 may actually be stored as
2.999999999997 or similar. It will vary depending on your platform and the series of calculations taken to reach it. So never try to test two floating point values for equality. Instead, take the absolute difference between the two and see if it is below some acceptable minimum threshold.
e.g. instead of:
if Target.pos == 3.0:
try something like:
if abs(Target.pos - 3.0) <= 0.0001:
The size of the threshold is situation-dependent. In this case, an angle smaller than one ten thousandth of a degree is not meaningful, so we can use that as a (relatively high) threshold.
Note that the deviations you are seeing are absolutely infinitesimal fractions of the width of a pixel. The target will be displayed exactly where you intend it to be, as its displayed location will effectively be rounded to the nearest integer pixel value. There is not only no perceptible difference between a stimulus intended to be displayed at
3.0 degrees and one whose coordinate is
3.00000000000000004 degrees, but also no physical difference, due to the quantised nature of a pixel-based display.
This issue of floating point precision is not a restriction unique to PsychoPy or Python, but common to all computing systems.
Hey, thank you. That is some great insight and it’s completely fixed the discrepancies. I really appreciate your help.
Glad that helped. The frustrating thing about this is that it does sometimes work (as you found when stepping by
0.25 instead of
0.2) - it is the unpredictability that can make this issue hard to track down. Everyone falls for it at some stage.
Socratic paradox hits me every time I post here…