|
@@ -0,0 +1,221 @@
|
|
|
+
|
|
|
+
|
|
|
+function varargout = Chromatic_Integration_Contrast_Order(pulsenumbers, varargin)
|
|
|
+%
|
|
|
+%
|
|
|
+%%% Chromatic_Integration_Contrast_Order %%%
|
|
|
+%
|
|
|
+%
|
|
|
+% This function returns the stimulus order and contrast values for the defined
|
|
|
+% numbers of input pluses. It uses Fisher-Yates random permutation to set the
|
|
|
+% stimulus order. For more info, see the function fisher_Yates_shuffle_order
|
|
|
+% below.
|
|
|
+%
|
|
|
+% ===============================Inputs====================================
|
|
|
+%
|
|
|
+% pulsenumbers : number of stimulus pulses.
|
|
|
+%
|
|
|
+% =========================Optional Inputs=================================
|
|
|
+%
|
|
|
+% selecttextfile false Browse prompt to select the parameters text file
|
|
|
+% stimduration 30 Stimulus presentation duration screen refresh rate unit (30 = 0.5 sec)
|
|
|
+% preframes 120 Duration of background light presented in between the stimulus flashes
|
|
|
+% maxcontrast 0.2 Maximum Weber contrast value (ranges from -1 to 1)
|
|
|
+% mincontrast -0.2 Minimum Weber contrast value (ranges from -1 to 1)
|
|
|
+% contrastdiff 0.02 Contrast steps from mincontrast to maxcontrast e.g. -20 : 2 : 20
|
|
|
+% seed -1000 Starting value for random number generator (should be always negative)
|
|
|
+% redmeanintensity 0 Mean intensity for red gun of the screen
|
|
|
+% greenmeanintensity 0.5 Mean intensity for green gun of the screen
|
|
|
+% bluemeanintensity 0.5 Mean intensity for blue gun of the screen
|
|
|
+% help false Option to check the list of available parameters
|
|
|
+%
|
|
|
+%================================Output====================================
|
|
|
+%
|
|
|
+% varargout{1} : stimulus order, green contrasts and blue contrasts.
|
|
|
+% varargout{2} : stimulus order.
|
|
|
+%
|
|
|
+% written by Mohammad, 12.02.2021
|
|
|
+
|
|
|
+para = read_stimulus_parameters(varargin{:});
|
|
|
+if para.help, return; end
|
|
|
+
|
|
|
+offcontrasts = fliplr(0:-para.contrastdiff:para.mincontrast);
|
|
|
+oncontrasts = 0:para.contrastdiff:para.maxcontrast;
|
|
|
+numcontrasts = length(oncontrasts)*2;
|
|
|
+% generating contrast values
|
|
|
+if numel(pulsenumbers) > 1
|
|
|
+ para.numtrials = floor( length( pulsenumbers(2 : 2 : numel(pulsenumbers))) / numcontrasts );
|
|
|
+else
|
|
|
+ para.numtrials = floor( pulsenumbers/2 / numcontrasts ); % 1 pulse for stimulus, one for preframe
|
|
|
+end
|
|
|
+seed = para.seed;
|
|
|
+stimorder = nan(para.numtrials, numcontrasts);
|
|
|
+
|
|
|
+% getting the order and location of each contrast from the psudo random sequence
|
|
|
+for ii = 1 : para.numtrials
|
|
|
+ [stimorder(ii,:),seed,~] = fisher_Yates_shuffle_order(seed, 1:numcontrasts);
|
|
|
+end
|
|
|
+
|
|
|
+% setting up output to get the stimulus order contrast orders
|
|
|
+stimorder = stimorder'; % transposing to make vectorization (:) easier
|
|
|
+stimorder = stimorder(:);
|
|
|
+% out.numberContrastsShown = floor((framecounter-1)/(para.stimduration+para.preframes));
|
|
|
+% stimorder = stimorder(1:out.numberContrastsShown);
|
|
|
+out.stimulusOrder = transpose(stimorder);
|
|
|
+% to get contrasts between input values
|
|
|
+greencontrasts = [offcontrasts,oncontrasts];
|
|
|
+bluecontrasts = [oncontrasts,offcontrasts];
|
|
|
+% convert to weber contrast (mean+(contrast*mean))
|
|
|
+greencontrasts = para.greenmeanintensity + (greencontrasts * para.greenmeanintensity);
|
|
|
+bluecontrasts = para.bluemeanintensity + (bluecontrasts * para.bluemeanintensity);
|
|
|
+% set the contrast output
|
|
|
+out.greenContrasts = greencontrasts(stimorder);
|
|
|
+out.blueContrasts = bluecontrasts(stimorder);
|
|
|
+out.trialnumbers = para.numtrials;
|
|
|
+
|
|
|
+varargout{1} = out;
|
|
|
+varargout{2} = stimorder;
|
|
|
+
|
|
|
+end
|
|
|
+
|
|
|
+%--------------------------------------------------------------------------------------------------%
|
|
|
+%---------- sub-functions ----------%
|
|
|
+%--------------------------------------------------------------------------------------------------%
|
|
|
+
|
|
|
+
|
|
|
+function varargout = fisher_Yates_shuffle_order(seed,inputVec,varargin)
|
|
|
+%
|
|
|
+%%% fisher_Yates_shuffle_order %%%
|
|
|
+%
|
|
|
+%
|
|
|
+% This function generate psudorandom permution similar to randperm in MATLAB
|
|
|
+% but works with psudorandom number generator ran1. It also gives back the
|
|
|
+% most recent seed value to continue the permuation in case of repeated trials.
|
|
|
+% note that the direction of permutation is along x-axix or for columns of
|
|
|
+% MATALB not for the rows.
|
|
|
+%
|
|
|
+%
|
|
|
+% ===============================Inputs====================================
|
|
|
+%
|
|
|
+% seed : seed value for random number generation.
|
|
|
+% inputVec : input vector used for permutation.
|
|
|
+%
|
|
|
+%================================Output====================================
|
|
|
+%
|
|
|
+% testOrder : vector of permuted indices for the inputVec.
|
|
|
+% newSeed : the recent seed that used in the ran1 function.
|
|
|
+% outputVec : the permuted input vector along x-axis
|
|
|
+%
|
|
|
+% Note that the permution algorithem is based on Fisher-Yates shuffle
|
|
|
+% algorithem identical to what is used in the stimulus program.
|
|
|
+% for more info check :
|
|
|
+% https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
|
|
|
+%
|
|
|
+% written by Mohammad, 01.02.2016
|
|
|
+
|
|
|
+newSeed = seed;
|
|
|
+testOrder = zeros(1,size(inputVec,2));
|
|
|
+testOrder(1) = 1;
|
|
|
+
|
|
|
+for i = 2:length(inputVec)-1
|
|
|
+
|
|
|
+ [randVal,newSeed] = ran1(newSeed);
|
|
|
+
|
|
|
+ j = ceil(i*randVal); % based on Fischer-Yates algorithem
|
|
|
+ testOrder(i) = testOrder(j);
|
|
|
+ testOrder(j) = i;
|
|
|
+
|
|
|
+end
|
|
|
+
|
|
|
+testOrder = [testOrder(end),testOrder(1:end-1)]+1; % to match MATLAB indexing
|
|
|
+varargout{1} = testOrder;
|
|
|
+varargout{2} = newSeed;
|
|
|
+for j = 1:size(inputVec,1)
|
|
|
+ varargout{3}(j,:) = inputVec(j,testOrder);
|
|
|
+end
|
|
|
+
|
|
|
+end
|
|
|
+
|
|
|
+%--------------------------------------------------------------------------------------------------%
|
|
|
+
|
|
|
+function paraout = read_stimulus_parameters(varargin)
|
|
|
+
|
|
|
+% first parse the user inputs
|
|
|
+p = inputParser(); % check the user options.
|
|
|
+p.addParameter('selecttextfile', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
|
|
|
+p.addParameter('stimduration', 30, @isnumeric);
|
|
|
+p.addParameter('preframes', 120, @isnumeric);
|
|
|
+p.addParameter('contrastdiff', 0.02, @isnumeric);
|
|
|
+p.addParameter('mincontrast',-0.2, @isnumeric);
|
|
|
+p.addParameter('maxcontrast', 0.2, @isnumeric);
|
|
|
+p.addParameter('seed', -1000, @isnumeric);
|
|
|
+p.addParameter('redmeanintensity', 0, @isnumeric);
|
|
|
+p.addParameter('greenmeanintensity', 0.5, @isnumeric);
|
|
|
+p.addParameter('bluemeanintensity', 0.5, @isnumeric);
|
|
|
+p.addParameter('help', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
|
|
|
+
|
|
|
+p.parse(varargin{:});
|
|
|
+% defualt parameters
|
|
|
+defpara = p.Results;
|
|
|
+
|
|
|
+if defpara.help
|
|
|
+ help_info_for_parameters(defpara);
|
|
|
+ paraout = defpara;
|
|
|
+ return;
|
|
|
+end
|
|
|
+
|
|
|
+if defpara.selecttextfile
|
|
|
+ % now read the text files
|
|
|
+ [stimfile, stimpath] = uigetfile('*.txt','Select a chromatic integration stimulus parameter file','chromatic_integration.txt');
|
|
|
+
|
|
|
+ fid = fopen([stimpath,filesep,stimfile]);
|
|
|
+ tline = fgetl(fid);
|
|
|
+ while ischar(tline)
|
|
|
+ tline = fgetl(fid);
|
|
|
+ if tline == -1, break; end
|
|
|
+ fn = extractBefore(tline,' = ');
|
|
|
+ if isempty(fn), continue; end
|
|
|
+ val = extractAfter(tline,' = ');
|
|
|
+ % to convert to double
|
|
|
+ if ~isnan(str2double(val))
|
|
|
+ val = str2double(val);
|
|
|
+ end
|
|
|
+ % to convert to logical
|
|
|
+ if strcmp(val,'true'), val = true; end
|
|
|
+ if strcmp(val,'false'), val = false; end
|
|
|
+ % store the values in a structure
|
|
|
+ stimpara.(fn) = val;
|
|
|
+ end
|
|
|
+ fclose(fid);
|
|
|
+
|
|
|
+ % compare the text file to the defualt values and fill the missing parameters
|
|
|
+ fn = fieldnames(defpara);
|
|
|
+ for ii = 1:numel(fn)
|
|
|
+ if isfield(stimpara,fn{ii})
|
|
|
+ paraout.(fn{ii}) = stimpara.(fn{ii});
|
|
|
+ else
|
|
|
+ paraout.(fn{ii}) = defpara.(fn{ii});
|
|
|
+ end
|
|
|
+ end
|
|
|
+else
|
|
|
+ paraout = defpara;
|
|
|
+end
|
|
|
+
|
|
|
+end
|
|
|
+
|
|
|
+%--------------------------------------------------------------------------------------------------%
|
|
|
+
|
|
|
+function help_info_for_parameters(defpara)
|
|
|
+
|
|
|
+fn = fieldnames(defpara);
|
|
|
+fprintf(['\n\n',repmat('==',1,50),'\r\n']);
|
|
|
+fprintf([repmat(' ',1,35),'List of Stimulus Parameters\r\n']);
|
|
|
+fprintf([repmat('==',1,50),'\r\n']);
|
|
|
+maxtxtlen = max(cellfun(@numel,fn))+10;
|
|
|
+for ii = 1:numel(fn)
|
|
|
+ g = repmat(' ',1,maxtxtlen - numel(fn{ii}));
|
|
|
+ fprintf(['\t-- \t%s',g,':',repmat(' ',1,10),'%s\n'],fn{ii},num2str(defpara.(fn{ii})));
|
|
|
+end
|
|
|
+fprintf(['\n',repmat('==',1,50),'\r\n']);
|
|
|
+
|
|
|
+end
|