function DATA = test_exp(subject) % Cogent demo experimental script % expects the input 'subject' (string variable), % runs an experiment, % and returns the data in the strucure DATA % written by christian ruff december 2006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % change into directory containing experimental files cd('C:\test_experiment\exp_files'); format bank; % this improves readability of matlab number outputs % This program can be run from the command line, specifying the subject's name: % e.g. >> test_exp( 'Elliot' ) % Alternatively it can be run without parameters, just by hitting F5 (RUN). % In this case, data are saved to a default file 'TEST.MAT' and 'TEST.log'. % if nargin == 0 % 'nargin' returns the number of parameters specified by the user subject = 'TEST'; % if there were no parameters, just use a default subject name elseif exist( subject ,'file')~=0; % otherwise (if there was a subject specified)... % check whether a file with that name exists already disp('file for this subject exists already! aborting...') clear all; return; % abort experiment, rather than overwrite existing data!!! end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Set up data structure for control and logging of experiment % This structure should contain every detail necessary to reconstruct the % sequence and timing of the experiment, and the responses. % It's convenient to keep all the relevant experimental parameters together near % the top of the script. % subject details DATA.subject = subject; % when was the script started? DATA.when_start = datestr(now,30); % location of input file containing list of stimuli DATA.inputList = 'C:\test_experiment\exp_files\inputList.txt'; % timing of trials DATA.dur_pict = 1000; % presentation time of stimulus (ms) DATA.dur_resp = 2000; % duration after offset of stimulus in which subject has time to respond (ms) DATA.ITI = 1000; % inter-trial interval (ms) % read in input file containing a list of stimuli % TEXTREAD reads from the inputList file. Here it expects to find two columns: % ('%d') one column of decimal condition codes -> read into condition_codes variable, then % ('%s') a column of strings indicating the filenames of pictures -> stimulusFile variable [DATA.condition_codes DATA.stimulus] = textread( DATA.inputList , '%d%s'); % the total number of pictures to be presented determines the number of trials DATA.n_trials = length(DATA.condition_codes); % initialise random number generator % It is absolutely crucial to do this before calling any other randomizing % functions (otherwise you get the same sequence each time you start MATLAB) !!!!!! rand('seed',sum(100*clock)); % seeds the random number generator with the clock time % create a randomized vector specifying the order of stimuli DATA.random_order = randperm(DATA.n_trials); % randperm generates a random sequence of 1 to n non-repeating digits % create data strucure for experiment, used in storing data for each trial DATA.exp_data = zeros(DATA.n_trials,7); % initialize data array DATA.exp_data_columns = {'condition','stimulus','time_present','time_keypress','key','RT','corr'}; % this helps remember what the data mean! % update the randomised condition codes and picture codes in the DATA DATA.exp_data(:,1) = DATA.condition_codes(DATA.random_order); DATA.exp_data(:,2) = DATA.random_order; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % COGENT: CONFIGURATION % display parameters screenMode = 0; % 0 for small window, 1 for full screen, 2 for second screen if attached screenRes = 2; % 800 x 600 resolution white = [1 1 1]; % foreground colour (optional) black = [0 0 0]; % background colour (optional) fontName = 'Helvetica'; fontSize = 20; % font parameters (optional) number_of_buffers = 5; % how many offscreen buffers to create % call config_... to set up cogent environment, before starting cogent config_display(screenMode, screenRes, black ,white, fontName, fontSize, number_of_buffers); % open graphics window config_log( subject ); % open log file with subject's name config_sound(2,16,44100,10); % sound configuration: stereo/mono, number of bits, sample frequency, number of buffers config_keyboard; % this enables collection of keyboard responses %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % START COGENT MODE: this hands control to cogent, and starts cogent timing start_cogent; % time is now on zero %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % EXECUTE COGENT COMMANDS % load feedback sounds loadsound('correct.wav',1); % load correct wav file into buffer 1 loadsound('wrong.wav',2); % load wrong wav file into buffer 2 % put instruction text into the screen buffer 1 (this doesn't display it yet) preparestring('Hello - Welcome to the experiment!',1,0,100); preparestring('Press <space> to start or <esc> to abort!',1,0,0); % prepare fixation cross in middle of buffer 2 (this doesn't display it yet) preparestring('+',2); % now show buffer 1 with instruction text DATA.t_first_screen = drawpict(1); % define the keys for the subject's responses keys = getkeymap; % getkeymap command outputs a list of codes assigned to each key by cogent DATA.controlKeys = [keys.Space keys.Escape]; % define key codes for space and escape for controlling program DATA.key_famous = keys.F; % key for Famous DATA.key_nonfamous = keys.N; % key for Non-famous [k,t,n] = waitkeydown(inf, DATA.controlKeys); % wait for ever ('inf') until one of the control keys is pressed if k(1) == keys.Escape % if escape is pressed, abort experiment logstring('...aborted'); % print out error message and write to log stop_cogent; % exits Cogent mode clear all; return; % exit from this function end % now start trial loop DATA.t_start_experiment = time; % record the time of the beginning of the trial sequence % trial loop: continue until maximum number of trials is reached for trial = 1 : DATA.n_trials trial_start = time; % record the current time for trial timing % trial starts with a fixation cross, from buffer 2 drawpict(2); % pick the next value from the randomized sequence defined previously which_row = DATA.random_order(trial); % this value is then used as an index to select the stimuli and their corresponding condition codes condition = DATA.condition_codes(which_row); % pick the condition code for this trial using the randomized index stimulus = DATA.stimulus{which_row}; % get the stimulus for this trial using the randomized index. % NOTE the use of braces {which_trial} ... % The next statement (loadpict) expects a character array as input. But DATA.stimulus contains a cell array. Indexing this with normal % brackets will just return one of the cells in this array: % DATA.stimulus(which_trial) -> 'stimulusName'. This will generate an error if used in loadpict. % To actually extract the string inside the cell, use braces instead % DATA.stimulus{which_trial} -> stimulusName % load picture and fixation cross on top of it into buffer 3 loadpict(stimulus, 3); preparestring('+',3); % wait for the Inter-Trial Interval to end wait(DATA.ITI) % we could also use 'waituntil( current_time + DATA.ITI )'; this would take into % account the delay of loading the pictures etc. since the trial began % clear keyboard buffer to avoid garbage from previous keypresses clearkeys; % cogent will store all keypresses from now on until 'readkeys' below % show picture in buffer 3 time_present = drawpict(3); % put up picture and record the time at which this happens % COGENT logging logstring(['trial ' num2str(trial) ', condition ' num2str(condition) ', picture ' stimulus]); % wait until end of presentation time, then remove picture wait( DATA.dur_pict ); % We could also use waituntil( time_present + DATA.dur_pict ) if exact timing mattered; drawpict(4); % put up blank display created by default in buffer 4 % wait a period for subjects to respond, then read responses and log them wait( DATA.dur_resp ); % here we could also use waituntil( time_present + DATA.dur_pict + DATA.dur_resp) if exact timing mattered; readkeys; % this command retrieves all keyboard events since the last call to clear keys above [k,t,n] = getkeydown([DATA.key_famous DATA.key_nonfamous 52]); % this command outputs all keydown events for the specified keys % -> k: key value, t: time, n: number of keypresses % logging: columns 'condition','stimulus','time_present','time_keypress','key','RT','corr' DATA.exp_data(trial,1) = condition; % condition code DATA.exp_data(trial,2) = which_row; % which row of the input file DATA.exp_data(trial,3) = time_present; % time at which stimulus was presented if isempty(k) % if there was no key response record dummy values DATA.exp_data(trial,[4:7]) = -99; % no response logstring('---'); % cogent log else % if there was a key response record data DATA.exp_data(trial,4) = t(1); % time of first key onset DATA.exp_data(trial,5) = k(1); % key code of first keypress DATA.exp_data(trial,6) = t(1) - DATA.exp_data(trial,3); % Response Time % determine if response was correct if (condition == 1 & DATA.exp_data(trial,5) == DATA.key_famous) | ... % "if we're in the first condition and the famous key was pressed... or... (condition == 2 & DATA.exp_data(trial,5) == DATA.key_nonfamous) ... % ... if we're in the second condition and the non-famous key was pressed ... then DATA.exp_data(trial,7) = 1; % ... set the value in the 'correct' column to 1" end % write out response values to cogent log and also command window logstring(['response ' num2str(k(1)) ', RT ' num2str(DATA.exp_data(trial,6)) ', corr ' num2str(DATA.exp_data(trial,7))]); end % check for abort if any( k==52 ) % "are any of the key-codes recorded in k equal to 52 (escape key)?" logstring('... aborted'); % write error to log stop_cogent; clear all; % if so, abort return; % exit this program end % give feedback if DATA.exp_data(trial,7) == 1 % if this trial was correct playsound(1); % play 'correct' sound else playsound(2); % else play 'error' sound end % save data save( subject, 'DATA' ); % save the whole structure to the subject's data file end n_correct = sum( DATA.exp_data(:,end)==1 ); % counts up the correct trials while ignoring -99 dummies p_correct = ( n_correct / DATA.n_trials ) * 100; % calculate percentage of correct out of the total number of trials logstring(['Percent Correct ' num2str(p_correct)]); % log this % end of block message to subject preparestring('Thank you',5,0,100); preparestring(['You were correct on ' num2str(p_correct) ' percent of trials!'],5,0,0); drawpict(5); wait(3000); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Return to MATLAB mode stop_cogent; clear all; return