229 lines
8.6 KiB
Matlab
229 lines
8.6 KiB
Matlab
function runInteractiveRSDTwoGaussianFitGUI(spectral_analysis_results)
|
|
%% runInteractiveRSDTwoGaussianFitGUI
|
|
% Author: Karthik
|
|
% Date: 2025-10-16
|
|
% Version: 1.0
|
|
%
|
|
% Description:
|
|
% Interactive viewer for raw radial 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, KRhoRange, AmplitudeRange)
|
|
% - "Re-fit Current Curve" button triggers recomputation
|
|
%
|
|
% Dependencies:
|
|
% Requires Analyzer.fitTwoGaussianCurvesToRadialSpectralDistribution.m
|
|
|
|
%% --- Initialization ---
|
|
rawCurves.x = spectral_analysis_results.S_k_all;
|
|
rawCurves.k = spectral_analysis_results.k_rho_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,:), 'k', rawCurves.k(:)'), 1:Ncurves);
|
|
rawCurves = tmp(:);
|
|
end
|
|
|
|
% Default fit parameters
|
|
params.KRhoRange = [0, 3];
|
|
params.AmplitudeRange = [0, 0.3];
|
|
params.ResidualThreshold = 0.15;
|
|
params.PositionThreshold = 0.5;
|
|
params.AmplitudeThreshold = 0.015;
|
|
|
|
currentIdx = 1;
|
|
|
|
%% --- Create figure ---
|
|
hFig = findobj('Type','figure','Tag','InteractiveTwoGaussianRSD');
|
|
if isempty(hFig)
|
|
hFig = figure('Name','Radial Spectra Fit Viewer',...
|
|
'NumberTitle','off',...
|
|
'Position',[100 100 1250 800],...
|
|
'Color','w',...
|
|
'KeyPressFcn',@keyPressCallback,...
|
|
'Tag','InteractiveTwoGaussianRSD');
|
|
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,'k_\rho','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 = {'KRhoRange','AmplitudeRange','ResidualThreshold','PositionThreshold','AmplitudeThreshold'};
|
|
paramLabels = {'KRho range [min,max]','Amplitude range [min,max]','Residual Threshold','Position Threshold','Amplitude Threshold'};
|
|
paramDescs = {'Truncate Krho range for fitting',...
|
|
'Truncate amplitudes below/above values',...
|
|
'Maximum mean residual for valid fits',...
|
|
'Minimum separation between peaks',...
|
|
'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
|
|
|
|
%% --- Re-fit button ---
|
|
uicontrol('Style','pushbutton','Units','normalized',...
|
|
'Position',[0.700 0.295 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.fitTwoGaussianCurvesToRadialSpectralDistribution(...
|
|
spectral_analysis_results.S_k_all,...
|
|
spectral_analysis_results.k_rho_vals,...
|
|
'KRhoRange', params.KRhoRange,...
|
|
'AmplitudeRange', params.AmplitudeRange,...
|
|
'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 for the entire radial dataset
|
|
drawnow;
|
|
[fitResults, rawData] = Analyzer.fitTwoGaussianCurvesToRadialSpectralDistribution(...
|
|
spectral_analysis_results.S_k_all, ...
|
|
spectral_analysis_results.k_rho_vals, ...
|
|
'KRhoRange', params.KRhoRange, ...
|
|
'AmplitudeRange', params.AmplitudeRange, ...
|
|
'ResidualThreshold', params.ResidualThreshold, ...
|
|
'PositionThreshold', params.PositionThreshold, ...
|
|
'AmplitudeThreshold', params.AmplitudeThreshold);
|
|
updatePlot();
|
|
end
|
|
|
|
function updatePlot()
|
|
% Display raw curve and two-Gaussian fit
|
|
idx = round(get(hSlider,'Value'));
|
|
currentIdx = idx;
|
|
|
|
cla(hAx); hold(hAx,'on');
|
|
|
|
raw = rawData(idx);
|
|
plot(hAx, raw.k, raw.x, 'k.-','LineWidth',1,'DisplayName','Raw Data');
|
|
|
|
fit = fitResults(idx);
|
|
if isfield(fit,'isValid') && ~isempty(fit.isValid) && fit.isValid && ...
|
|
isfield(fit,'kFine') && isfield(fit,'yFit') && ~isempty(fit.kFine)
|
|
plot(hAx, fit.kFine, fit.yFit, 'r-','LineWidth',1.5,'DisplayName','Two-Gaussian Fit');
|
|
|
|
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('[%.3f, %.3f]',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
|
|
|
|
set(gca, 'YScale', 'log');
|
|
|
|
xlabel(hAx,'k_\rho'); ylabel(hAx,'Normalized amplitude');
|
|
title(hAx,sprintf('Curve %d / %d',idx,Ncurves),'FontSize',16);
|
|
legend(hAx,'Location','northeast','FontSize',14);
|
|
hold(hAx,'off'); drawnow;
|
|
end
|
|
|
|
function keyPressCallback(event)
|
|
% Navigate curves via arrow keys
|
|
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
|