Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
d9a8731
Forked from another repo and setting up here
DavidBrainard Jun 25, 2025
46c8484
Adding in Niveditha's code
DavidBrainard Jun 25, 2025
29aa277
Adding in Nivedetha's code
DavidBrainard Jun 25, 2025
83539d7
Delete BrainardLab and Psychtoolboxes. TbTb now handles putting thos…
DavidBrainard Jun 25, 2025
c247ba4
Remove Palamedes toolbox
DavidBrainard Jun 25, 2025
558fc6e
Going through, cleaning, simplifying, etc.
DavidBrainard Jun 25, 2025
0bc5a13
Cleaning
DavidBrainard Jun 27, 2025
36a8596
Something
DavidBrainard Jun 29, 2025
cf41398
Little changes
DavidBrainard Jun 29, 2025
6afe9b8
Tune up code a little, split half analysis for each session, write ex…
DavidBrainard Jun 30, 2025
3f818ca
Comments
DavidBrainard Jun 30, 2025
5507701
Working on this.
DavidBrainard Jul 2, 2025
ffcebaf
Rename
DavidBrainard Jul 3, 2025
eed72fb
More data checks, handle rounded down trials, fix up LUT handling, ch…
DavidBrainard Jul 3, 2025
656c441
First go at Figure3 and t-test.
DavidBrainard Jul 7, 2025
1c4b357
Adding combined data analysis.
DavidBrainard Jul 7, 2025
0afc503
This version freezes Matlab for unknown reasons.
DavidBrainard Jul 8, 2025
c5736a9
Put back 7/3 version. Let's make sure this runs to completion
DavidBrainard Jul 8, 2025
fa2fe66
Still trying to prevent beachball, but at least it is getting cleaner
DavidBrainard Jul 10, 2025
64d79ca
Fussing with slope and organizing output appropriately
DavidBrainard Jul 12, 2025
de91c9c
Save code snippets useful for copying and pasting to look at various …
DavidBrainard Jul 12, 2025
2e59f00
More bells and whistles
DavidBrainard Jul 12, 2025
f6b8461
Tuning.
DavidBrainard Jul 14, 2025
2f1d2c1
Add version
DavidBrainard Jul 14, 2025
a9d11da
Log quantization figure
DavidBrainard Jul 14, 2025
bf37ac7
Plot save, and condition futzing.
DavidBrainard Jul 14, 2025
c137530
More snippets
DavidBrainard Jul 14, 2025
08f07eb
Put figure vis back. Other tweaks
DavidBrainard Jul 16, 2025
706a98f
Merge branch 'main' of https://github.com/BrainardLab/AOMicroRepeat
DavidBrainard Jul 16, 2025
8251b29
Mostly adding comments.
DavidBrainard Jul 29, 2025
c75321c
Remove N's original code from here.
DavidBrainard Aug 5, 2025
b778b80
Add Bland-Altman plot routine from Mathworks file exchange
DavidBrainard Aug 5, 2025
5dd4827
Update README.md
DavidBrainard Sep 10, 2025
d1ae701
Push test bug fix identified by N.
DavidBrainard Sep 16, 2025
a274ae0
Moved over by hand from NG_AOM_Repeatability branch
DavidBrainard Sep 26, 2025
aa7935e
Rename the original versions
DavidBrainard Sep 26, 2025
d01ba22
Move in from N's branch
DavidBrainard Sep 26, 2025
579250b
Add a file that might fix up the line ending issues, or might not.
DavidBrainard Sep 26, 2025
0062cc4
code compatibility issue
Nivedhitha14 Sep 26, 2025
2a1878c
Update CombineTrials.m
Nivedhitha14 Oct 3, 2025
5298765
Nivedhitha14 Oct 3, 2025
966dfb6
new_session_split_code
Nivedhitha14 Oct 3, 2025
a7aa9e4
Nivedhitha14 Oct 7, 2025
dc78633
Delete CombineTrials.asv
Nivedhitha14 Oct 7, 2025
bd37fb9
Update CombineTrials.m
Nivedhitha14 Oct 7, 2025
8610441
Update CombineTrials.m
Nivedhitha14 Oct 7, 2025
139fefb
Create combinedData.mat
Nivedhitha14 Oct 7, 2025
61ac04e
changing code for within-session analysis
Nivedhitha14 Oct 7, 2025
1c52159
Nivedhitha14 Oct 8, 2025
27ed2bf
Nivedhitha14 Oct 8, 2025
16efc92
Nivedhitha14 Oct 9, 2025
8b67a4f
Nivedhitha14 Oct 9, 2025
e51c8b4
Create FitTrials.asv
Nivedhitha14 Oct 9, 2025
5a946a2
Added Wilcoxon-test
Nivedhitha14 Oct 13, 2025
5bba707
Nivedhitha14 Oct 16, 2025
fcdae3a
Delete FitTrials.asv
Nivedhitha14 Oct 16, 2025
b1e56e9
Updated figures with similar settings and added figure 4
Nivedhitha14 Oct 16, 2025
b846209
Nivedhitha14 Oct 20, 2025
f62bf5d
Update Figure3.m
Nivedhitha14 Oct 24, 2025
01cfb5f
updated figures with errorbar
Nivedhitha14 Oct 27, 2025
e61109c
Update FigureSetup.m
Nivedhitha14 Oct 27, 2025
aad527e
plots with error bars and split BA pplot
Nivedhitha14 Oct 31, 2025
e06d10a
Review and merge N's changes
DavidBrainard Nov 6, 2025
098290d
Checking N's CombineTrials, and cleaning it.
DavidBrainard Nov 6, 2025
a22faad
Cleaning, checking.
DavidBrainard Nov 6, 2025
af45d67
Deleting stray data in repo
DavidBrainard Nov 6, 2025
25ab880
Rewrote splits in CombineData and how they are read into FitTrials
DavidBrainard Nov 6, 2025
d4921fe
Comments
DavidBrainard Nov 6, 2025
a3acd0a
Get rid of old DHB versions, to minimize confusion. They exist in the…
DavidBrainard Nov 6, 2025
42b4aa9
Fix path in figure setup
DavidBrainard Nov 6, 2025
39258b8
Figure out how to get pdfs not to be cut off. Only implement in firs…
DavidBrainard Nov 7, 2025
c964fac
Nivedhitha14 Nov 10, 2025
8da5a8c
Nivedhitha14 Nov 11, 2025
7778221
Update Figure3.m
Nivedhitha14 Nov 13, 2025
79de8f8
Update Figure3.m
Nivedhitha14 Nov 13, 2025
e4d4c70
Nivedhitha14 Nov 13, 2025
0bd7eeb
Nivedhitha14 Nov 13, 2025
f13aadb
Nivedhitha14 Nov 17, 2025
7a1d416
Nivedhitha14 Nov 17, 2025
b6429b5
Create Figure4.asv
Nivedhitha14 Nov 17, 2025
202d317
Nov 18, 2025
abd1d3d
Update Figure3.m
Nivedhitha14 Nov 18, 2025
cca20c1
Update Figure4.m
Nivedhitha14 Nov 18, 2025
9d80f55
Update Figure5.m
Nivedhitha14 Nov 18, 2025
0f4079f
Update FigureSetup.m
Nivedhitha14 Nov 18, 2025
a6b44bc
Nivedhitha14 Nov 19, 2025
8b2bdf1
Updated code that prints p values for 8 vs 43 ppixels and LoA
Nivedhitha14 Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto: This tells Git to automatically handle EOL conversion for files it detects as text.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# AOCompObserver
Computational observer calculations for our AO psychopysical experiments
# AOMicroRepeat
AO psychophysics analysis
Binary file added analysisDir/combinedData.mat
Binary file not shown.
11 changes: 11 additions & 0 deletions code/CheckDataSnippet.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

% Load a QUEST data file and run this to find maximum trial intensity with
% incorrect response. Intensity is raw log10, not LUT corrected.
index = find(response_matrix == 0 & ~isnan(theThreshold) );
max(trial_matrix(index))

% Load a MOCS data file and run this to find maximum trial intensity with
% incorrect response. Intensity is raw log10, not LUT corrected.
index = find(response_vector);
max(trial_vector(index))
unique(trial_vector)
312 changes: 312 additions & 0 deletions code/CombineTrials.m

Large diffs are not rendered by default.

264 changes: 264 additions & 0 deletions code/CombineTrialsDHB.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
%% CombineTrials
%
% Combine all of the psychophysical data files into one big .mat
% file. We do this to simplify the other programs, since the logic
% here involves lots of checks that everything is as it should be.
%
% Also identify catch trials in QUEST runs, which are somewhat obscurely
% indicated, and fix them up so later programs don't have to worry about
% this.
%
% Although this might look like it is splitting the data into two halves, that
% actually happens elsewhere, and here all the data are stored under all the splits.
% This is a vestige from the time this code was part of the full data analysis loop, and
% if we were better people we'd skip this step here. Maybe we will become better at some
% point.
%
% The output of this program is a mat file 'CombinedData.mat' that gets stored in the
% analysis output tree, and that contains one big cell array with all of the data.
%
% See also: FitTrials, FigureN

%% Initialize
close all; clear all;

%% Get path to data
%
% Path to data tree on this machine
%
% This is set up by TbTb local hook file, but
% you can also use
% setpath('AOMicroRepeat','dataDir',theDataDir)
% to do this, where theDataDir is the path to the
% files.
%
% In the code example I got, this was
% path = 'W:\Data\11125\20231103\AO_Psychophysics\MOCS\8\group2';
% but that does not match the actuall data tree I received for 11002.
dataDir = getpref('AOMicroRepeat','dataDir');

%% Also analysis output dir, same idea
analysisDir = getpref('AOMicroRepeat','analysisDir');

%% Some parameters
log0Value = -3.5;

%% Define what data we are analyzing here
%
% Generally speaking, this routine loops over everything and does its thing
%
% Define subjects
theParticipants = {'11002' '11108' '11118' '11119' '11125'};

% Define sessions (1 or 2)
theSessions = [1 2];

% Define session splits
theSplits = {'All', 'FirstHalf', 'SecondHalf'};

% Define sizes (8 or 43)
theDiameters = [8 43];

% Define methods ('MOCS' or 'QUEST')
%
% When we create COMBINED data below, we count
% on this being as it is. Don't change wihtout care.
theMethods = {'MOCS' 'QUEST', 'COMBINED'};

%% Get the AOM lookup table info.
%
% This is loaded and saved here with the data, so that the programs that
% read the output of this program don't have to go and find it.
%
% As far as I can guess
% Column 1: nominal linear intensities
% Column 2: LUT linear intensities
% Column 3: nominal log10 intensities
% Column 4: looks like a non-linear mapping from 10 to 8 bits
AOM = load('green_AOM_LUT_processing');

%% Loop over everything
tableRow = 1;
for pp = 1:length(theParticipants)
for dd = 1:length(theDiameters)
for ss = 1:length(theSessions)
for hh = 1:length(theSplits)
checkSessionDate = [];
MOCSFileTimes = [];
QUESTFileTimes = [];
for mm = 1:length(theMethods)

% Store info for what we are analyzing in this run
theMethod{tableRow,1} = theMethods{mm};
theSubject{tableRow,1} = theParticipants{pp};
theDiameter(tableRow,1) = theDiameters(dd);
theSession(tableRow,1) = theSessions(ss);
theSplit{tableRow,1} = theSplits{hh};

% Handle MOCS and QUEST from the data
if (strcmp(theMethods{mm},'MOCS') | strcmp(theMethods{mm},'QUEST'))

% Form path the the directory with the data sitting in it
pathToData = fullfile(dataDir,theSubject{tableRow},['Session' num2str(theSession(tableRow))],['Size' num2str(theDiameter(tableRow))],theMethod{tableRow});

% Get list of data files
dirOffset = 1;
trial_videos = dir(fullfile(pathToData,'*.mat'));
num_trial_videos = length(trial_videos(dirOffset:end));
if (num_trial_videos ~= 4)
error('Expect 4 data files');
end

% Read and concatenate the data
all_trials{pp,dd,ss,hh,mm} = {};
all_trials_unpacked{pp,dd,ss,hh,mm} = [];
fprintf('\tReadng videos\n');
for i = 1:num_trial_videos
% Get check date from filename. This should be the
% same for all the MOCS and QUEST files from the
% same subject session. The method is the inner
% loop variable, so we can do the check here.
if (isempty(checkSessionDate))
checkSessionDate = trial_videos(i+dirOffset-1).name(7:16);
else
if (~strcmp(checkSessionDate,trial_videos(i+dirOffset-1).name(7:16)))
error('Not all data files from same session are on the same date');
end
end

% Check that QUEST file times are later than last
% MOCS time. This code assumes that it runs to
% completion on the same calendar day on which it
% was started, which will almost always be true.
%
% Count underscores to find start of the time
% string.
nUnderscore = 0;
for cc = 1:length(trial_videos(i+dirOffset-1).name)
if (trial_videos(i+dirOffset-1).name(cc) == '_')
nUnderscore = nUnderscore + 1;
end
if (nUnderscore == 4)
timeStartIndex = cc + 1;
break;
end
end
fileTimeStr = trial_videos(i+dirOffset-1).name(timeStartIndex:timeStartIndex+4);

% Handle way single digit time numbers get written
% in the filename. Brute force this. Ugh.
if (fileTimeStr(2) == '_')
fileTimeStr = ['0' fileTimeStr];
end
if (fileTimeStr(5) == '_')
fileTimeStr(5) = fileTimeStr(4);
fileTimeStr(4) = '0';
end
fileTimeStr = fileTimeStr(1:5);

% Convert to format MATLAB can operate on
fileTime = datetime(fileTimeStr,'InputFormat','HH_mm');
if (strcmp(theMethod(tableRow),'MOCS'))
MOCSFileTimes = [MOCSFileTimes ; fileTime];
else
QUESTFileTimes = [QUESTFileTimes ; fileTime];
end

% Read the data file
% fprintf('Reading file %s\n',fullfile(pathToData,trial_videos(i+dirOffset-1).name));
loadedData{pp,dd,ss,hh,mm,i} = load(fullfile(pathToData,trial_videos(i+dirOffset-1).name));

% Grab the trial data we need. Also do some
% MOCS and QUEST specific reality checks
if (isfield(loadedData{pp,dd,ss,hh,mm,i},'trial_vector'))
% It's a MOCS data file by what's in it
if (~strcmp(theMethod{tableRow},'MOCS'))
fprintf('File %s\n\tUnder QUEST, appears to be MOCS\n\tTrials: %d\n',fullfile(pathToData,trial_videos(i+dirOffset-1).name),length(loadedData{pp,dd,ss,hh,mm,i}.trial_vector));
end
if (length(loadedData{pp,dd,ss,hh,mm,i}.trial_vector) ~= 90 & length(loadedData{pp,dd,ss,hh,mm,i}.trial_vector) ~= 100)
fprintf('File %s: Wrong number of trials in MOCS data file\n');
end
all_trials{pp,dd,ss,hh,mm}{i,1} = loadedData{pp,dd,ss,hh,mm,i}.trial_vector;
all_trials{pp,dd,ss,hh,mm}{i,2} = loadedData{pp,dd,ss,hh,mm,i}.response_vector;

% Reality check on MOCS stimulus levels.
% This should be the same across runs
% within session, I think, but are not
% always. This doesn't error out, but does
% report the unexpected cases.
for j = i-1:-1:1
if (any(unique(loadedData{pp,dd,ss,hh,mm,j}.trial_vector) ~= unique(loadedData{pp,dd,ss,hh,mm,i}.trial_vector)))
fprintf('\t\tMOCS mismatch in trial levels across runs\n')
fprintf('\t\t%s, session %d, size %d, run loaded #%d vs run loaded #%d\n',theParticipants{pp},theSessions(ss), theDiameters(dd),i,j);
fprintf('\t\tFile loaded #%d: %s\n',i,fullfile(trial_videos(i+dirOffset-1).name));
fprintf('\t\tFile loaded #%d: %s\n',j,fullfile(trial_videos(j+dirOffset-1).name));
end
end

elseif (isfield(loadedData{pp,dd,ss,hh,mm,i},'trial_matrix'))
% It's a QUEST data file by what's in it
if (~strcmp(theMethod{tableRow},'QUEST'))
fprintf('File %s\n\tUnder MOCS, appears to be QUEST\n\tTrials: %d\n',fullfile(pathToData,trial_videos(i+dirOffset-1).name),length(loadedData{pp,dd,ss,hh,mm,i}.trial_matrix));
end
if (length(loadedData{pp,dd,ss,hh,mm,i}.trial_matrix) ~= 44)
fprintf('File %s: Wrong number of trials in QUEST data file\n');
end
all_trials{pp,dd,ss,hh,mm}{i,1} = loadedData{pp,dd,ss,hh,mm,i}.trial_matrix;
questCatchTrialIndex = find(isnan(loadedData{pp,dd,ss,hh,mm,i}.theThreshold));
if (length(questCatchTrialIndex) ~= 4)
fprintf('Wrong number of catch trials in QUEST data file');
end
all_trials{pp,dd,ss,hh,mm}{i,1}(questCatchTrialIndex) = log0Value;
all_trials{pp,dd,ss,hh,mm}{i,2} = loadedData{pp,dd,ss,hh,mm,i}.response_matrix;
else
error('Do not understand data format');
end

% Get/check number of trials and make sure they
% are the same for each run within
% method/session.
if (i == 1)
nTrialsPerSession = length(all_trials{pp,dd,ss,hh,mm}{i,1});
else
if (length(all_trials{pp,dd,ss,hh,mm}{i,1}) ~= nTrialsPerSession)
error('Trial mismatch across runs');
end
end

% Concatenate across runs into one long pair of
% vectors.
all_trials_unpacked{pp,dd,ss,hh,mm} = [all_trials_unpacked{pp,dd,ss,hh,mm} ; [all_trials{pp,dd,ss,hh,mm}{i,1} all_trials{pp,dd,ss,hh,mm}{i,2}] ];
end

if (strcmp(theMethod{tableRow},'MOCS') & size(MOCSFileTimes,1) ~= 4)
error('Wrong number of MOCS files somewhere in data tree');
end
if (strcmp(theMethod{tableRow},'QUEST') & size(QUESTFileTimes,1) ~= 4)
error('Wrong number of QUEST files somewhere in data tree');
end
if (strcmp(theMethod{tableRow},'QUEST'))
QUESTFileTimesSorted = sort(QUESTFileTimes);
MOCSFileTimesSorted = sort(MOCSFileTimes);
for ff = 1:size(MOCSFileTimes,1)
if (QUESTFileTimesSorted(ff) < MOCSFileTimesSorted(ff))
fprintf('File time order error\n');
end
end
end
else
% Concatenate MOCS and QUEST data into COMBINED
all_trials_unpacked{pp,dd,ss,hh,mm} = [all_trials_unpacked{pp,dd,ss,hh,1} ; all_trials_unpacked{pp,dd,ss,hh,2}];
end

% Bump table row
tableRow = tableRow + 1;
end
end
end
end
end

% Save out one nice big combined file
save(fullfile(analysisDir,'combinedData.mat'),'all_trials','all_trials_unpacked','log0Value','theParticipants','theDiameters','theSessions','theSplits','theMethods','AOM','-v7.3');


Loading