237 lines
8.6 KiB
Matlab
237 lines
8.6 KiB
Matlab
function runInteractiveASDTwoGaussianFitGUI(spectral_analysis_results)
|
|
%% runInteractiveASDTwoGaussianFitGUI
|
|
% Author: Karthik
|
|
% Date: 2025-10-16
|
|
% Version: 1.0
|
|
%
|
|
% Description:
|
|
% Interactive viewer for raw angular spectral distributions.
|
|
% Lets user browse through individual curves and refit two-Gaussian models
|
|
% live as threshold parameters are adjusted.
|
|
%
|
|
% Controls:
|
|
% - Slider or ←/→ keys to browse curves
|
|
% - Editable boxes for thresholds (Residual, Position, Amplitude, MaxTheta)
|
|
% - "Re-fit" button triggers recomputation
|
|
%
|
|
% Dependencies:
|
|
% Requires Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution.m
|
|
|
|
%% --- Initialization ---
|
|
rawCurves.x = spectral_analysis_results.S_theta_norm_all;
|
|
rawCurves.theta = spectral_analysis_results.theta_vals;
|
|
|
|
% Handle input organization
|
|
if iscell(rawCurves.x)
|
|
Ncurves = numel(rawCurves.x);
|
|
else
|
|
Ncurves = size(rawCurves.x, 1);
|
|
tmp = arrayfun(@(i) struct('x', rawCurves.x(i, :), 'theta', rawCurves.theta(:)'), 1:Ncurves);
|
|
rawCurves = tmp(:);
|
|
end
|
|
|
|
% Default fit parameters
|
|
params.MaxTheta = pi/2;
|
|
params.ResidualThreshold = 0.15;
|
|
params.PositionThreshold = pi/15;
|
|
params.AmplitudeThreshold = 0.15;
|
|
|
|
currentIdx = 1;
|
|
|
|
%% --- Create figure ---
|
|
hFig = findobj('Type','figure','Tag','InteractiveTwoGaussianFit');
|
|
if isempty(hFig)
|
|
hFig = figure('Name','Angular Spectra Fit Viewer',...
|
|
'NumberTitle','off',...
|
|
'Position',[100 100 1250 800],...
|
|
'Color','w',...
|
|
'KeyPressFcn',@keyPressCallback,...
|
|
'Tag','InteractiveTwoGaussianFit');
|
|
else
|
|
figure(hFig); clf(hFig);
|
|
end
|
|
|
|
%% --- Axes for plot ---
|
|
hAx = axes('Parent',hFig,'Position',[0.08 0.1 0.55 0.8]);
|
|
grid(hAx,'on'); box(hAx,'on');
|
|
xlabel(hAx,'\theta (rad)','FontSize',12);
|
|
ylabel(hAx,'Normalized amplitude','FontSize',12);
|
|
|
|
%% --- Slider for curve index ---
|
|
hSlider = uicontrol('Style','slider','Min',1,'Max',Ncurves,'Value',1,...
|
|
'SliderStep',[1/(Ncurves-1), 10/(Ncurves-1)],...
|
|
'Units','normalized','Position',[0.1 0.02 0.5 0.03],...
|
|
'Callback',@(~,~) updatePlot());
|
|
|
|
%% --- Parameter definitions ---
|
|
paramNames = {'MaxTheta','ResidualThreshold','PositionThreshold','AmplitudeThreshold'};
|
|
paramLabels = {'Max Theta (rad)', 'Residual Threshold', 'Position Threshold (rad)', 'Amplitude Threshold'};
|
|
paramDescs = {'Upper limit for fitting range',...
|
|
'Maximum mean residual for valid fits',...
|
|
'Minimum peak separation required',...
|
|
'Minimum amplitude ratio threshold'};
|
|
|
|
nParams = numel(paramNames);
|
|
hEdit = gobjects(nParams,1);
|
|
baseY = 0.88;
|
|
|
|
%% --- Create parameter controls ---
|
|
for i = 1:nParams
|
|
yPos = baseY - 0.12*(i-1);
|
|
|
|
% Label
|
|
uicontrol('Style','text','Units','normalized',...
|
|
'Position',[0.68 yPos 0.25 0.04],...
|
|
'String',paramLabels{i},...
|
|
'HorizontalAlignment','left',...
|
|
'FontSize',10,'FontWeight','bold',...
|
|
'BackgroundColor','w');
|
|
|
|
% Edit box
|
|
hEdit(i) = uicontrol('Style','edit','Units','normalized',...
|
|
'Position',[0.9 yPos 0.07 0.045],...
|
|
'String',num2str(params.(paramNames{i}), '%.4f'),...
|
|
'Callback',@(src,~) applyParams());
|
|
|
|
% Description
|
|
uicontrol('Style','text','Units','normalized',...
|
|
'Position',[0.68 yPos - 0.04 0.35 0.03],...
|
|
'String',paramDescs{i},...
|
|
'HorizontalAlignment','left',...
|
|
'FontSize',8,'ForegroundColor',[0.4 0.4 0.4],...
|
|
'BackgroundColor','w');
|
|
end
|
|
|
|
%% --- Apply button ---
|
|
uicontrol('Style','pushbutton','Units','normalized',...
|
|
'Position',[0.70 0.35 0.25 0.06],...
|
|
'String','Re-fit All Curves',...
|
|
'FontSize',14,'FontWeight','bold',...
|
|
'BackgroundColor',[0.2 0.6 1],...
|
|
'ForegroundColor','w',...
|
|
'Callback',@(~,~) refitAll());
|
|
|
|
%% --- Fit info text boxes ---
|
|
infoLabels = {'Fit validity:','Peak positions:','Amplitudes:','Residual (RMS):'};
|
|
hInfoText = gobjects(numel(infoLabels),1);
|
|
for i = 1:numel(infoLabels)
|
|
yText = 0.22 - 0.07*(i-1);
|
|
uicontrol('Style','text','Units','normalized',...
|
|
'Position',[0.68 yText 0.25 0.05],...
|
|
'String',infoLabels{i},...
|
|
'FontSize',10,'FontWeight','bold',...
|
|
'HorizontalAlignment','left',...
|
|
'BackgroundColor','w');
|
|
hInfoText(i) = uicontrol('Style','text','Units','normalized',...
|
|
'Position',[0.88 yText 0.1 0.05],...
|
|
'String','-','FontSize',10,...
|
|
'HorizontalAlignment','left',...
|
|
'BackgroundColor','w');
|
|
end
|
|
|
|
%% --- Compute initial fits ---
|
|
[fitResults, rawData] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
|
spectral_analysis_results.S_theta_norm_all,...
|
|
spectral_analysis_results.theta_vals,...
|
|
'MaxTheta', params.MaxTheta,...
|
|
'ResidualThreshold', params.ResidualThreshold,...
|
|
'PositionThreshold', params.PositionThreshold,...
|
|
'AmplitudeThreshold', params.AmplitudeThreshold);
|
|
|
|
%% --- Initial plot ---
|
|
updatePlot();
|
|
|
|
%% --- Nested functions ---
|
|
|
|
function applyParams()
|
|
% Update parameters from GUI fields
|
|
for j = 1:nParams
|
|
val = str2double(hEdit(j).String);
|
|
if ~isnan(val)
|
|
params.(paramNames{j}) = val;
|
|
end
|
|
end
|
|
refitAll();
|
|
end
|
|
|
|
function refitAll()
|
|
% Recompute all fits
|
|
drawnow;
|
|
[fitResults, rawData] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
|
|
spectral_analysis_results.S_theta_norm_all,...
|
|
spectral_analysis_results.theta_vals,...
|
|
'MaxTheta', params.MaxTheta,...
|
|
'ResidualThreshold', params.ResidualThreshold,...
|
|
'PositionThreshold', params.PositionThreshold,...
|
|
'AmplitudeThreshold', params.AmplitudeThreshold);
|
|
updatePlot();
|
|
end
|
|
|
|
function updatePlot()
|
|
idx = round(get(hSlider,'Value'));
|
|
currentIdx = idx;
|
|
|
|
cla(hAx);
|
|
hold(hAx,'on');
|
|
raw = rawData(idx);
|
|
plot(hAx, raw.theta, raw.x, 'k.-', 'LineWidth', 1, 'DisplayName', 'Raw Data');
|
|
|
|
if idx <= numel(fitResults)
|
|
fit = fitResults(idx);
|
|
if isfield(fit,'isValid') && ~isempty(fit.isValid) && fit.isValid
|
|
plot(hAx, fit.thetaFine, fit.yFit, 'r-', 'LineWidth', 1.5, 'DisplayName', 'Two-Gaussian Fit');
|
|
|
|
% Extract parameters from pFit vector if available
|
|
if isfield(fit,'pFit') && numel(fit.pFit) >= 6
|
|
A1 = fit.pFit(1); mu1 = fit.pFit(2);
|
|
A2 = fit.pFit(4); mu2 = fit.pFit(5);
|
|
hInfoText(1).String = 'Valid';
|
|
hInfoText(1).ForegroundColor = [0 0.5 0];
|
|
hInfoText(2).String = sprintf('[%.3f, %.3f]', mu1, mu2);
|
|
hInfoText(3).String = sprintf('[%.2f, %.2f]', A1, A2);
|
|
else
|
|
hInfoText(1).String = 'Valid';
|
|
hInfoText(1).ForegroundColor = [0 0.5 0];
|
|
hInfoText(2).String = '-';
|
|
hInfoText(3).String = '-';
|
|
end
|
|
|
|
if isfield(fit,'residual') && ~isempty(fit.residual)
|
|
hInfoText(4).String = sprintf('%.4f', fit.residual);
|
|
else
|
|
hInfoText(4).String = '-';
|
|
end
|
|
|
|
else
|
|
hInfoText(1).String = 'Invalid';
|
|
hInfoText(1).ForegroundColor = [0.7 0 0];
|
|
hInfoText(2).String = '-';
|
|
hInfoText(3).String = '-';
|
|
hInfoText(4).String = '-';
|
|
end
|
|
end
|
|
|
|
title(hAx,sprintf('Curve %d / %d', idx, Ncurves),'FontSize',16);
|
|
legend(hAx,'Location','northeast','FontSize',14);
|
|
hold(hAx,'off');
|
|
drawnow;
|
|
end
|
|
|
|
function keyPressCallback(event)
|
|
switch event.Key
|
|
case 'rightarrow'
|
|
if currentIdx < Ncurves
|
|
currentIdx = currentIdx + 1;
|
|
set(hSlider,'Value',currentIdx);
|
|
updatePlot();
|
|
end
|
|
case 'leftarrow'
|
|
if currentIdx > 1
|
|
currentIdx = currentIdx - 1;
|
|
set(hSlider,'Value',currentIdx);
|
|
updatePlot();
|
|
end
|
|
end
|
|
end
|
|
end
|