function runstim(Hnd) %#ok global Par %global parameters global LPStat %das status values global STIM Par.TrainSession = false; % set to false for recording sessions ! % it will allow more control over file-naming % files will get a default name if set to true (see line 52 and later) %Difficulty ================================================= % CK20140128: Timing is taken from par file but overwritten by % CK20140128: values from the settings file in this runstim load('PAR_HICKEY.mat'); % load the default parameter-file % (maybe we can remove this later, but default Tracker relies on some settings) LOG.DefMonkeyName = 'Duvel'; % default monkey name used in creating a filename % can be overruled in filename when it's set through the dialog % Load settings file LOG.SettingsFile = 'HICKEY_SETTINGS_Duvel2'; eval(LOG.SettingsFile); LOG.SettingsFileFull = fullfile(pwd,[LOG.SettingsFile '.m']); Par.Trlcount = 0; %reset trialcount to 0 % Every time you press run, Tracker will generate a unique list of stimuli % and conditions. If we continue the trialcount over repeated runs the % references to conditions won't work anymore. THis automatically means % that for every time you press run, there will be a separate log file % saved. There should also be a matching TDT file, so always stop and start % TDT and Tracker together. % Save stuff in this version cc=clock; % get time info from the system clock LOG.datestr=[sprintf('%04d',cc(1)) sprintf('%02d',cc(2)) ... sprintf('%02d',cc(3))]; % YYYYMMDD LOG.timestr=[sprintf('%02d',cc(4)) sprintf('%02d',cc(5)) ... sprintf('%02d',round(cc(6)))]; % HHMMSS % Create a random number in the largest range that can be encoded using 14 bits % We will send this as a word bit to TDT as an extra check when matching up % our TDT data-file and the Tracker-log. THis is in addition to consistent % filenaming. LOG.uniqueID = round(rand(1).*2^14); dasword(LOG.uniqueID); pause(.05); % make sure the word is received dasclearword(); WordsSent=1; %keep track of how many words are sent so we back-check TDT against the log LOG.Words(WordsSent)=LOG.uniqueID; %collect all the words that are sent to TDT % Create a filename Monkeyname_YYYYMMDD_B# if Par.TrainSession fnLead = [LOG.DefMonkeyName '_' LOG.datestr '_TRAIN' ]; % Training- and Recording sessions will be saved under different names else fnLead = [LOG.DefMonkeyName '_' LOG.datestr '_B' ]; % this filename construction is convention for TDT as well end % look for log files with this date and find the lowest new block nr % will only check for files that follow the filename convention % MonkeyName_YYYYMMDD_Bx.mat (where x=blocknr) cd LOG; % go to log folder prevfiles=dir([fnLead '*']); % Assuming the last file has the highest blocknr % as it should when naming conventions are followed if ~isempty(prevfiles) CurrBlockNr = str2num(prevfiles(end).name(end))+1; %#ok<*ST2NM> else CurrBlockNr = 1; end cd .. % create an input dialog for setting the filename % suggest a default name based on the files that are already present LOG.FileNameSug = [fnLead sprintf('%03d',CurrBlockNr)]; if ~Par.TrainSession LOG.FileName = inputdlg(... 'Start a new TDT block and set filename for Tracker-log consistently\n',... 'Filename',1,{LOG.FileNameSug}); % take input name on 'ok', suggested name on 'cancel' if ~isempty(LOG.FileName) LOG.FileName = LOG.FileName{1}; else LOG.FileName = LOG.FileNameSug; end else LOG.FileName = LOG.FileNameSug; end % Create an empty screen BG = STIM.BackCol; %background Color cgflip(BG(1), BG(2), BG(3)) %flip the videobuffer nf=1; %keep track of number of flips nfr=100; %refresh tracker every nfr frame flips %timing ==================================================== % Timing from the Settings-file are used PREFIXT = STIM.PreFixT; % time allowed to initiate fixation FIXT = STIM.FixT; % duration to hold fixation before stimuli appear TARGT = STIM.KeepFixT; % Duration to hold fixation while stimuli are on the screen RACT = STIM.ReacT; % Time allowed to select a target after target onset SACCT= STIM.SaccT; % Allowed saccade time between leaving fixwin and entering targwin ISI = STIM.ISI; % interstimulus interval ISI_R = STIM.ISI_RAND; % max random addition to ISI ERRT = STIM.ErrT; % punishment addition to ISI after error trial %Stimulus information ====================================== %Fixation dot size and location in pixels FixPosPix = STIM.FixPos*Par.PixPerDeg; px = FixPosPix(1); py = FixPosPix(2); Fsz = STIM.FixSz.*Par.PixPerDeg; TargSz = STIM.TarSz.*Par.PixPerDeg; %Windowing information ===================================== % Info will be read from the settings file upon 'run' % but can be altered in the parameters tab Par.FixWdDeg = STIM.FixWinSz(1); Par.FixHtDeg = STIM.FixWinSz(2); Par.TargWdDeg = STIM.TarWinSz(1); Par.TargHtDeg = STIM.TarWinSz(2); %Make targets & distractors ---------------------- targx = STIM.TarPos(:,1)'.*Par.PixPerDeg; % x-coordinates targy = STIM.TarPos(:,2)'.*Par.PixPerDeg; % y-coordinates %////YOUR STIMULATION CONTROL LOOP ///////////////////////////////// Hit = 2; Par.ESC = false; %escape has not been pressed Par.Distcount = 0; %also count the number of times the distractor was selected TrialNr=0; % this is the index to rows in the triallist STIM.TrialList % it will increase when a new trial needs to be defined and stay the same % if a trial needs to be repeated. NB! this is not the same as the trial % count, which keep track of the number of started trials LOG.TotRew = 0; %keep track of earned total reward (excluding manual rewards) while ~Par.ESC %Pretrial %SETUP YOUR STIMULI FOR THIS TRIAL if Par.Drum && Hit ~= 2 %if drumming and this was an error trial %just redo with current settings else TrialNr=TrialNr+1; % CK20140128: This whole procedure is unflexible % CK20140128: I re-wrote it completely TrialTypeNo=STIM.TrialList(TrialNr,1); % be careful with referencing via Par.Trlcount % control window setup % 0 = fixation, 2 = target WIN=zeros(STIM.TarN+1,5); WIN(1,:)=[Par.PixPerDeg*STIM.FixPos(1),Par.PixPerDeg*STIM.FixPos(2),... Par.PixPerDeg*Par.FixWdDeg, Par.PixPerDeg*Par.FixHtDeg, 0]; for i=1:STIM.TarN WIN(i+1,:)=[Par.PixPerDeg*STIM.TarPos(i,1),Par.PixPerDeg*STIM.TarPos(i,2), ... Par.PixPerDeg*Par.TargWdDeg, Par.PixPerDeg*Par.TargHtDeg, 1]; end % which is the target? WIN(STIM.TrialTypes(TrialTypeNo,1)+1,5)=2; Par.WIN = WIN'; end %///////////////////////////////////////////////////////////////////// %START THE TRIAL %set control window positions and dimensions refreshtracker(1) %for your control display SetWindowDas %for the dascard Abort = false; %whether subject has aborted before end of trial % sent trial number as word dasword(TrialNr); % TrialNr is the row index in STIM.TrialList for the prepared % next trial. In this row STIM.TrialList defines the stimuus array as: % # STIM.TrialList(TrialNr,1) = TrialType, which in turn indexes a % row in STIM.TrialTypes where c1 is target location, c2 is target % shape (1=circle, 2=square), and c3 is distractor location % # STIM.TrialList(TrialNr,2) = Index to STIM.ColSchemes that % defines the RGB values for target, distractor and non-targets % # STIM.TrialList(TrialNr,3) = Index to STIM.RewardValues to % dissociate between high and low rewarded trials. WordsSent=WordsSent+1; LOG.Words(WordsSent)=TrialNr; %///////// EVENT 0 START FIXATING////////////////////////////////////// cgellipse(px,py,Fsz,Fsz,STIM.FixCol_NoGo,'f') %the red fixation dot on the screen cgflip(BG(1), BG(2), BG(3)) nf=nf+1; dasreset(0); %test enter fix window % 0 enter fix window % 1 leave fix window % 2 enter target window %subject has to start fixating central dot Par.SetZero = false; %set key to false to remove previous presses %Par.Updatxy = 1; %centering key is enabled Time = 1; Hit = 0; while Time < PREFIXT && Hit == 0 %dasrun(5) [Hit Time] = DasCheck; %#ok<*NCOMMA> %retrieve position values and plot on Control display if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end %disp( [num2str(hitbreak) ' enter ' num2str(toc)]) %///////// EVENT 1 KEEP FIXATING or REDO //////////////////////////////////// if Hit ~= 0 %subjects eyes are in fixation window keep fixating for FIX time dasreset(1); %set test parameters for exiting fix window Time = 1; Hit = 0; while Time < FIXT && Hit== 0 %Check for 10 ms %dasrun(5) [Hit Time] = DasCheck; %retrieve eyechannel buffer and events, plot eye motion, if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end if Hit ~= 0 %eye has left fixation to early %possibly due to eye overshoot, give another chance dasreset(0); Time = 1; Hit = 0; while Time < PREFIXT && Hit == 0 %dasrun(5) [Hit Time] = DasCheck; %retrieve position values and plot on Control display if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end if Hit ~= 0 %subjects eyes are in fixation window keep fixating for FIX time dasreset( 1); %test for exiting fix window Time = 1; Hit = 0; while Time < FIXT && Hit == 0 %Check for 10 ms % pause(0.005) %dasrun(5) [Hit Time] = DasCheck; if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end else Hit = -1; %the subject did not fixate end end else Hit = -1; %the subject did not fixate end % LPP = dasrun 10); % setdatatype(LPP, 'int32Ptr', 2, 1) % display(LPP.Value) %///////// EVENT 2 DISPLAY STIMULUS ////////////////////////////////////// if Hit == 0 %subject kept fixation, display stimulus Par.Trlcount = Par.Trlcount + 1; %counts total number of trials for this session % a trial is only counted when prefix is fullfilled % as long as no stimulus is shown, it's still the same trial % since this trial is started and the trial count updated: % log extensive info for this trial LOG.Trial(Par.Trlcount).TrialNr = TrialNr; LOG.Trial(Par.Trlcount).TrialCnt = Par.Trlcount; LOG.Trial(Par.Trlcount).TrialTypeNo = TrialTypeNo; LOG.Trial(Par.Trlcount).WordsSent = WordsSent; LOG.Trial(Par.Trlcount).LastWord = LOG.Words(WordsSent); LOG.Trial(Par.Trlcount).TarPos = STIM.TrialTypes(STIM.TrialList(TrialNr,1),1); LOG.Trial(Par.Trlcount).TarShape = STIM.TrialTypes(STIM.TrialList(TrialNr,1),2); LOG.Trial(Par.Trlcount).DistPos = STIM.TrialTypes(STIM.TrialList(TrialNr,1),3); LOG.Trial(Par.Trlcount).ColScheme = STIM.TrialList(TrialNr,2); LOG.Trial(Par.Trlcount).RewValue = STIM.RewardValues(STIM.TrialList(TrialNr,3)); % draw stimuli for s = 1:STIM.TarN if s==STIM.TrialTypes(TrialTypeNo,1) %this is the target if STIM.TrialTypes(TrialTypeNo,2) == 1 % target is circle cgellipse(targx(s),targy(s),... TargSz,TargSz,STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColCorr,'f'); else % target is rect cgrect(targx(s),targy(s),... sqrt(pi.*((TargSz/2).^2)),sqrt(pi.*((TargSz/2).^2)),... STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColCorr); end elseif s==STIM.TrialTypes(TrialTypeNo,3) %this is the salient non-target if STIM.TrialTypes(TrialTypeNo,2) == 1 % target is circle cgrect(targx(s),targy(s),... sqrt(pi.*((TargSz/2).^2)),sqrt(pi.*((TargSz/2).^2)),... STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_Sal); else % target is rect cgellipse(targx(s),targy(s),... TargSz,TargSz,STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_Sal,'f'); end else % normal non-target if STIM.TrialTypes(TrialTypeNo,2) == 1 % target is circle cgrect(targx(s),targy(s),... sqrt(pi.*((TargSz/2).^2)),sqrt(pi.*((TargSz/2).^2)),... STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_NonSal); else % target is rect cgellipse(targx(s),targy(s),... TargSz,TargSz,STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_NonSal,'f'); end end end cgellipse(px,py,Fsz,Fsz,STIM.FixCol_NoGo,'f') %the red fixation dot on the screen FO = cgflip(BG(1), BG(2), BG(3)); nf=nf+1; dasbit(Par.StimB, 1); % set the stim channel in TDT to 1 to allow stimulus onset reconstruction %dasbit( Par.MicroB, 1); tic %measure onset to %.............................................................. dasreset(1); %test for exiting fix window refreshtracker(2) Time = 0; %StimFlg = true; while Time < TARGT && Hit == 0 %Keep fixating till target onset if TARGT - Time < 5 %dasrun(5) %don't plot any more, so we immediately break from loop Hit = LPStat(2); break else %Check for 5 ms %dasrun(5) %get hit time and plot eyemotion [Hit Time] = DasCheck; if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end end % if 0 % Never executed? For debugging? [CK] % %.............................................................. % %%%%%% % %how much time(ms) do we have left till target onset??? % delay = floor(TARGT + 5 * FLtime - toc*1000 - 5); %hold up calling the next flip % if delay > 0 % dasrun(delay) % Hit = LPStat(2); %don't wast time!!!! on updating the screen % end % end %///////// EVENT 3 TARGET ONSET, REACTION TIME%%////////////////////////////////////// if Hit == 0 %subject kept fixation, subject may make an eye movement %Draw targets for s = 1:STIM.TarN if s==STIM.TrialTypes(TrialTypeNo,1) %this is the target if STIM.TrialTypes(TrialTypeNo,2) == 1 % target is circle cgellipse(targx(s),targy(s),... TargSz,TargSz,STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColCorr,'f'); else % target is rect cgrect(targx(s),targy(s),... sqrt(pi.*((TargSz/2).^2)),sqrt(pi.*((TargSz/2).^2)),... STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColCorr); end elseif s==STIM.TrialTypes(TrialTypeNo,3) %this is the salient non-target if STIM.TrialTypes(TrialTypeNo,2) == 1 % target is circle cgrect(targx(s),targy(s),... sqrt(pi.*((TargSz/2).^2)),sqrt(pi.*((TargSz/2).^2)),... STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_Sal); else % target is rect cgellipse(targx(s),targy(s),... TargSz,TargSz,STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_Sal,'f'); end else % normal non-target if STIM.TrialTypes(TrialTypeNo,2) == 1 % target is circle cgrect(targx(s),targy(s),... sqrt(pi.*((TargSz/2).^2)),sqrt(pi.*((TargSz/2).^2)),... STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_NonSal); else % target is rect cgellipse(targx(s),targy(s),... TargSz,TargSz,STIM.ColSchemes(STIM.TrialList(TrialNr,2)).TargColErr_NonSal,'f'); end end end cgellipse(px,py,Fsz,Fsz,STIM.FixCol_Go,'f') %the green fixation dot on the screen FS = cgflip(BG(1), BG(2), BG(3)); nf=nf+1; LOG.Trial(Par.Trlcount).StimOnset = FO; LOG.Trial(Par.Trlcount).TargOnset = FS; LOG.Trial(Par.Trlcount).StimTarDur = FS - FO; % log time between stim and targ onset dasbit(Par.TargetB, 1); % set the target channel in TDT to 1 to allow target onset reconstruction dasreset(2); %check target window enter refreshtracker(3) %set fix point to green Time = 0; while Time < RACT && Hit <= 0 %RACT = time to respond (reaction time) %Check for 5 ms %dasrun(5) [Hit Time] = DasCheck; if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end %Hit % don't output this to cmd else Abort = true; end %END EVENT 3 else Abort = true; end %END EVENT 2 %///////// POSTTRIAL AND REWARD ////////////////////////////////////// if Hit ~= 0 && ~Abort %has entered a target window (false or correct) if Par.Mouserun HP = line('XData', Par.ZOOM * (LPStat(3) + Par.MOff(1)), 'YData', Par.ZOOM * (LPStat(4) + Par.MOff(2)), 'EraseMode','none'); else HP = line('XData', Par.ZOOM * LPStat(3), 'YData', Par.ZOOM * LPStat(4), 'EraseMode','none'); end set(HP, 'Marker', '+', 'MarkerSize', 20, 'MarkerEdgeColor', 'm') if Hit == 2 && LPStat(6) < SACCT %correct target within max allowed saccade time >> give juice dasbit( Par.CorrectB, 1); % set the Correct channel in TDT to 1 dasbit( Par.RewardB, 1); % set the Reward channel in TDT to 1 dasjuice(5); % open reward valve Par.Corrcount = Par.Corrcount + 1; %log correct trials % beep TrialStatus='Target'; % find out how much reward should be given and give it if STIM.UseSpecRew RewTime = STIM.RewardValues(STIM.TrialList(TrialNr,3)); LOG.TotRew = LOG.TotRew+RewTime; pause(RewTime) %RewardTime is in seconds else pause(Par.RewardTime) %RewardTime is in seconds LOG.TotRew = LOG.TotRew+Par.RewardTime; end dasjuice(0.0); % switch off reward dasbit( Par.RewardB, 0); % set the Reward channel in TDT to 0 % log which target was selected LOG.Trial(Par.Trlcount).TarChoice=LOG.Trial(Par.Trlcount).TarPos; elseif Hit == 1 && LPStat(6) < SACCT dasbit( Par.ErrorB, 1); Par.Errcount = Par.Errcount + 1; % what kind of error? % check x,y endpoints against target windows % these are the x and y endpoints of the eye data xE=LPStat(3); yE=LPStat(4); %targx is a column of target x positions %targy is a column of target y positions % calculate the distance from the eye-endpoint to all targets EyeDistFromTar = sqrt((targx-xE).^2+(targy-yE).^2); % find the closest target (should be the one that is selected) SelTar=find(EyeDistFromTar==min(EyeDistFromTar),1,'first'); % this procedure fails if target windows overlap % which they shouldn't anyway % log which target was selected LOG.Trial(Par.Trlcount).TarChoice=SelTar; % determine the status of the chosen target if SelTar==STIM.TrialTypes(TrialTypeNo,3) % salient distractor selected TrialStatus='Distractor'; Par.Distcount=Par.Distcount+1; else %in wrong target window TrialStatus='Nontarget'; end elseif Hit ~= 0 && LPStat(6) >= SACCT % too slow TrialStatus='SaccadeTooSlow'; end %keep following eye motion to plot complete saccade for i = 1:10 %keep targoff for 50ms %dasrun(5) %not time critical, add some time to follow eyes %dasrun 5); DasCheck; %keep following eye motion if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end % log some behavioral info LOG.Trial(Par.Trlcount).ReactTime = LPStat(5); LOG.Trial(Par.Trlcount).SaccTime = LPStat(6); LOG.Trial(Par.Trlcount).HitPos = [LPStat(3) LPStat(4)]; LOG.Trial(Par.Trlcount).Status = TrialStatus; LOG.Trial(Par.Trlcount).TotRew = LOG.TotRew; %output some info to cmd % trial nr and status fprintf(['Trial ' num2str(Par.Trlcount) ': ' TrialStatus '\n']); % reaction time (RT), saccade time(ST), and Total reward (TR) until now fprintf(['RT: ' num2str(LPStat(5)) ... ' ST: ' num2str(LPStat(6)) ... ' TR: ' num2str(LOG.TotRew) '\n\n']); elseif Par.Trlcount>0 && ~Abort % trial was not aborted but no target was selected in time TrialStatus='NoHit'; % log some behavioral info LOG.Trial(Par.Trlcount).ReactTime = []; LOG.Trial(Par.Trlcount).SaccTime = []; LOG.Trial(Par.Trlcount).HitPos = []; LOG.Trial(Par.Trlcount).Status = TrialStatus; LOG.Trial(Par.Trlcount).TotRew = LOG.TotRew; %output some info to cmd fprintf(['Trial ' num2str(Par.Trlcount) ': ' TrialStatus '\n']); fprintf(['RT: ' num2str(LPStat(5)) ... ' ST: ' num2str(LPStat(6)) ... ' TR: ' num2str(LOG.TotRew) '\n\n']); elseif Par.Trlcount>0 && Abort % trial was started but aborted TrialStatus='Aborted'; % log some behavioral info LOG.Trial(Par.Trlcount).ReactTime = []; LOG.Trial(Par.Trlcount).SaccTime = []; LOG.Trial(Par.Trlcount).HitPos = []; LOG.Trial(Par.Trlcount).Status = TrialStatus; LOG.Trial(Par.Trlcount).TotRew = LOG.TotRew; end if Hit ~= 2 %error response %add pause when subject makes error for i = 1:round(ERRT/5) %keep targoff for Times.Err ms % pause(0.005) %dasrun(5) DasCheck; if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end end %Times.Err is in ms [Hit Lasttime] = DasCheck; if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short %///////////////////////INTERTRIAL AND CLEANUP %reset all bits to null for i = [0 1 2 3 4 5 6 7] %Error, Stim, Saccade, Trial, Correct, dasbit( i, 0); end dasclearword(); SCNT = {'TRIALS'}; SCNT(2) = { ['N: ' num2str(Par.Trlcount) ]}; % n trials SCNT(3) = { ['C: ' num2str(Par.Corrcount) ] }; % n correct SCNT(4) = { ['E: ' num2str(Par.Errcount) ] }; % n error SCNT(5) = { ['D: ' num2str(Par.Distcount) ] }; % n distractor set(Hnd(1), 'String', SCNT ) %display updated numbers in GUI SD = dasgetnoise(); SD = SD./Par.PixPerDeg; set(Hnd(2), 'String', SD ) cgpencol(BG(1), BG(2), BG(3)) %clear background before flipping cgrect cgflip(BG(1), BG(2), BG(3)) nf=nf+1; %pause( Times.InterTrial/1000 ) %pause is called with seconds %Times.InterTrial is in ms Time = Lasttime; while Time < ISI + ISI_R*rand(1) + ERRT % pause(0.005) %dasrun(5) [hit Time] = DasCheck; %#ok if mod(nf,nfr)==0; refreshtracker(1); end %keep eye-position line short end end %WHILE_NOT_ESCAPED%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Save log file =============================== cd LOG; % create a folder for this run warning off; %#ok<*WNOFF> mkdir(LOG.FileName); cd(LOG.FileName); % the generated parameters in a log-file save(LOG.FileName,'LOG','Par','STIM','-v7.3'); % a copy of this runstim fn=[LOG.FileName '_runstim.m']; cfn=[mfilename('fullpath') '.m']; copyfile(cfn,fn); % a copy of the settings file copyfile(LOG.SettingsFileFull,[LOG.FileName '_settings.m']); cd .. cd .. % backup under unique time name in case you accidentally overwrite mkdir('REDUNDANT_LOG'); warning on; %#ok<*WNON> cd 'REDUNDANT_LOG'; save(['DATA_' LOG.datestr '_' LOG.timestr],'LOG','Par','STIM','-v7.3'); copyfile(cfn,['Runstim_' LOG.datestr '_' LOG.timestr '.m']); copyfile(LOG.SettingsFileFull,['Settings_' LOG.datestr '_' LOG.timestr '.m']); cd ..