Added routines to analyze BEC to Droplets, BEC to Stripes.
This commit is contained in:
parent
c24ab3e994
commit
7904c3aa20
@ -0,0 +1,411 @@
|
||||
%% ===== BEC-Droplets Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'StructuralPhaseTransition', ...
|
||||
'date', '2025/08/13', ...
|
||||
'runs', [62]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToDroplets';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [1420, 2050];
|
||||
options.span = [200, 200];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 24.6;
|
||||
options.ImagingMode = 'HighIntensity';
|
||||
options.PulseDuration = 5e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = false;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = false;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Conduct spectral analysis
|
||||
spectral_analysis_results = Analyzer.extractFullAngularSpectralDistribution(od_imgs, options);
|
||||
|
||||
%% ------------------ 1. Plot of all Angular Spectral Distribution Curves ------------------
|
||||
Plotter.plotSpectralCurves( ...
|
||||
spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ... % correct θ values
|
||||
scan_reference_values, ... % correct scan params
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\theta / \pi', ...
|
||||
'YLabel', 'S(\theta)', ...
|
||||
'HighlightEvery', 10, ... % highlight every 10th repetition
|
||||
'FontName', options.font, ...
|
||||
'FigNum', 1, ...
|
||||
'TileTitlePrefix', '\alpha', ... % user-defined tile prefix
|
||||
'TileTitleSuffix', '^\circ', ... % user-defined suffix (e.g., degrees symbol)
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SpectralCurves.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 2. Plot of all Angular Spectral Distribution Curves shifted ------------------
|
||||
|
||||
% --- Recenter curves first ---
|
||||
results = Analyzer.recenterSpectralCurves(spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ...
|
||||
scan_reference_values, ...
|
||||
'SearchRange', [0 90]); % degrees
|
||||
|
||||
% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
|
||||
thetaMin = 0; % in units of pi (since you divided by pi)
|
||||
thetaMax = 1; % corresponds to pi/2
|
||||
|
||||
mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
|
||||
results.x_values = results.x_values(mask);
|
||||
|
||||
% Apply the same mask to each curve set
|
||||
for i = 1:numel(results.curves)
|
||||
results.curves{i} = results.curves{i}(:, mask);
|
||||
results.curves_mean{i} = results.curves_mean{i}(mask);
|
||||
results.curves_error{i}= results.curves_error{i}(mask);
|
||||
end
|
||||
|
||||
Plotter.plotSpectralCurvesRecentered( ...
|
||||
results, ...
|
||||
scan_reference_values, ... % correct scan params
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\theta / \pi', ...
|
||||
'YLabel', 'S(\theta)', ...
|
||||
'HighlightEvery', 10, ... % highlight every 10th repetition
|
||||
'FontName', options.font, ...
|
||||
'FigNum', 2, ...
|
||||
'TileTitlePrefix', '\alpha', ... % user-defined tile prefix
|
||||
'TileTitleSuffix', '^\circ', ... % user-defined suffix (e.g., degrees symbol)
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SpectralCurves.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 3. Plot cumulants from shifted Angular Spectral Distribution Curves ------------------
|
||||
Plotter.plotSpectralDistributionCumulants(results, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 14, ...
|
||||
'FigNum', 3, ...
|
||||
'SkipSaveFigures', false, ...
|
||||
'SaveFileName', 'SpectralCumulants.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 4. Fit shifted Angular Spectral Distribution Curves ------------------
|
||||
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
||||
spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals, ...
|
||||
'MaxTheta', pi/2, ...
|
||||
'ResidualThreshold', 0.15, ...
|
||||
'PositionThreshold', pi/15, ...
|
||||
'AmplitudeThreshold', 0.15);
|
||||
|
||||
%{
|
||||
% --- Function call ---
|
||||
plotTwoGaussianFitsOnRaw(fitResults, rawCurves, 8, 12); % 8×12 subplots per page
|
||||
|
||||
% --- Function definition ---
|
||||
function plotTwoGaussianFitsOnRaw(fitResults, rawCurves, nRows, nCols)
|
||||
% plotTwoGaussianFitsOnRaw - Plots raw curves and overlays valid two-Gaussian fits.
|
||||
%
|
||||
% Inputs:
|
||||
% fitResults - struct array from fitTwoGaussianCurvesToAngularSpectralDistribution (may contain fits)
|
||||
% rawCurves - struct array with fields:
|
||||
% .x - raw curve
|
||||
% .theta - corresponding theta values
|
||||
% nRows - number of subplot rows per page (default: 4)
|
||||
% nCols - number of subplot columns per page (default: 6)
|
||||
|
||||
if nargin < 3, nRows = 4; end
|
||||
if nargin < 4, nCols = 6; end
|
||||
|
||||
Ncurves = numel(rawCurves);
|
||||
plotsPerPage = nRows * nCols;
|
||||
pageNum = 1;
|
||||
|
||||
for k = 1:Ncurves
|
||||
% --- New figure/page if needed ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
figure('Name', sprintf('Curves Page %d', pageNum), ...
|
||||
'NumberTitle', 'off', 'Color', 'w');
|
||||
pageNum = pageNum + 1;
|
||||
end
|
||||
|
||||
% --- Select subplot ---
|
||||
subplot(nRows, nCols, mod(k-1, plotsPerPage)+1);
|
||||
hold on; grid on; box on;
|
||||
|
||||
% --- Plot raw curve ---
|
||||
xRaw = rawCurves(k).x;
|
||||
thetaRaw = rawCurves(k).theta;
|
||||
if ~isempty(xRaw) && ~isempty(thetaRaw) && all(isfinite(xRaw))
|
||||
plot(thetaRaw, xRaw, 'k.-', 'LineWidth', 1, 'DisplayName', 'Raw data');
|
||||
end
|
||||
|
||||
% --- Overlay fit if valid ---
|
||||
if k <= numel(fitResults)
|
||||
fit = fitResults(k);
|
||||
if isfield(fit, 'isValid') && fit.isValid ...
|
||||
&& ~isempty(fit.yFit) && ~isempty(fit.thetaFine)
|
||||
plot(fit.thetaFine, fit.yFit, 'r-', 'LineWidth', 1.2, 'DisplayName', 'Two-Gaussian fit');
|
||||
end
|
||||
end
|
||||
|
||||
% --- Formatting ---
|
||||
xlabel('\theta (rad)');
|
||||
ylabel('Normalized amplitude');
|
||||
title(sprintf('Curve %d', k), 'FontSize', 10);
|
||||
|
||||
% --- Legend once per page ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
legend('Location', 'best', 'FontSize', 8);
|
||||
end
|
||||
|
||||
hold off;
|
||||
end
|
||||
end
|
||||
%}
|
||||
|
||||
%% ------------------ 5. Plot fit parameters - amplitude ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'A2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak amplitude', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 4, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakAmplitudePDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.6]);
|
||||
|
||||
%% ------------------ 6. Plot fit parameters - position ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'mu2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak position (\theta, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 5, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakPositionPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.6]);
|
||||
|
||||
%% ------------------ 7. Plot fit parameters - width ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'sigma2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak width (\sigma, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 6, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakWidthPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.6]);
|
||||
|
||||
%% ------------------ 8. Plot fit parameters of mean shifted Angular Spectral Distribution Curves ------------------
|
||||
|
||||
% --- Recenter curves first ---
|
||||
results = Analyzer.recenterSpectralCurves(spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ...
|
||||
scan_reference_values, ...
|
||||
'SearchRange', [0 90]); % degrees
|
||||
|
||||
% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
|
||||
thetaMin = 0; % in units of pi (since you divided by pi)
|
||||
thetaMax = 1; % corresponds to pi/2
|
||||
|
||||
mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
|
||||
results.x_values = results.x_values(mask);
|
||||
|
||||
% Apply the same mask to each curve set
|
||||
for i = 1:numel(results.curves)
|
||||
results.curves_mean{i} = results.curves_mean{i}(mask);
|
||||
end
|
||||
|
||||
% --- Fit two-Gaussian model to mean curves ---
|
||||
[fitResultsMean, ~] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
||||
results.curves_mean, ...
|
||||
results.x_values*pi, ...
|
||||
'MaxTheta', pi/2, ...
|
||||
'ResidualThreshold', 0.15, ...
|
||||
'PositionThreshold', pi/15, ...
|
||||
'AmplitudeThreshold', 0.15, ...
|
||||
'RecenterCurves', false);
|
||||
|
||||
% --- Prepare parameter values ---
|
||||
N_params = numel(fitResultsMean);
|
||||
amp2_vals = nan(1, N_params);
|
||||
mu2_vals = nan(1, N_params);
|
||||
sigma2_vals = nan(1, N_params);
|
||||
|
||||
for i = 1:N_params
|
||||
pFit = fitResultsMean(i).pFit;
|
||||
if all(~isnan(pFit))
|
||||
% Successful fit → use fitted values
|
||||
amp2_vals(i) = pFit(4);
|
||||
mu2_vals(i) = pFit(5);
|
||||
sigma2_vals(i) = pFit(6);
|
||||
|
||||
else
|
||||
% Fit failed → leave as NaN (skipped automatically)
|
||||
continue;
|
||||
end
|
||||
end
|
||||
|
||||
% --- Plot peak amplitude mean with SEM ---
|
||||
Plotter.plotMeanWithSE(scan_reference_values, amp2_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak amplitude', ...
|
||||
'FigNum', 7, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakAmplitudeMeanWithSEM.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
% --- Plot peak position mean with SEM ---
|
||||
Plotter.plotMeanWithSE(scan_reference_values, mu2_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak position (\theta, rad)', ...
|
||||
'FigNum', 8, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakPositionMeanWithSEM.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
% --- Plot peak width mean with SEM ---
|
||||
Plotter.plotMeanWithSE(scan_reference_values, sigma2_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak width (\sigma, rad)', ...
|
||||
'FigNum', 9, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakWidthMeanWithSEM.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% Inspect individual realizations of a single parameter
|
||||
|
||||
% --- Recenter curves first ---
|
||||
results = Analyzer.recenterSpectralCurves( ...
|
||||
spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ...
|
||||
scan_reference_values, ...
|
||||
'SearchRange', [0 90]); % degrees
|
||||
|
||||
% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
|
||||
thetaMin = 0; % in units of pi (since you divided by pi)
|
||||
thetaMax = 1; % corresponds to pi/2
|
||||
|
||||
mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
|
||||
results.x_values = results.x_values(mask);
|
||||
|
||||
% --- Apply the same mask to each curve set (1x10 cell, each 60x180) ---
|
||||
for i = 1:numel(results.curves)
|
||||
results.curves{i} = results.curves{i}(:, mask);
|
||||
end
|
||||
|
||||
% --- Convert selected curve set (e.g., 5th) into 1x60 cell array of 1xN row vectors ---
|
||||
paramIdx = 1; % <-- choose which scan point or curve set to analyze
|
||||
curves_matrix = results.curves{paramIdx}; % 60xN numeric
|
||||
curves_cell = num2cell(curves_matrix, 2); % 1x60 cell array
|
||||
curves_cell = cellfun(@(x) x(:).', curves_cell, 'UniformOutput', false); % ensure 1xN row vectors
|
||||
|
||||
% --- Fit two-Gaussian model to these curves ---
|
||||
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
||||
curves_cell, ...
|
||||
results.x_values*pi, ...
|
||||
'MaxTheta', pi/2, ...
|
||||
'ResidualThreshold', 0.15, ...
|
||||
'PositionThreshold', pi/15, ...
|
||||
'AmplitudeThreshold', 0.15, ...
|
||||
'RecenterCurves', false);
|
181
Data-Analyzer/+Scripts/BECToDroplets/runCorrelationAnalysis.m
Normal file
181
Data-Analyzer/+Scripts/BECToDroplets/runCorrelationAnalysis.m
Normal file
@ -0,0 +1,181 @@
|
||||
%% ===== BEC-Droplets Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'StructuralPhaseTransition', ...
|
||||
'date', '2025/08/13', ...
|
||||
'runs', [62]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToDroplets';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [1420, 2050];
|
||||
options.span = [200, 200];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 24.6;
|
||||
options.ImagingMode = 'HighIntensity';
|
||||
options.PulseDuration = 5e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = false;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = false;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Conduct correlation analysis
|
||||
options.skipLivePlot = true;
|
||||
g2_analysis_results = Analyzer.conductCorrelationAnalysis(od_imgs, scan_parameter_values, options);
|
||||
|
||||
%% Analyze G2 matrices
|
||||
|
||||
% ROI definition
|
||||
options.roi.center = [3, 3]; % center of ROI in µm (x0, y0)
|
||||
options.roi.size = [3, 11]; % width and height in µm
|
||||
options.roi.angle = pi/4; % rotation angle (radians, CCW)
|
||||
options.threshold = 0.85;
|
||||
options.fitDeviationThreshold = 0.9;
|
||||
|
||||
% Plot control
|
||||
options.skipLivePlot = true;
|
||||
analysis_results = Analyzer.analyzeG2Structures(g2_analysis_results, options);
|
||||
|
||||
%% Plot raw OD images and the corresponding real space g2 matrix
|
||||
|
||||
options.skipLivePlot = true;
|
||||
options.skipSaveFigures = true;
|
||||
|
||||
Plotter.plotODG2withAnalysis(od_imgs, scan_parameter_values, g2_analysis_results, analysis_results, options, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 14, ...
|
||||
'ShowOverlays', true, ...
|
||||
'RepsPerPage', 5, ...
|
||||
'SaveDirectory', options.saveDirectory , ...
|
||||
'SkipLivePlot', options.skipLivePlot, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures);
|
||||
|
||||
%% Plot mean and standard error of anisotropy
|
||||
|
||||
Plotter.plotMeanWithSE(scan_parameter_values, analysis_results.anisotropy_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'YLim', [0,6], ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Anisotropy of correlation peaks', ...
|
||||
'FigNum', 1, ...
|
||||
'FontName', options.font, ...
|
||||
'SaveFileName', 'RadialSpectralContrast.fig', ...
|
||||
'SaveDirectory', options.saveDirectory , ...
|
||||
'SkipSaveFigures', options.skipSaveFigures);
|
||||
|
||||
%% Plot distribution of anisotropy
|
||||
grouped_data = groupDataByScan(scan_parameter_values, analysis_results.anisotropy_vals);
|
||||
|
||||
% call plotPDF
|
||||
Plotter.plotPDF(grouped_data, ...
|
||||
scan_reference_values, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Anisotropy of correlation peaks', ...
|
||||
'FigNum', 2, ...
|
||||
'FontName', options.font, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'PDF_MaxG2AcrossTransition.fig', ...
|
||||
'SaveDirectory', options.saveDirectory , ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'DataRange', [0 10], ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values) max(scan_reference_values)]);
|
||||
|
||||
function groupedData = groupDataByScan(scan_values, data_values)
|
||||
%% groupByScanValues
|
||||
% Groups data according to unique scan parameter values.
|
||||
%
|
||||
% Inputs:
|
||||
% scan_values : array of scan parameters (length = N_reps * N_scan)
|
||||
% data_values : numeric array or cell array of measured values
|
||||
% (same length as scan_values)
|
||||
%
|
||||
% Output:
|
||||
% groupedData : 1 x N_unique cell array, each containing all repetitions
|
||||
% corresponding to a unique scan value
|
||||
|
||||
[unique_vals, ~, idx] = unique(scan_values, 'stable'); % preserve order
|
||||
groupedData = cell(1, numel(unique_vals));
|
||||
|
||||
for i = 1:numel(unique_vals)
|
||||
if iscell(data_values)
|
||||
group = data_values(idx == i);
|
||||
groupedData{i} = [group{:}]; % concatenate if nested cells
|
||||
else
|
||||
groupedData{i} = data_values(idx == i);
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,263 @@
|
||||
%% ===== BEC-Droplets Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'StructuralPhaseTransition', ...
|
||||
'date', '2025/08/13', ...
|
||||
'runs', [62]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToDroplets';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [1420, 2050];
|
||||
options.span = [200, 200];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 24.6;
|
||||
options.ImagingMode = 'HighIntensity';
|
||||
options.PulseDuration = 5e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = false;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = false;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Conduct spectral analysis
|
||||
spectral_analysis_results = Analyzer.extractFullRadialSpectralDistribution(od_imgs, options);
|
||||
|
||||
%% ------------------ 1. Plot of all Radial Spectral Distribution Curves ------------------
|
||||
Plotter.plotSpectralCurves( ...
|
||||
spectral_analysis_results.S_k_all, ...
|
||||
spectral_analysis_results.k_rho_vals, ...
|
||||
scan_reference_values, ... % correct scan params
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', 'k_\rho', ...
|
||||
'YLabel', 'S(k_\rho)', ...
|
||||
'YScale', 'log', ...
|
||||
'XLim', [min(spectral_analysis_results.k_rho_vals), max(spectral_analysis_results.k_rho_vals)], ...
|
||||
'HighlightEvery', 10, ... % highlight every 10th repetition
|
||||
'FontName', options.font, ...
|
||||
'FigNum', 1, ...
|
||||
'TileTitlePrefix', '\alpha', ... % user-defined tile prefix
|
||||
'TileTitleSuffix', '^\circ', ... % user-defined suffix (e.g., degrees symbol)
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SpectralCurves.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 2. Fit truncated Radial Spectral Distribution Curves ------------------
|
||||
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToRadialSpectralDistribution(...
|
||||
spectral_analysis_results.S_k_all, ... % radial spectral curves
|
||||
spectral_analysis_results.k_rho_vals, ... % corresponding k_rho values
|
||||
'KRhoRange', [0, 3], ... % truncate curves to 0 <= k_rho <= 3
|
||||
'AmplitudeRange', [0, 0.5], ... % truncate curves to y >= 0 (all amplitudes)
|
||||
'ResidualThreshold', 0.15, ... % maximum allowed residual
|
||||
'PositionThreshold', 0.5, ... % minimum separation between peaks
|
||||
'AmplitudeThreshold', 0.015); % minimum secondary peak fraction
|
||||
%%
|
||||
%{
|
||||
% --- Function call ---
|
||||
plotTwoGaussianFitsOnRaw(fitResults, rawCurves, 8, 12); % 8×12 subplots per page
|
||||
|
||||
% --- Function definition ---
|
||||
function plotTwoGaussianFitsOnRaw(fitResults, rawCurves, nRows, nCols)
|
||||
%% plotTwoGaussianFitsOnRaw
|
||||
% Plots raw radial spectral curves with their two-Gaussian fits.
|
||||
%
|
||||
% Inputs:
|
||||
% fitResults - struct array from fitTwoGaussianCurvesToRadialSpectralDistribution
|
||||
% rawCurves - struct array with fields:
|
||||
% .x - raw normalized amplitudes
|
||||
% .k - corresponding k_rho values
|
||||
% nRows - number of subplot rows per page (default: 8)
|
||||
% nCols - number of subplot columns per page (default: 12)
|
||||
|
||||
if nargin < 3, nRows = 8; end
|
||||
if nargin < 4, nCols = 12; end
|
||||
|
||||
Ncurves = numel(rawCurves);
|
||||
plotsPerPage = nRows * nCols;
|
||||
pageNum = 1;
|
||||
|
||||
for k = 1:Ncurves
|
||||
% --- Create new figure page if needed ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
figure('Name', sprintf('Radial Spectra Page %d', pageNum), ...
|
||||
'NumberTitle', 'off', 'Color', 'w', ...
|
||||
'Position', [50 50 1600 900]);
|
||||
pageNum = pageNum + 1;
|
||||
end
|
||||
|
||||
% --- Subplot selection ---
|
||||
subplot(nRows, nCols, mod(k-1, plotsPerPage) + 1);
|
||||
hold on; grid on; box on;
|
||||
|
||||
% --- Plot raw curve ---
|
||||
if isfield(rawCurves, 'x') && isfield(rawCurves, 'k') && ...
|
||||
~isempty(rawCurves(k).x) && all(isfinite(rawCurves(k).x))
|
||||
plot(rawCurves(k).k, rawCurves(k).x, 'k.-', ...
|
||||
'LineWidth', 1, 'DisplayName', 'Raw data');
|
||||
end
|
||||
|
||||
% --- Overlay fit if valid ---
|
||||
if k <= numel(fitResults)
|
||||
fit = fitResults(k);
|
||||
if isfield(fit, 'isValid') && fit.isValid && ...
|
||||
isfield(fit, 'kFine') && isfield(fit, 'yFit') && ...
|
||||
~isempty(fit.kFine) && all(isfinite(fit.yFit))
|
||||
plot(fit.kFine, fit.yFit, 'r-', ...
|
||||
'LineWidth', 1.2, 'DisplayName', 'Two-Gaussian fit');
|
||||
end
|
||||
end
|
||||
|
||||
% --- Labels and title ---
|
||||
xlabel('k_\rho', 'FontSize', 10);
|
||||
ylabel('Normalized amplitude', 'FontSize', 10);
|
||||
title(sprintf('Curve %d', k), 'FontSize', 9, 'Interpreter', 'none');
|
||||
|
||||
% --- Legend on first subplot of each page ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
legend('Location', 'best', 'FontSize', 8);
|
||||
end
|
||||
|
||||
hold off;
|
||||
end
|
||||
end
|
||||
%}
|
||||
%% ------------------ 3. Plot fit parameters - amplitude ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'A2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak amplitude', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 4, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakAmplitudePDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 0.06]);
|
||||
|
||||
%% ------------------ 4. Plot fit parameters - position ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'mu2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak position (\theta, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 5, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakPositionPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [1, 2]);
|
||||
%% ------------------ 5. Plot fit parameters - width ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'sigma2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak width (\sigma, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 6, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakWidthPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.5]);
|
||||
|
||||
%% ------------------ 6. Plot secondary Gaussian range ------------------
|
||||
Plotter.plotSecondaryGaussianRange(fitResults, scan_reference_values, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', '\mu_2 \pm \sigma_2 (rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 8, ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'OverlayMeanSEM', true, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryGaussianRange.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
|
@ -0,0 +1,411 @@
|
||||
%% ===== BEC-Stripes Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'StructuralPhaseTransition', ...
|
||||
'date', '2025/08/15', ...
|
||||
'runs', [3]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToStripes';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [1420, 2050];
|
||||
options.span = [200, 200];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 24.6;
|
||||
options.ImagingMode = 'HighIntensity';
|
||||
options.PulseDuration = 5e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = false;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = false;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Conduct spectral analysis
|
||||
spectral_analysis_results = Analyzer.extractFullAngularSpectralDistribution(od_imgs, options);
|
||||
|
||||
%% ------------------ 1. Plot of all Angular Spectral Distribution Curves ------------------
|
||||
Plotter.plotSpectralCurves( ...
|
||||
spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ... % correct θ values
|
||||
scan_reference_values, ... % correct scan params
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\theta / \pi', ...
|
||||
'YLabel', 'S(\theta)', ...
|
||||
'HighlightEvery', 10, ... % highlight every 10th repetition
|
||||
'FontName', options.font, ...
|
||||
'FigNum', 1, ...
|
||||
'TileTitlePrefix', '\alpha', ... % user-defined tile prefix
|
||||
'TileTitleSuffix', '^\circ', ... % user-defined suffix (e.g., degrees symbol)
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SpectralCurves.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 2. Plot of all Angular Spectral Distribution Curves shifted ------------------
|
||||
|
||||
% --- Recenter curves first ---
|
||||
results = Analyzer.recenterSpectralCurves(spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ...
|
||||
scan_reference_values, ...
|
||||
'SearchRange', [0 90]); % degrees
|
||||
|
||||
% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
|
||||
thetaMin = 0; % in units of pi (since you divided by pi)
|
||||
thetaMax = 1; % corresponds to pi/2
|
||||
|
||||
mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
|
||||
results.x_values = results.x_values(mask);
|
||||
|
||||
% Apply the same mask to each curve set
|
||||
for i = 1:numel(results.curves)
|
||||
results.curves{i} = results.curves{i}(:, mask);
|
||||
results.curves_mean{i} = results.curves_mean{i}(mask);
|
||||
results.curves_error{i}= results.curves_error{i}(mask);
|
||||
end
|
||||
|
||||
Plotter.plotSpectralCurvesRecentered( ...
|
||||
results, ...
|
||||
scan_reference_values, ... % correct scan params
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\theta / \pi', ...
|
||||
'YLabel', 'S(\theta)', ...
|
||||
'HighlightEvery', 10, ... % highlight every 10th repetition
|
||||
'FontName', options.font, ...
|
||||
'FigNum', 2, ...
|
||||
'TileTitlePrefix', '\alpha', ... % user-defined tile prefix
|
||||
'TileTitleSuffix', '^\circ', ... % user-defined suffix (e.g., degrees symbol)
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SpectralCurves.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 3. Plot cumulants from shifted Angular Spectral Distribution Curves ------------------
|
||||
Plotter.plotSpectralDistributionCumulants(results, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 14, ...
|
||||
'FigNum', 3, ...
|
||||
'SkipSaveFigures', false, ...
|
||||
'SaveFileName', 'SpectralCumulants.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 4. Fit shifted Angular Spectral Distribution Curves ------------------
|
||||
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
||||
spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals, ...
|
||||
'MaxTheta', pi/2, ...
|
||||
'ResidualThreshold', 0.15, ...
|
||||
'PositionThreshold', pi/15, ...
|
||||
'AmplitudeThreshold', 0.15);
|
||||
|
||||
%{
|
||||
% --- Function call ---
|
||||
plotTwoGaussianFitsOnRaw(fitResults, rawCurves, 8, 12); % 8×12 subplots per page
|
||||
|
||||
% --- Function definition ---
|
||||
function plotTwoGaussianFitsOnRaw(fitResults, rawCurves, nRows, nCols)
|
||||
% plotTwoGaussianFitsOnRaw - Plots raw curves and overlays valid two-Gaussian fits.
|
||||
%
|
||||
% Inputs:
|
||||
% fitResults - struct array from fitTwoGaussianCurvesToAngularSpectralDistribution (may contain fits)
|
||||
% rawCurves - struct array with fields:
|
||||
% .x - raw curve
|
||||
% .theta - corresponding theta values
|
||||
% nRows - number of subplot rows per page (default: 4)
|
||||
% nCols - number of subplot columns per page (default: 6)
|
||||
|
||||
if nargin < 3, nRows = 4; end
|
||||
if nargin < 4, nCols = 6; end
|
||||
|
||||
Ncurves = numel(rawCurves);
|
||||
plotsPerPage = nRows * nCols;
|
||||
pageNum = 1;
|
||||
|
||||
for k = 1:Ncurves
|
||||
% --- New figure/page if needed ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
figure('Name', sprintf('Curves Page %d', pageNum), ...
|
||||
'NumberTitle', 'off', 'Color', 'w');
|
||||
pageNum = pageNum + 1;
|
||||
end
|
||||
|
||||
% --- Select subplot ---
|
||||
subplot(nRows, nCols, mod(k-1, plotsPerPage)+1);
|
||||
hold on; grid on; box on;
|
||||
|
||||
% --- Plot raw curve ---
|
||||
xRaw = rawCurves(k).x;
|
||||
thetaRaw = rawCurves(k).theta;
|
||||
if ~isempty(xRaw) && ~isempty(thetaRaw) && all(isfinite(xRaw))
|
||||
plot(thetaRaw, xRaw, 'k.-', 'LineWidth', 1, 'DisplayName', 'Raw data');
|
||||
end
|
||||
|
||||
% --- Overlay fit if valid ---
|
||||
if k <= numel(fitResults)
|
||||
fit = fitResults(k);
|
||||
if isfield(fit, 'isValid') && fit.isValid ...
|
||||
&& ~isempty(fit.yFit) && ~isempty(fit.thetaFine)
|
||||
plot(fit.thetaFine, fit.yFit, 'r-', 'LineWidth', 1.2, 'DisplayName', 'Two-Gaussian fit');
|
||||
end
|
||||
end
|
||||
|
||||
% --- Formatting ---
|
||||
xlabel('\theta (rad)');
|
||||
ylabel('Normalized amplitude');
|
||||
title(sprintf('Curve %d', k), 'FontSize', 10);
|
||||
|
||||
% --- Legend once per page ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
legend('Location', 'best', 'FontSize', 8);
|
||||
end
|
||||
|
||||
hold off;
|
||||
end
|
||||
end
|
||||
%}
|
||||
|
||||
%% ------------------ 5. Plot fit parameters - amplitude ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'A2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak amplitude', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 4, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakAmplitudePDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.6]);
|
||||
|
||||
%% ------------------ 6. Plot fit parameters - position ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'mu2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak position (\theta, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 5, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakPositionPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.6]);
|
||||
|
||||
%% ------------------ 7. Plot fit parameters - width ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'sigma2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak width (\sigma, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 6, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakWidthPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.6]);
|
||||
|
||||
%% ------------------ 8. Plot fit parameters of mean shifted Angular Spectral Distribution Curves ------------------
|
||||
|
||||
% --- Recenter curves first ---
|
||||
results = Analyzer.recenterSpectralCurves(spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ...
|
||||
scan_reference_values, ...
|
||||
'SearchRange', [0 90]); % degrees
|
||||
|
||||
% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
|
||||
thetaMin = 0; % in units of pi (since you divided by pi)
|
||||
thetaMax = 1; % corresponds to pi/2
|
||||
|
||||
mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
|
||||
results.x_values = results.x_values(mask);
|
||||
|
||||
% Apply the same mask to each curve set
|
||||
for i = 1:numel(results.curves)
|
||||
results.curves_mean{i} = results.curves_mean{i}(mask);
|
||||
end
|
||||
|
||||
% --- Fit two-Gaussian model to mean curves ---
|
||||
[fitResultsMean, ~] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
||||
results.curves_mean, ...
|
||||
results.x_values*pi, ...
|
||||
'MaxTheta', pi/2, ...
|
||||
'ResidualThreshold', 0.15, ...
|
||||
'PositionThreshold', pi/15, ...
|
||||
'AmplitudeThreshold', 0.15, ...
|
||||
'RecenterCurves', false);
|
||||
|
||||
% --- Prepare parameter values ---
|
||||
N_params = numel(fitResultsMean);
|
||||
amp2_vals = nan(1, N_params);
|
||||
mu2_vals = nan(1, N_params);
|
||||
sigma2_vals = nan(1, N_params);
|
||||
|
||||
for i = 1:N_params
|
||||
pFit = fitResultsMean(i).pFit;
|
||||
if all(~isnan(pFit))
|
||||
% Successful fit → use fitted values
|
||||
amp2_vals(i) = pFit(4);
|
||||
mu2_vals(i) = pFit(5);
|
||||
sigma2_vals(i) = pFit(6);
|
||||
|
||||
else
|
||||
% Fit failed → leave as NaN (skipped automatically)
|
||||
continue;
|
||||
end
|
||||
end
|
||||
|
||||
% --- Plot peak amplitude mean with SEM ---
|
||||
Plotter.plotMeanWithSE(scan_reference_values, amp2_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak amplitude', ...
|
||||
'FigNum', 7, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakAmplitudeMeanWithSEM.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
% --- Plot peak position mean with SEM ---
|
||||
Plotter.plotMeanWithSE(scan_reference_values, mu2_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak position (\theta, rad)', ...
|
||||
'FigNum', 8, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakPositionMeanWithSEM.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
% --- Plot peak width mean with SEM ---
|
||||
Plotter.plotMeanWithSE(scan_reference_values, sigma2_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak width (\sigma, rad)', ...
|
||||
'FigNum', 9, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakWidthMeanWithSEM.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% Inspect individual realizations of a single parameter
|
||||
|
||||
% --- Recenter curves first ---
|
||||
results = Analyzer.recenterSpectralCurves( ...
|
||||
spectral_analysis_results.S_theta_norm_all, ...
|
||||
spectral_analysis_results.theta_vals/pi, ...
|
||||
scan_reference_values, ...
|
||||
'SearchRange', [0 90]); % degrees
|
||||
|
||||
% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
|
||||
thetaMin = 0; % in units of pi (since you divided by pi)
|
||||
thetaMax = 1; % corresponds to pi/2
|
||||
|
||||
mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
|
||||
results.x_values = results.x_values(mask);
|
||||
|
||||
% --- Apply the same mask to each curve set (1x10 cell, each 60x180) ---
|
||||
for i = 1:numel(results.curves)
|
||||
results.curves{i} = results.curves{i}(:, mask);
|
||||
end
|
||||
|
||||
% --- Convert selected curve set (e.g., 5th) into 1x60 cell array of 1xN row vectors ---
|
||||
paramIdx = 1; % <-- choose which scan point or curve set to analyze
|
||||
curves_matrix = results.curves{paramIdx}; % 60xN numeric
|
||||
curves_cell = num2cell(curves_matrix, 2); % 1x60 cell array
|
||||
curves_cell = cellfun(@(x) x(:).', curves_cell, 'UniformOutput', false); % ensure 1xN row vectors
|
||||
|
||||
% --- Fit two-Gaussian model to these curves ---
|
||||
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
||||
curves_cell, ...
|
||||
results.x_values*pi, ...
|
||||
'MaxTheta', pi/2, ...
|
||||
'ResidualThreshold', 0.15, ...
|
||||
'PositionThreshold', pi/15, ...
|
||||
'AmplitudeThreshold', 0.15, ...
|
||||
'RecenterCurves', false);
|
181
Data-Analyzer/+Scripts/BECToStripes/runCorrelationAnalysis.m
Normal file
181
Data-Analyzer/+Scripts/BECToStripes/runCorrelationAnalysis.m
Normal file
@ -0,0 +1,181 @@
|
||||
%% ===== BEC-Stripes Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'StructuralPhaseTransition', ...
|
||||
'date', '2025/08/15', ...
|
||||
'runs', [3]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToStripes';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [1420, 2050];
|
||||
options.span = [200, 200];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 24.6;
|
||||
options.ImagingMode = 'HighIntensity';
|
||||
options.PulseDuration = 5e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = false;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = false;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Conduct correlation analysis
|
||||
options.skipLivePlot = true;
|
||||
g2_analysis_results = Analyzer.conductCorrelationAnalysis(od_imgs, scan_parameter_values, options);
|
||||
|
||||
%% Analyze G2 matrices
|
||||
|
||||
% ROI definition
|
||||
options.roi.center = [3, 3]; % center of ROI in µm (x0, y0)
|
||||
options.roi.size = [3, 11]; % width and height in µm
|
||||
options.roi.angle = pi/4; % rotation angle (radians, CCW)
|
||||
options.threshold = 0.85;
|
||||
options.fitDeviationThreshold = 0.9;
|
||||
|
||||
% Plot control
|
||||
options.skipLivePlot = true;
|
||||
analysis_results = Analyzer.analyzeG2Structures(g2_analysis_results, options);
|
||||
|
||||
%% Plot raw OD images and the corresponding real space g2 matrix
|
||||
|
||||
options.skipLivePlot = true;
|
||||
options.skipSaveFigures = true;
|
||||
|
||||
Plotter.plotODG2withAnalysis(od_imgs, scan_parameter_values, g2_analysis_results, analysis_results, options, ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 14, ...
|
||||
'ShowOverlays', true, ...
|
||||
'RepsPerPage', 5, ...
|
||||
'SaveDirectory', options.saveDirectory , ...
|
||||
'SkipLivePlot', options.skipLivePlot, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures);
|
||||
|
||||
%% Plot mean and standard error of anisotropy
|
||||
|
||||
Plotter.plotMeanWithSE(scan_parameter_values, analysis_results.anisotropy_vals, ...
|
||||
'Title', options.titleString, ...
|
||||
'YLim', [0,6], ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Anisotropy of correlation peaks', ...
|
||||
'FigNum', 1, ...
|
||||
'FontName', options.font, ...
|
||||
'SaveFileName', 'RadialSpectralContrast.fig', ...
|
||||
'SaveDirectory', options.saveDirectory , ...
|
||||
'SkipSaveFigures', options.skipSaveFigures);
|
||||
|
||||
%% Plot distribution of anisotropy
|
||||
grouped_data = groupDataByScan(scan_parameter_values, analysis_results.anisotropy_vals);
|
||||
|
||||
% call plotPDF
|
||||
Plotter.plotPDF(grouped_data, ...
|
||||
scan_reference_values, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Anisotropy of correlation peaks', ...
|
||||
'FigNum', 2, ...
|
||||
'FontName', options.font, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'PDF_MaxG2AcrossTransition.fig', ...
|
||||
'SaveDirectory', options.saveDirectory , ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'DataRange', [0 10], ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values) max(scan_reference_values)]);
|
||||
|
||||
function groupedData = groupDataByScan(scan_values, data_values)
|
||||
%% groupByScanValues
|
||||
% Groups data according to unique scan parameter values.
|
||||
%
|
||||
% Inputs:
|
||||
% scan_values : array of scan parameters (length = N_reps * N_scan)
|
||||
% data_values : numeric array or cell array of measured values
|
||||
% (same length as scan_values)
|
||||
%
|
||||
% Output:
|
||||
% groupedData : 1 x N_unique cell array, each containing all repetitions
|
||||
% corresponding to a unique scan value
|
||||
|
||||
[unique_vals, ~, idx] = unique(scan_values, 'stable'); % preserve order
|
||||
groupedData = cell(1, numel(unique_vals));
|
||||
|
||||
for i = 1:numel(unique_vals)
|
||||
if iscell(data_values)
|
||||
group = data_values(idx == i);
|
||||
groupedData{i} = [group{:}]; % concatenate if nested cells
|
||||
else
|
||||
groupedData{i} = data_values(idx == i);
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,263 @@
|
||||
%% ===== BEC-Stripes Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'StructuralPhaseTransition', ...
|
||||
'date', '2025/08/15', ...
|
||||
'runs', [3]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToStripes';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [1420, 2050];
|
||||
options.span = [200, 200];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 24.6;
|
||||
options.ImagingMode = 'HighIntensity';
|
||||
options.PulseDuration = 5e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = false;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = false;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Conduct spectral analysis
|
||||
spectral_analysis_results = Analyzer.extractFullRadialSpectralDistribution(od_imgs, options);
|
||||
|
||||
%% ------------------ 1. Plot of all Radial Spectral Distribution Curves ------------------
|
||||
Plotter.plotSpectralCurves( ...
|
||||
spectral_analysis_results.S_k_all, ...
|
||||
spectral_analysis_results.k_rho_vals, ...
|
||||
scan_reference_values, ... % correct scan params
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', 'k_\rho', ...
|
||||
'YLabel', 'S(k_\rho)', ...
|
||||
'YScale', 'log', ...
|
||||
'XLim', [min(spectral_analysis_results.k_rho_vals), max(spectral_analysis_results.k_rho_vals)], ...
|
||||
'HighlightEvery', 10, ... % highlight every 10th repetition
|
||||
'FontName', options.font, ...
|
||||
'FigNum', 1, ...
|
||||
'TileTitlePrefix', '\alpha', ... % user-defined tile prefix
|
||||
'TileTitleSuffix', '^\circ', ... % user-defined suffix (e.g., degrees symbol)
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SpectralCurves.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
%% ------------------ 2. Fit truncated Radial Spectral Distribution Curves ------------------
|
||||
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToRadialSpectralDistribution(...
|
||||
spectral_analysis_results.S_k_all, ... % radial spectral curves
|
||||
spectral_analysis_results.k_rho_vals, ... % corresponding k_rho values
|
||||
'KRhoRange', [0, 3], ... % truncate curves to 0 <= k_rho <= 3
|
||||
'AmplitudeRange', [0, 0.5], ... % truncate curves to y >= 0 (all amplitudes)
|
||||
'ResidualThreshold', 0.15, ... % maximum allowed residual
|
||||
'PositionThreshold', 0.5, ... % minimum separation between peaks
|
||||
'AmplitudeThreshold', 0.015); % minimum secondary peak fraction
|
||||
%%
|
||||
%{
|
||||
% --- Function call ---
|
||||
plotTwoGaussianFitsOnRaw(fitResults, rawCurves, 8, 12); % 8×12 subplots per page
|
||||
|
||||
% --- Function definition ---
|
||||
function plotTwoGaussianFitsOnRaw(fitResults, rawCurves, nRows, nCols)
|
||||
%% plotTwoGaussianFitsOnRaw
|
||||
% Plots raw radial spectral curves with their two-Gaussian fits.
|
||||
%
|
||||
% Inputs:
|
||||
% fitResults - struct array from fitTwoGaussianCurvesToRadialSpectralDistribution
|
||||
% rawCurves - struct array with fields:
|
||||
% .x - raw normalized amplitudes
|
||||
% .k - corresponding k_rho values
|
||||
% nRows - number of subplot rows per page (default: 8)
|
||||
% nCols - number of subplot columns per page (default: 12)
|
||||
|
||||
if nargin < 3, nRows = 8; end
|
||||
if nargin < 4, nCols = 12; end
|
||||
|
||||
Ncurves = numel(rawCurves);
|
||||
plotsPerPage = nRows * nCols;
|
||||
pageNum = 1;
|
||||
|
||||
for k = 1:Ncurves
|
||||
% --- Create new figure page if needed ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
figure('Name', sprintf('Radial Spectra Page %d', pageNum), ...
|
||||
'NumberTitle', 'off', 'Color', 'w', ...
|
||||
'Position', [50 50 1600 900]);
|
||||
pageNum = pageNum + 1;
|
||||
end
|
||||
|
||||
% --- Subplot selection ---
|
||||
subplot(nRows, nCols, mod(k-1, plotsPerPage) + 1);
|
||||
hold on; grid on; box on;
|
||||
|
||||
% --- Plot raw curve ---
|
||||
if isfield(rawCurves, 'x') && isfield(rawCurves, 'k') && ...
|
||||
~isempty(rawCurves(k).x) && all(isfinite(rawCurves(k).x))
|
||||
plot(rawCurves(k).k, rawCurves(k).x, 'k.-', ...
|
||||
'LineWidth', 1, 'DisplayName', 'Raw data');
|
||||
end
|
||||
|
||||
% --- Overlay fit if valid ---
|
||||
if k <= numel(fitResults)
|
||||
fit = fitResults(k);
|
||||
if isfield(fit, 'isValid') && fit.isValid && ...
|
||||
isfield(fit, 'kFine') && isfield(fit, 'yFit') && ...
|
||||
~isempty(fit.kFine) && all(isfinite(fit.yFit))
|
||||
plot(fit.kFine, fit.yFit, 'r-', ...
|
||||
'LineWidth', 1.2, 'DisplayName', 'Two-Gaussian fit');
|
||||
end
|
||||
end
|
||||
|
||||
% --- Labels and title ---
|
||||
xlabel('k_\rho', 'FontSize', 10);
|
||||
ylabel('Normalized amplitude', 'FontSize', 10);
|
||||
title(sprintf('Curve %d', k), 'FontSize', 9, 'Interpreter', 'none');
|
||||
|
||||
% --- Legend on first subplot of each page ---
|
||||
if mod(k-1, plotsPerPage) == 0
|
||||
legend('Location', 'best', 'FontSize', 8);
|
||||
end
|
||||
|
||||
hold off;
|
||||
end
|
||||
end
|
||||
%}
|
||||
%% ------------------ 3. Plot fit parameters - amplitude ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'A2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak amplitude', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 4, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakAmplitudePDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 0.06]);
|
||||
|
||||
%% ------------------ 4. Plot fit parameters - position ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'mu2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak position (\theta, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 5, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakPositionPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [1, 2]);
|
||||
%% ------------------ 5. Plot fit parameters - width ------------------
|
||||
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'sigma2', ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', 'Secondary peak width (\sigma, rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 6, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryPeakWidthPDF.fig', ...
|
||||
'SaveDirectory', options.saveDirectory, ...
|
||||
'PlotType', 'histogram', ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
|
||||
'YLim', [0, 1.5]);
|
||||
|
||||
%% ------------------ 6. Plot secondary Gaussian range ------------------
|
||||
Plotter.plotSecondaryGaussianRange(fitResults, scan_reference_values, ...
|
||||
'Title', options.titleString, ...
|
||||
'XLabel', '\alpha (degrees)', ...
|
||||
'YLabel', '\mu_2 \pm \sigma_2 (rad)', ...
|
||||
'FontName', options.font, ...
|
||||
'FontSize', 16, ...
|
||||
'FigNum', 8, ...
|
||||
'NumberOfBins', 20, ...
|
||||
'NormalizeHistogram', true, ...
|
||||
'Colormap', @Colormaps.coolwarm, ...
|
||||
'OverlayMeanSEM', true, ...
|
||||
'SkipSaveFigures', options.skipSaveFigures, ...
|
||||
'SaveFileName', 'SecondaryGaussianRange.fig', ...
|
||||
'SaveDirectory', options.saveDirectory);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user