Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3552a63
First commit for longitudinal analysis, electrical noise
iwhiteley Aug 12, 2025
b2b1f84
Code not working, debugging
iwhiteley Aug 15, 2025
bdca442
Can give the microscope diagnostics file and code identifies all nois…
iwhiteley Aug 18, 2025
94215ad
Started on analysis for changes to noise over time, not yet working
iwhiteley Aug 18, 2025
35a0b8e
Added FWHM loop to check change over time
iwhiteley Aug 22, 2025
a1d6acc
Added Gaussian smoothing of the hist data and FWHM measurements
iwhiteley Aug 29, 2025
f4c216b
now only looks for .tif files
iwhiteley Aug 29, 2025
abee3e6
Added date labels for x axis on plots and gave brief description of t…
iwhiteley Aug 29, 2025
32a6ca2
Changed load to scanImage_stackLoad to all PMT recordings are loaded …
iwhiteley Sep 1, 2025
acbc1b5
Fixed plotting and titles so date is main title and PMT number is sub…
iwhiteley Sep 2, 2025
b61b3f7
Updated FWHM and max value plots to separate each PMT
iwhiteley Sep 2, 2025
4c4588d
Changed to varargout, and made structure for previous outputs, moved …
iwhiteley Sep 3, 2025
254924a
Sorted data by date and time
iwhiteley Sep 4, 2025
b3d509d
Updated output to include FWHM and max values, changed to output file…
iwhiteley Sep 4, 2025
415932d
Code clean up and minor edits to description
iwhiteley Sep 4, 2025
15d3384
Starting varargin for selecting starting date for the data to plot/an…
iwhiteley Sep 5, 2025
9473591
Minor update to README.md
iwhiteley Jun 18, 2025
12798d9
Changed the date read to take from the file name rather than file cre…
iwhiteley Sep 8, 2025
a62e3ce
Added varargin of a start date for plotting the data, currently input…
iwhiteley Sep 8, 2025
3ce0147
Added function return point if no electrical noise files found
iwhiteley Sep 8, 2025
fad1a2f
Cleaned up code and minor edits, BUG found- for some datasets, the PM…
iwhiteley Sep 8, 2025
980bea5
First commit for of longitudinal analysis for standard light source d…
iwhiteley Sep 9, 2025
d31ea3c
First commit of longitudinal power measurements, taking code from lon…
iwhiteley Sep 10, 2025
585b682
Plots all power measurements in one plot for overview, added waveleng…
iwhiteley Sep 10, 2025
21364e7
Plots all power curves and changes in max power over time if waveleng…
iwhiteley Sep 10, 2025
267cf76
Updated description of function, multiple wavelengths currently not …
iwhiteley Sep 10, 2025
2de87bd
Added else if for specific wavelength varargin, not yet working as ne…
iwhiteley Sep 11, 2025
05dca71
Working on separating plots for different wavelengths, partway there …
iwhiteley Sep 11, 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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ and for measuring field of view size using an EM grid.
## Main functions

* `mpqc.record.PSF` to easily acquire PSF stacks with ScanImage.
* `mpqc.record.lens_tissue` to acquire standardised data from lens tissue.
* `mpqc.record.lens_paper` to acquire standardised data from lens tissue.
* `mpqc.record.standard_light_source` to data from a standard light source
* `mpqc.record.electrical_noise` to measure the electrical noise of the PMTs.
* `measurePSF` to estimate PSF size. For a demo, run `measurePSF('demo')`.
* `Grid2MicsPerPixel` measures the number of microns per pixel along x and y by analyzing an image of an EM grid.
* `mpqc.tools.meanFrame` plots the mean frame intensity as a function of time whilst you are scanning.
Expand Down
151 changes: 151 additions & 0 deletions code/+mpqc/+longitudinal/long_electrical_noise.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
function varargout = long_electrical_noise(data_dir,varargin)
% Longitudinal electrical noise plots showing FWHM and max values over time
%
% mpqc.longitudinal.long_electrical_noise(maintenace_folder_path, varargin)
% Optional inputs: Starting date- month-year
%
% Purpose
% Plots of the maximum value and FWHM of electrical noise for each channel
% with PMTs off. If there is significant change in electrical noise, the FWHM
% distributions will likely increase in value.
%
%
% Outputs
% out (optional) - structure containing key information and data.
%
% Notes
% Currently plots for 4 PMT channels regardless of actual number of
% PMTs in system
%
%
% Isabell Whiteley, SWC AMF 2025

if nargin<1
data_dir = pwd;
end


debugPlots = true;

maintenanceFiles = dir(fullfile(data_dir,'\**\*.tif'));
n=1;

for ii=1:length(maintenanceFiles)
tmp = maintenanceFiles(ii);

if contains(tmp.name,'electrical_noise')
plotting_template(n) = generic_generator_template(tmp);
plotting_template(n).type = 'electrical_noise';
plotting_template(n).plotting_func = @mpqc.plot.electrical_noise;
plotting_template(n).date = string(datetime(regexp(tmp.name, '(\d{4}-\d{2}-\d{2})_(\d{2}-\d{2}-\d{2})','match'),'InputFormat','yyyy-MM-dd_HH-mm-ss'));
[pathstr,plotting_template(n).name,ext] = fileparts(tmp.name);
n=n+1;
end
end
if ~exist('plotting_template','var')
disp('No electrical noise files found')
varargout{1} = [];
return
end

% sort plotting_template data by the date/time
date_list = [plotting_template.date];
[~,order] = sort(datenum(date_list,'dd-mm-yyyy hh:MM:ss'),1,'ascend');
plotting_template = plotting_template(order);

if nargin > 1 % Optional variable for selecting starting date
startDate = datetime(varargin{1});
startIndex = 1;

while [plotting_template(startIndex).date] < startDate
startIndex = startIndex + 1;
end

plotting_template = plotting_template(startIndex:end);
end

for ii = 1:length(plotting_template)
if contains(plotting_template(ii).full_path_to_data, '.tif')
noiseData(:,:,:,ii) = mpqc.tools.scanImage_stackLoad(plotting_template(ii).full_path_to_data);
end
end

noiseData = single(noiseData);

for q = 1:size(noiseData,4) % each date

if debugPlots
fig = mpqc.tools.returnFigureHandleForFile(sprintf('%s_%02d',mfilename,q));
end
for t = 1:size(noiseData,3) % each PMT

% Extract data
t_im = noiseData(:,:,t,q);
[n,x] = hist(t_im(:),100); % plots all data as histogram
m = smoothdata(n,'gaussian',5);
detail = interp1(x,m,[1:1000]);

maxVal(t,q) = max(detail(:));
halfMaxVal = maxVal(t,q)/2;
leftIndex = find(detail(:) >= halfMaxVal, 1, 'first');
rightIndex = find(detail(:) >= halfMaxVal, 1, 'last');
fwhm(t,q) = rightIndex -leftIndex;

% TODO Remove data where PMT does not exist

% Optionally plot
if debugPlots
subplot(2,2,t)
a=area(n);
a.EdgeColor=[0,0,0.75];
a.FaceColor=[0.5,0.5,1];
a.LineWidth=2;
hold on
b = plot(m);
b.LineWidth = 2;
sgtitle(plotting_template(q).date)
title(['PMT # ',num2str(t)])
hold off
end
end
end

for ii = 1:size(noiseData,3) % plotting FWHM and max value over time for each PMT
xlabels = {plotting_template.date};
fig = mpqc.tools.returnFigureHandleForFile(sprintf('%s_%02d',mfilename,ii));
subplot(2,1,1)
plot(maxVal(ii,:))
xticks(1:length(xlabels))
xticklabels(xlabels)
title('Max value')

subplot(2,1,2)
plot(fwhm(ii,:))
xticks(1:length(xlabels))
xticklabels(xlabels)
title('FWHM')
sgtitle(['PMT # ',num2str(ii)])
end


% Output of the main function
if nargout>0
out.fileName = {plotting_template(:).name};
out.noiseData = noiseData;
out.fwhm = fwhm;
out.maxValues = maxVal;
out.date ={plotting_template(:).date};
varargout{1} = out;
end

end % close main funtion



function out = generic_generator_template(t_dir)
out.full_path_to_data = fullfile(t_dir.folder,t_dir.name);
out.type = [];
out.plotting_func = [];
out.name = [];
out.date = [];
end
181 changes: 181 additions & 0 deletions code/+mpqc/+longitudinal/long_power.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
function varargout = long_power(data_dir,varargin)
% Function to track the changes in laser power over time
%
% mpqc.longitudinal.long_power(maintenace_folder_path, varargin)
%
% Optional inputs:
% 'startDate', month-year
% 'wavelength, value
%
% Purpose
% Plots the power at the objective from 0-100% and compares maximum output
% power over time. Used to monitor the health of a laser
%
%
% Outputs
% out (optional) - structure containing key information and data.
%
%
%
% Isabell Whiteley, SWC AMF 2025

if nargin<1
data_dir = pwd;
end

% For elseif statement of wavelength given in varargin
% % process inputs
% % addpath("+tools");
% optInputs = parseInputVariable(varargin{:});
%
% % Extract critical input variable
% selectWavelength = optInputs.wavelength;
% % TODO Add to parseInputVariable startDate

debugPlots = true;

maintenanceFiles = dir(fullfile(data_dir,'\**\*.mat'));
n=1;

for ii=1:length(maintenanceFiles)
tmp = maintenanceFiles(ii);

if contains(tmp.name,'power')
plotting_template(n) = generic_generator_template(tmp);
plotting_template(n).type = 'power';
plotting_template(n).plotting_func = @mpqc.plot.power;
plotting_template(n).date = string(datetime(regexp(tmp.name, '(\d{4}-\d{2}-\d{2})_(\d{2}-\d{2}-\d{2})','match'),'InputFormat','yyyy-MM-dd_HH-mm-ss'));
plotting_template(n).wavelength = str2num(cell2mat(regexp(tmp.name,'\d*(?=nm)','match')));
[pathstr,plotting_template(n).name,ext] = fileparts(tmp.name);
n=n+1;
end
end
if ~exist('plotting_template','var')
disp('No power measurement files found')
varargout{1} = [];
return
end

% sort plotting_template data by the date/time
date_list = [plotting_template.date];
[~,order] = sort(datenum(date_list,'dd-mm-yyyy hh:MM:ss'),1,'ascend');
plotting_template = plotting_template(order);

if nargin > 1 % Optional variable for selecting starting date
startDate = datetime(varargin{1});
startIndex = 1;

while [plotting_template(startIndex).date] < startDate
startIndex = startIndex + 1;
end

plotting_template = plotting_template(startIndex:end);
end

if isequal(plotting_template(:).wavelength) % if wavelength is the same
for ii = 1:length(plotting_template)
if contains(plotting_template(ii).full_path_to_data, '.mat')
% If power data is found, load it and find max value
powerData(ii) = load(plotting_template(ii).full_path_to_data);
maxPower(ii) = powerData(ii).powerMeasurements.observedPower(end);

% Plot the power curves for each date
hold on
subplot(2,1,1)
plot([0:5:100],powerData(ii).powerMeasurements.observedPower,'.')
legend(plotting_template.date,'location', 'Northwest')
title(cell2mat(['Power at ', string(plotting_template(1).wavelength), 'nm']))
xlabel('Percent power')
ylabel('Power (mW)')
hold off
end
end
% Plot the maximum laser output over time
subplot(2,1,2)
plot(maxPower, '-*')
title('Maximum laser power')
xlabels = {plotting_template.date};
xticks(1:length(xlabels))
xticklabels(xlabels)
ylabel('Maximum power (mW)')

% elseif isequal(plotting_template(:).wavelength,selectWavelength)
% elseif ~isempty(selectWavelength)
% tempStruc = find(plotting_template(:).wavelength == selectWavelength);
% % if wavelength given in varargin, plot only that wavelength and dates

else
% disp('Different wavelengths found')
% powerData = [];
% maxPower = [];
for i = 1:length(plotting_template)
allWave(i) = plotting_template(i).wavelength;
end
wavelengthVals = unique(allWave);
numWavelength = length(wavelengthVals);
maxPower = cell(zeros(1:length(plotting_template)),zeros(1:length(wavelengthVals))); % Need to be cells because they will have empty values
powerData = cell(zeros(1:length(plotting_template)),zeros(1:length(wavelengthVals)));
for jj = 1:length(numWavelength)
figure;
for ii = 1:length(plotting_template)

if contains(plotting_template(ii).full_path_to_data, '.mat') && isequal(plotting_template(ii).wavelength,wavelengthVals(jj))
% If power data is found, load it and find max value
powerData(ii,jj) = load(plotting_template(ii).full_path_to_data);
maxPower(ii,jj) = powerData(ii).powerMeasurements.observedPower(end);


hold on
subplot(2,1,1)
plot([0:5:100],powerData(ii,jj).powerMeasurements.observedPower,'.')
legend(plotting_template.date,'location', 'Northwest')
title(cell2mat(['Power at ', string(wavelengthVals(jj)), 'nm']))
xlabel('Percent power')
ylabel('Power (mW)')
hold off
end
end
end
% for ii = 1:length(numWavelength)
% end
% separate plots for each wavelength
% tried groupcounts
% in R2025a numunique function may work

% plot wavelengths separately, but keep all dates - so assisgn NaN to
% unused dates. Make temporary structures so values are not permanently
% replaced
end



% identify wavelength - done
% plot curves on same plot - done
% separate plots based on wavelength
% max value to see decay of laser - done






% Output of the main function
if nargout>0
out.fileName = {plotting_template(:).name};
out.date ={plotting_template(:).date};
out.powerData = powerData;
out.maxPower = maxPower;
out.wavelength = {plotting_template(:).wavelength};
varargout{1} = out;
end

end

function out = generic_generator_template(t_dir)
out.full_path_to_data = fullfile(t_dir.folder,t_dir.name);
out.type = [];
out.plotting_func = [];
out.name = [];
out.date = [];
out.wavelength = [];
end
38 changes: 38 additions & 0 deletions code/+mpqc/+longitudinal/long_standard_light_source.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
function varargout = long_standard_light_source(data_dir,varargin)
% Plots showing photon count of PMTs over time
%
%
%
%
% Isabell Whiteley, SWC AMF 2025

if nargin<1
data_dir = pwd;
end


debugPlots = true;

maintenanceFiles = dir(fullfile(data_dir,'\**\*.tif'));
n=1;

for ii=1:length(maintenanceFiles)
tmp = maintenanceFiles(ii);

if contains(tmp.name,'standard_light_source')
plotting_template(n) = generic_generator_template(tmp);
plotting_template(n).type = 'standard_light_source';
plotting_template(n).plotting_func = @mpqc.plot.electrical_noise;
plotting_template(n).date = string(datetime(regexp(tmp.name, '(\d{4}-\d{2}-\d{2})_(\d{2}-\d{2}-\d{2})','match'),'InputFormat','yyyy-MM-dd_HH-mm-ss'));
[pathstr,plotting_template(n).name,ext] = fileparts(tmp.name);
n=n+1;
end
end
if ~exist('plotting_template','var')
disp('No standard light source files found')
varargout{1} = [];
return
end


end
2 changes: 2 additions & 0 deletions code/testscript.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a = 1
[d,out] = mpqc.longitudinal.long_electrical_noise('C:\Users\Isabell\Desktop\2p-313_diagnostics\')