Calculations/Data-Analyzer/+Analyzer/analyzeAutocorrelation.m

178 lines
6.0 KiB
Matlab
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

function results = analyzeAutocorrelation(autocorrresults)
%% analyzeAutocorrelation
% Author: Karthik
% Date: 2025-09-12
% Version: 1.0
%
% Description:
% Computes features from g2(theta) curves and aggregates results.
%
% Notes:
% Assumes theta_values are given in radians.
feature_list = table();
per_group = cell(numel(autocorrresults.scan_parameter_values),1);
per_group_mean = cell(numel(autocorrresults.scan_parameter_values),1); % mean-curve features
% --- Loop over groups ---
for i = 1:numel(autocorrresults.scan_parameter_values)
curves = autocorrresults.g2_curves{i};
thetas = autocorrresults.theta_values;
group_tbl = table();
for j = 1:size(curves,1)
g2curve = curves(j,:);
feats = extractFeatures(thetas, g2curve);
group_tbl = [group_tbl; feats]; %#ok<AGROW>
feature_list = [feature_list; feats]; %#ok<AGROW>
end
per_group{i} = group_tbl;
% --- Compute mean-curve features for this group ---
meanFeatures = extractMeanG2Features(thetas, curves);
per_group_mean{i} = meanFeatures;
end
%% Compute group summaries (mean & SEM of features)
summary_table = table();
N_params = numel(autocorrresults.scan_parameter_values);
for i = 1:N_params
FT = per_group{i};
% Scalars
vars = {'A2','A4','A6','S2','Q4','H6','Contrast'};
% Group averages
meanVals = varfun(@mean, FT, 'InputVariables', vars);
semVals = varfun(@(x) std(x,0,1,'omitnan')/sqrt(size(FT,1)), ...
FT, 'InputVariables', vars);
row = table(autocorrresults.scan_parameter_values(i), ...
'VariableNames', {'scanParamVal'});
summary_table = [summary_table; [row meanVals semVals]]; %#ok<AGROW>
end
%% --- Compute cumulants from full distribution of curves ---
N_theta = numel(autocorrresults.theta_values);
g2_cums = cell(N_params,1);
for i = 1:N_params
curves = autocorrresults.g2_curves{i}; % [N_reps × N_theta]
cumul_mat = zeros(N_theta,4); % θ × first 4 cumulants
for th = 1:N_theta
g2vals = curves(:,th); % all repetitions at this theta
cumul_mat(th,:) = Calculator.computeCumulants(g2vals,4);
end
g2_cums{i} = cumul_mat;
end
%% Package results
results = struct();
results.features_all = feature_list; % all repetitions pooled
results.features_group = per_group; % per scan parameter
results.meanCurve = per_group_mean; % mean-curve contrast & peaks
results.summaries = summary_table; % group-level stats
results.g2_cumulants = g2_cums; % cumulants from full distribution
results.theta_values = autocorrresults.theta_values;
end
function features = extractFeatures(thetas, g2curve)
%% --- Step 1: Restrict theta to [0, π] ---
mask = (thetas >= 0 & thetas <= pi);
thetasWithinPi = thetas(mask);
g2WithinPi = g2curve(mask);
%% --- Step 2: DC removal ---
g2cWithinPi = g2WithinPi - mean(g2WithinPi);
%% --- Step 3: Fourier projections (n=2,4,6) ---
nlist = [2 4 6];
An = zeros(size(nlist));
for k = 1:numel(nlist)
n = nlist(k);
An(k) = abs(mean(g2cWithinPi .* exp(-1i*n*thetasWithinPi)));
end
%% --- Step 3b: Symmetry fractions ---
totalAmp = sum(An);
if totalAmp > 0
S2 = An(1)/totalAmp;
Q4 = An(2)/totalAmp;
H6 = An(3)/totalAmp;
else
S2 = 0; Q4 = 0; H6 = 0;
end
%% --- Step 4: Contrast ---
g2_restricted = g2curve((thetas >= pi/18 & thetas <= 17*pi/18));
if ~isempty(g2_restricted)
ContrastVal = (max(g2_restricted) - min(g2_restricted)) / ...
(max(g2_restricted) + min(g2_restricted));
else
ContrastVal = NaN;
end
%% --- Step 8: Package as one row table ---
features = table( ...
An(1), An(2), An(3), ContrastVal, ...
S2, Q4, H6, ...
'VariableNames', {'A2','A4','A6','Contrast','S2','Q4','H6'});
end
function meanFeatures = extractMeanG2Features(thetas, g2_curves)
% Computes contrast and peak angles from the mean g2 curve.
% g2_curves: [N_reps × N_theta] array of individual g2(theta) curves
% thetas: vector of theta values in radians
% --- Step 1: Compute mean curve ---
meanG2 = mean(g2_curves, 1);
% --- Step 2: Contrast calculation ---
maskContrast = (thetas >= pi/18 & thetas <= 17*pi/18);
meanG2_contrast = meanG2(maskContrast);
if ~isempty(meanG2_contrast)
ContrastVal = (max(meanG2_contrast) - min(meanG2_contrast)) / ...
(max(meanG2_contrast) + min(meanG2_contrast));
else
ContrastVal = NaN;
end
% --- Step 3: Peak finding restricted to (pi/18, pi/2) ---
maskPeaks = (thetas >= pi/18 & thetas <= pi/2);
meanG2_peaks = meanG2(maskPeaks);
thetas_peaks = thetas(maskPeaks);
% Dynamic prominence: 10% of range or minimum 0.05 absolute
alpha = 0.1;
minAbsProm = 0.005;
dynamicProm = max(alpha * range(meanG2_peaks), minAbsProm);
minDist = max(round(numel(thetas_peaks)/12),1); % at least 1
[~, locs] = findpeaks(meanG2_peaks, ...
'MinPeakProminence', dynamicProm, ...
'MinPeakDistance', minDist);
PeakAngles = thetas_peaks(locs); % angles of detected peaks
% --- Step 4: Fallback if no peaks detected ---
if isempty(PeakAngles)
% Use global maximum of restricted curve
[~, idxMax] = max(meanG2_peaks);
MaxPeakAngle = thetas_peaks(idxMax);
else
% Otherwise, take max of detected peaks
[~, idxMax] = max(meanG2_peaks(locs));
MaxPeakAngle = PeakAngles(idxMax);
end
% --- Step 5: Package as struct ---
meanFeatures = struct();
meanFeatures.MeanContrast = ContrastVal;
meanFeatures.MeanPeakAngles = PeakAngles;
meanFeatures.MaxPeakAngle = MaxPeakAngle;
end