| MOCS {OPI} | R Documentation |
Method of Constant Stimuli (MOCS)
Description
MOCS performs either a yes/no or n-interval-forced-choice Method of Constant Stimuli test
Usage
MOCS(
params = NA,
order = "random",
responseWindowMeth = "constant",
responseFloor = 1500,
responseHistory = 5,
keyHandler = function(correct, ret) return(list(seen = TRUE, time = 0, err = NULL)),
interStimMin = 200,
interStimMax = 500,
beep_function,
makeStim,
stim_print,
...
)
Arguments
params |
A matrix where each row is
|
order |
Control the order in which the stimuli are presented.
|
responseWindowMeth |
Control time perimeter waits for response.
|
responseFloor |
Minimum response window (for any |
responseHistory |
Number of past yeses to average to get response window
(only used if |
keyHandler |
Function to get a keyboard input and returns as for |
interStimMin |
Regardless of response, wait |
interStimMax |
Regardless of response, wait |
beep_function |
A function that takes the string |
makeStim |
A helper function to take a row of |
stim_print |
A function that takes an |
... |
Extra parameters to pass to the opiPresent function. |
Details
Whether the test is yes/no or forced-choice is determined by the number of columns
in params. The code simply presents all columns from 5 onwards and collects a
response at the end. So if there is only 5 columns, it is a yes/no task. If there are 6
columns it is a 2-interval-forced-choice. Generally, an nIFC experiment has 4+n columns in
params.
Note that when the order is "random", the number of trials in the test will be
the sum of the 3rd column of params. When the order is "fixed", there is
only one presentation per row, regardless of the value in the 3rd column of params.
If a response is received before the final trial in a nIFC experiment, it is ignored.
If the checkFixationOK function is present in a stimulus, then it is called after each
presentation, and the result is “anded” with each stimulus in a trial to get a TRUE/FALSE
for fixating on all stimuli in a trial.
Value
Returns a data.frame with one row per stimulus copied from params with extra columns
appended: checkFixation checks, and the return values from opiPresent()
(see example). These last values will differ depending on which
machine/simulation you are running (as chosen with chooseOpi().
column 1: x
column 2: y
column 3: location number
column 4: number of times to repeat this stim
column 5: correct stimulus index
column 6: TRUE/FALSE was fixating for all presentations in this trial according to
checkFixationOKcolumn 7...: columns from params
...: columns from opiPresent return
References
A. Turpin, P.H. Artes and A.M. McKendrick. "The Open Perimetry Interface: An enabling tool for clinical visual psychophysics", Journal of Vision 12(11) 2012.
See Also
Examples
# For the Octopus 900
# Check if pupil centre is within 10 pixels of (160,140)
checkFixationOK <- function(ret) return(sqrt((ret$pupilX - 160)^2 + (ret$pupilY - 140)^2) < 10)
# Return a list of opi stim objects (list of class opiStaticStimulus) for each level (dB) in
# p[5:length(p)]. Each stim has responseWindow BETWEEN_FLASH_TIME, except the last which has
# rwin. This one assumes p is on old Octopus 900 dB scale (0dB == 4000 cd/m^2).
makeStim <- function(p, rwin) {
BETWEEN_FLASH_TIME <- 750 # ms
res <- NULL
for(i in 5:length(p)) {
s <- list(x=p[1], y=p[2], level=dbTocd(p[i],4000/pi), size=0.43, duration=200,
responseWindow=ifelse(i < length(p), BETWEEN_FLASH_TIME, rwin),
checkFixationOK=NULL)
class(s) <- "opiStaticStimulus"
res <- c(res, list(s))
}
return(res)
}
################################################################
# Read in a key press 'z' is correct==1, 'm' otherwise
# correct is either 1 or 2, whichever is the correct interval
#
# Return list(seen={TRUE|FALSE}, time=time, err=NULL))
# seen is TRUE if correct key pressed
################################################################
## Not run:
if (length(dir(".", "getKeyPress.py")) < 1)
stop('Python script getKeyPress.py missing?')
## End(Not run)
keyHandler <- function(correct, ret) {
return(list(seen=TRUE, time=0, err=NULL))
ONE <- "b'z'"
TWO <- "b'm'"
time <- Sys.time()
key <- 'q'
while (key != ONE && key != TWO) {
a <- system('python getKeyPress.py', intern=TRUE)
key <- a # substr(a, nchar(a), nchar(a))
print(paste('Key pressed: ',key,'from',a))
if (key == "b'8'")
stop('Key 8 pressed')
}
time <- Sys.time() - time
if ((key == ONE && correct == 1) || (key == TWO && correct == 2))
return(list(seen=TRUE, time=time, err=NULL))
else
return(list(seen=FALSE, time=time, err=NULL))
}
################################################################
# Read in return value from opipresent with F310 controller.
# First param is correct, next is 1 for left button, 2 for right button
# Left button (LB) is correct for interval 1, RB for interval 2
# correct is either 1 or 2, whichever is the correct interval
#
# Return list(seen={TRUE|FALSE}, time=time, err=NULL))
# seen is TRUE if correct key pressed
################################################################
F310Handler <- function(correct, opiResult) {
z <- opiResult$seen == correct
opiResult$seen <- z
return(opiResult)
}
################################################################
# 2 example beep_function
################################################################
## Not run:
require(beepr)
myBeep <- function(type='None') {
if (type == 'correct') {
beepr::beep(2) # coin noise
Sys.sleep(0.5)
}
if (type == 'incorrect') {
beepr::beep(1) # system("rundll32 user32.dll,MessageBeep -1") # system beep
#Sys.sleep(0.0)
}
}
require(audio)
myBeep <- function(type="None") {
if (type == 'correct') {
wait(audio::play(sin(1:10000/10)))
}
if (type == 'incorrect') {
wait(audio::play(sin(1:10000/20)))
}
}
## End(Not run)
################################################################
# An example stim_print function
################################################################
## Not run:
stim_print <- function(s, ret) {
sprintf("%4.1f %2.0f",cdTodb(s$level,10000/pi), ret$seen)
}
## End(Not run)