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
checkFixationOK
column 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)