function varargout = MixSIR(varargin)
% MIXSIR M-file for MixSIR.fig
% MIXSIR, by itself, creates a new MIXSIR or raises the existing
% singleton*.
%
% H = MIXSIR returns the handle to a new MIXSIR or the handle to
% the existing singleton*.
%
% MIXSIR('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in MIXSIR.M with the given input arguments.
%
% MIXSIR('Property','Value',...) creates a new MIXSIR or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before MixSIR_OpeningFunction gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to MixSIR_OpeningFcn via varargin.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help MixSIR
% Last Modified by GUIDE v2.5 30-Jul-2007 12:25:36
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @MixSIR_OpeningFcn, ...
'gui_OutputFcn', @MixSIR_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before MixSIR is made visible.
function MixSIR_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to MixSIR (see VARARGIN)
% Choose default command line output for MixSIR
handles.output = hObject;
priors = 0; % initialize priors (this specifies the uniformative case as default)
save 'priors.mat' priors %saves prior placeholder to working directory
handles.printresult=0; %sets default to not write results files
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes MixSIR wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = MixSIR_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in "About MixSIR...".
function about_Callback(hObject, eventdata, handles)
% hObject handle to about (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
open about.txt;
function iterations_Callback(hObject, eventdata, handles)
% hObject handle to iterations (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'String') returns contents of iterations as text
handles.iters =str2double(get(hObject,'String'));% returns contents of iterations as a double
guidata(hObject,handles);
% --- Executes during object creation, after setting all properties.
function iterations_CreateFcn(hObject, eventdata, handles)
% hObject handle to iterations (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: edit controls usually have a white background on Windows.
% See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end
% --- Executes on button press in runmodelbutton.
function runmodelbutton_Callback(hObject, eventdata, handles)
% hObject handle to runmodelbutton (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
%get the user defined number of model iterations to be used
iterations_Callback(hObject, eventdata, handles);
try
if isnan(handles.iters)
msgbox('You have not entered a valid number into the iterations field.','Oops...','error')
return;
elseif handles.iters<100
msgbox('Less than 100 iterations not allowed.','Oops...','error')
return;
end
catch
msgbox('You have not entered a valid number into the iterations field.','Oops...','error')
return;
end
%READ THESE IN...
%_________________________________________________________________________________________
load mean_source.txt; %creates matrix called mean_source
load SD_source.txt; %creates matrix called SD_source
load mix_data.txt; handles.mean_mix = mean(mix_data,1);%creates matrix called mean_mix
load mean_frac.txt; %creates matrix called mean_frac
load SD_frac.txt; %creates matrix called SD_frac
%convert from SD to variance
var_source = SD_source.^2;
var_frac = SD_frac.^2;
%_________________________________________________________________________________________
% Check that data are in the right format (at least in terms of matrix sizes)
if (size(mean_source,1)-size(SD_source,1) + size(mean_source,2)-size(SD_source,2)) +...
(size(mean_frac,1)-size(SD_frac,1) + size(mean_frac,2)-size(SD_frac,2))+...
(size(mix_data,2)-size(SD_frac,2))+...
(size(mean_source,2)-size(mean_frac,2) + (size(mean_source,1)-size(mean_frac,1))) ~= 0
msgbox('Dimensions of source data matrices do not agree!','Oops...','error');
return;
end
%MAKE THESE FOR WORK....
%_________________________________________________________________________________________
handles.source_num = size(mean_source,1); % how many sources contribute to the mixator spp of concern?
handles.iso_num = size(mean_source,2); % how many isotopes are you using?
%set the priors based on user specs
load priors.mat; %load prior placeholder file
if priors == 0;
handles.A = ones(1,handles.source_num);%sets uninformative priors as default
handles.B = ones(1,handles.source_num);%sets uninformative priors as default
clear priors;
else
handles.A = priors(1,:);
handles.B = priors(2,:);
clear priors;
end
source = zeros(handles.source_num, handles.iso_num); % this used to store sampled iso vals for source
mix = zeros(1,handles.iso_num); % this used to store sampled iso vals for mix
contribution = zeros(1,handles.source_num)'; % this used to store sampled contribs
handles.contrib_out = zeros(1,handles.source_num); % this used to store accepted contrib vectors (OUTPUT!)
handles.mix_out = zeros(1,handles.iso_num); % this used to store accepted contrib vectors (OUTPUT!)
out_ticker = 0; % keeps track of the # of SIR accepts
fraction = zeros(handles.source_num,handles.iso_num); % this used to store sampled fractionation for each iso
Total = 0; % this used to keep tally of likes for SIR accept
MaxDup = 0; % keeps track of duplicate accepts. Used to evaluate threshold.
likestor = 0; % this used to store best threshold guess
ljump = 0; % keeps track of the # likes accepted
handles.L =zeros(1,1); % this used to store all the accepted likes
progressbar; %Initializes a GUI progress bar
%_________________________________________________________________________________________
%IDENTIFY AN APPROPRIATE THRESHOLD.....
%_________________________________________________________________________________________
for irep=1:(round(handles.iters*0.1))%uses 10% run size to get threshold
progressbar(irep/(round(handles.iters*0.1)+handles.iters)); %begins updating progress bar
% Generate random proportions that sum to one (one for each source)
contribution= rand_props2(handles.source_num); %calls function randprops2 to make random proportions (1 column)
%Below uses an analytic solution to the mean and var of pred (without frationation) based on combining source
%means and vars. Note that I have implemented a vectorized version of this calc below, so beware of tricky matrix math!
mproposal = (mean_source'*contribution)';%generates a row of proposed mean mixs (one for each iso)
vproposal = ((var_source)'*(contribution.^2))'; %does same for var
% Now use the same method as above to get proposed fractionation
mfraction = (mean_frac'*contribution)';%generates a row of proposed fractionations (one for each iso)
vfraction = ((var_frac)'*(contribution.^2))'; %does same for var
%when adding distributions, both the mean and variance are additive. So
%we simply add the fractionation mean and var to the mean and var of the
%mix calculated above
mproposal=mproposal+mfraction; vproposal=vproposal+vfraction;
% EVALUATE THE NEGLOGLIKE FOR THE PROPOSED MIX BASED ON DATA
neglog = 0; % clear to zero for each run
for iso_i = 1:handles.iso_num
neglog = neglog + normlike([mproposal(iso_i),vproposal(iso_i)],mix_data(:,iso_i)); %neglogs for consumer iso vals
end
% EVALUATE THE NEGLOGLIKE FOR source CONTRIBS BASED ON PRIORS
neglog = neglog - sum(log(betapdf(contribution',handles.A,handles.B)+1e-100));
likelihood = exp(-(neglog)); %presto! The likelihood appears like magic
if likelihood > likestor
likestor = likelihood; %keeps the very best like found
end
end
Threshold = likestor*2; % Define SIR accept. criteria
%_________________________________________________________________________________________
%RUN THE SIR ALGORITHM....
%_________________________________________________________________________________________
% DO for each replicate
for irep = 1:handles.iters
progressbar((round(handles.iters*0.1)+irep)/(handles.iters+(round(handles.iters*0.1)))); %continues updating progress bar from where left off last
% Generate random proportions that sum to one (one for each source)
contribution= rand_props2(handles.source_num); %calls function randprops2 to make random proportions (1 column)
%Below uses an analytic solution to the mean and var of pred (without frationation) based on combining source
%means and vars. See "Behboodian 1970. On a mixture of normal distributions. Biometrika 57(1):215-217"
%for details on the calculation. Note that I have implemented a
%vectorized version of this calc below, so beware of tricky matrix math!
mproposal = (mean_source'*contribution)';%generates a row of proposed mean mixs (one for each iso)
vproposal = ((var_source)'*(contribution.^2))'; %does same for var
% Now use the same method as above to get proposed fractionation
mfraction = (mean_frac'*contribution)';%generates a row of proposed fractionations (one for each iso)
vfraction = ((var_frac)'*(contribution.^2))'; %does same for var
%when adding distributions, both the mean and variance are additive. So
%we simply add the fractionation mean and var to the mean and var of the
%mix calculated above
mproposal=mproposal+mfraction; vproposal=vproposal+vfraction;
% EVALUATE THE NEGLOGLIKE FOR THE PROPOSED MIX BASED ON DATA
neglog = 0; % clear to zero for each run
for iso_i = 1:handles.iso_num
neglog = neglog + normlike([mproposal(iso_i),vproposal(iso_i)],mix_data(:,iso_i)); %neglogs for consumer iso vals
end
%INCORPORATE PRIORS FOR source CONTRIBS
neglog = neglog - sum(log(betapdf(contribution',handles.A,handles.B)+1e-100));
likelihood = exp(-(neglog)); %presto! The likelihood appears like magic
% HERE COMES THE SIR...
% Check whether write out is needed - also keep track of duplicates
Total = Total + likelihood;
Dups = 0;
while Total > Threshold
Dups = Dups + 1;
ljump = ljump + 1;
handles.L(ljump,1) = likelihood; %records all accepted likelihoods
if Dups > MaxDup
MaxDup = Dups; %shows the draw that got duped most before dropping Total below treshold
end
%WRITE OUT ACCEPTED DRAWS
out_ticker = out_ticker+1;
handles.contrib_out(out_ticker,:) = contribution';
handles.mix_out(out_ticker,:) = mix;
%Reduce the total and try again
Total = Total - Threshold;
end
end
%DEVELOP RESULTS OF MODEL RUN BELOW.....
% --- Executes during object creation, after setting all properties.
axes(handles.boxplotter) %identifies the correct parent axes to put the plot
boxplot(handles.contrib_out);
xlabel('Sources (Same Order As The Data Files)');
ylabel('Proportional Contribution Of Each Source To The Mix');
title('Posterior Source Contributions To The Mix')
if handles.printresult==1
% write out results
dlmwrite('contrib_out.txt', handles.contrib_out, '\t')
dlmwrite('likelihoods.txt', handles.L, '\t')
end
handles.runique = length(unique(handles.contrib_out,'rows'));% # of unique parameter vectors in resample
handles.Lmax = max(handles.L)/sum(handles.L); % best likelihood (% of total)
printout(out_ticker,MaxDup,handles.contrib_out,handles.source_num, handles.runique,handles.Lmax);
guidata(hObject, handles);
% --- Executes on button press in box_plot_butt.
function box_plot_butt_Callback(hObject, eventdata, handles)
% hObject handle to box_plot_butt (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
if handles.boxprint == 1
figure;
boxplot(handles.contrib_out);
xlabel('Sources (Same Order As The Data Files)');
ylabel('Proportional Contribution Of Each Source To The Mix');
title('Posterior Source Contributions To The Mix')
else
axes(handles.boxplotter) %identifies the correct parent axes to put the plot
boxplot(handles.contrib_out);
xlabel('Sources (Same Order As The Data Files)');
ylabel('Proportional Contribution Of Each Source To The Mix');
title('Posterior Source Contributions To The Mix')
end
% --- Executes on button press in likelihood_distrib.
function likelihood_distrib_Callback(hObject, eventdata, handles)
% hObject handle to likelihood_distrib (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% all accepted likelihoods relative to max accepted like (MLE)
if handles.likeprint == 1
figure;
hist(handles.L/max(handles.L),20);
h = findobj(gca,'Type','patch');
set(h,'FaceColor','y','EdgeColor','k')
xlabel('Relative Likelihood')
ylabel('Count')
Title('Likelihoods for SIR Resample')
else
axes(handles.boxplotter) %identifies the correct parent axes to put the plot
hist(handles.L/max(handles.L),20);
h = findobj(gca,'Type','patch');
set(h,'FaceColor','y','EdgeColor','k')
xlabel('Relative Likelihood')
ylabel('Count')
Title('Likelihoods for SIR Resample')
end
% --- Executes on button press in results_check.
function results_check_Callback(hObject, eventdata, handles)
% hObject handle to results_check (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.printresult =get(hObject,'Value');% returns toggle state of results_check
guidata(hObject,handles);
% --- Executes on button press in boxprint.
function boxprint_Callback(hObject, eventdata, handles)
% hObject handle to boxprint (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.boxprint =get(hObject,'Value');% returns toggle state of results_check
guidata(hObject,handles);
% --- Executes on button press in likeprint.
function likeprint_Callback(hObject, eventdata, handles)
% hObject handle to likeprint (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.likeprint =get(hObject,'Value');% returns toggle state of results_check
guidata(hObject,handles);
% --------------------------------------------------------------------
function priorpanel_SelectionChangeFcn(hObject, eventdata, handles)
% hObject handle to priorpanel (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
switch get(hObject,'Tag') % Get Tag of selected object
case 'uninformative'
% code piece when radiobutton1 is selected goes here
priors = 0; % 0 values set prior distrib to Beta(1,1) upon program execution
save 'priors.mat' priors %saves prior placeholder to working directory
case 'define'
% code piece when radiobutton2 is selected goes here
if exist('define.txt')==2
load define.txt; %brings in defined priors A and B (columns) for each source (rows)
priors(1,:) = define(1,:);
priors(2,:) = define(2,:);
save 'priors.mat' priors%saves prior placeholder to working directory
else
msgbox('ERROR: The file define.txt does not exist in the program directory','File Missing...','error')
end
end
%--- STATUS BAR BELOW
function progressbar(fractiondone, position)
% Description:
% progressbar(fractiondone,position) provides an indication of the progress of
% some task using graphics and text. Calling progressbar repeatedly will update
% the figure and automatically estimate the amount of time remaining.
% This implementation of progressbar is intended to be extremely simple to use
% while providing a high quality user experience.
%
% Features:
% - Can add progressbar to existing m-files with a single line of code.
% - The figure closes automatically when the task is complete.
% - Only one progressbar can exist so old figures don't clutter the desktop.
% - Remaining time estimate is accurate even if the figure gets closed.
% - Minimal execution time. Won't slow down code.
% - Random color and position options. When a programmer gets bored....
%
% Usage:
% fractiondone specifies what fraction (0.0 - 1.0) of the task is complete.
% Typically, the figure will be updated according to that value. However, if
% fractiondone == 0.0, a new figure is created (an existing figure would be
% closed first). If fractiondone == 1.0, the progressbar figure will close.
% position determines where the progressbar figure appears on screen. This
% argument only has an effect when a progress bar is first created or is reset
% by calling with fractiondone = 0. The progress bar's position can be specifed
% as follows:
% [x, y] - Position of lower left corner in normalized units (0.0 - 1.0)
% 0 - Centered (Default)
% 1 - Upper right
% 2 - Upper left
% 3 - Lower left
% 4 - Lower right
% 5 - Random [x, y] position
% The color of the progressbar is choosen randomly when it is created or
% reset. Clicking inside the figure will cause a random color change.
% For best results, call progressbar(0) (or just progressbar) before starting
% a task. This sets the proper starting time to calculate time remaining.
%
% Example Function Calls:
% progressbar(fractiondone,position)
% progressbar % Initialize/reset
% progressbar(0) % Initialize/reset
% progressbar(0,4) % Initialize/reset and specify position
% progressbar(0,[0.2 0.7]) % Initialize/reset and specify position
% progressbar(0.5) % Update
% progressbar(1) % Close
%
% Demo:
% n = 1000;
% progressbar % Create figure and set starting time
% for i = 1:n
% pause(0.01) % Do something important
% progressbar(i/n) % Update figure
% end
%
% Author: Steve Hoelzer
%
% Revisions:
% 2002-Feb-27 Created function
% 2002-Mar-19 Updated title text order
% 2002-Apr-11 Use floor instead of round for percentdone
% 2002-Jun-06 Updated for speed using patch (Thanks to waitbar.m)
% 2002-Jun-19 Choose random patch color when a new figure is created
% 2002-Jun-24 Click on bar or axes to choose new random color
% 2002-Jun-27 Calc time left, reset progress bar when fractiondone == 0
% 2002-Jun-28 Remove extraText var, add position var
% 2002-Jul-18 fractiondone input is optional
% 2002-Jul-19 Allow position to specify screen coordinates
% 2002-Jul-22 Clear vars used in color change callback routine
% 2002-Jul-29 Position input is always specified in pixels
% 2002-Sep-09 Change order of title bar text
% 2003-Jun-13 Change 'min' to 'm' because of built in function 'min'
% 2003-Sep-08 Use callback for changing color instead of string
% 2003-Sep-10 Use persistent vars for speed, modify titlebarstr
% 2003-Sep-25 Correct titlebarstr for 0% case
% 2003-Nov-25 Clear all persistent vars when percentdone = 100
% 2004-Jan-22 Cleaner reset process, don't create figure if percentdone = 100
% 2004-Jan-27 Handle incorrect position input
% 2004-Feb-16 Minimum time interval between updates
% 2004-Apr-01 Cleaner process of enforcing minimum time interval
% 2004-Oct-08 Seperate function for timeleftstr, expand to include days
% 2004-Oct-20 Efficient if-else structure for sec2timestr
%
persistent progfig progpatch starttime lastupdate
% Set defaults for variables not passed in
if nargin < 1
fractiondone = 0;
end
if nargin < 2
position = 0;
end
try
% Access progfig to see if it exists ('try' will fail if it doesn't)
dummy = get(progfig,'UserData');
% If progress bar needs to be reset, close figure and set handle to empty
if fractiondone == 0
delete(progfig) % Close progress bar
progfig = []; % Set to empty so a new progress bar is created
end
catch
progfig = []; % Set to empty so a new progress bar is created
end
% If task completed, close figure and clear vars, then exit
percentdone = floor(100*fractiondone);
if percentdone == 100 % Task completed
delete(progfig) % Close progress bar
clear progfig progpatch starttime lastupdate % Clear persistent vars
return
end
% Create new progress bar if needed
if isempty(progfig)
% Calculate position of progress bar in normalized units
scrsz = [0 0 1 1];
width = scrsz(3)/4;
height = scrsz(4)/50;
if (length(position) == 1)
hpad = scrsz(3)/64; % Padding from left or right edge of screen
vpad = scrsz(4)/24; % Padding from top or bottom edge of screen
left = scrsz(3)/2 - width/2; % Default
bottom = scrsz(4)/2 - height/2; % Default
switch position
case 0 % Center
% Do nothing (default)
case 1 % Top-right
left = scrsz(3) - width - hpad;
bottom = scrsz(4) - height - vpad;
case 2 % Top-left
left = hpad;
bottom = scrsz(4) - height - vpad;
case 3 % Bottom-left
left = hpad;
bottom = vpad;
case 4 % Bottom-right
left = scrsz(3) - width - hpad;
bottom = vpad;
case 5 % Random
left = rand * (scrsz(3)-width);
bottom = rand * (scrsz(4)-height);
otherwise
warning('position must be (0-5). Reset to 0.')
end
position = [left bottom];
elseif length(position) == 2
% Error checking on position
if (position(1) < 0) | (scrsz(3)-width < position(1))
position(1) = max(min(position(1),scrsz(3)-width),0);
warning('Horizontal position adjusted to fit on screen.')
end
if (position(2) < 0) | (scrsz(4)-height < position(2))
position(2) = max(min(position(2),scrsz(4)-height),0);
warning('Vertical position adjusted to fit on screen.')
end
else
error('position is not formatted correctly')
end
% Initialize progress bar
progfig = figure(...
'Units', 'normalized',...
'Position', [position width height],...
'NumberTitle', 'off',...
'Resize', 'off',...
'MenuBar', 'none',...
'BackingStore', 'off' );
progaxes = axes(...
'Position', [0.02 0.15 0.96 0.70],...
'XLim', [0 1],...
'YLim', [0 1],...
'Box', 'on',...
'ytick', [],...
'xtick', [] );
progpatch = patch(...
'XData', [0 0 0 0],...
'YData', [0 0 1 1],...
'EraseMode', 'none' );
set(progfig, 'ButtonDownFcn',{@changecolor,progpatch});
set(progaxes, 'ButtonDownFcn',{@changecolor,progpatch});
set(progpatch,'ButtonDownFcn',{@changecolor,progpatch});
changecolor(0,0,progpatch)
% Set time of last update to ensure a redraw
lastupdate = clock - 1;
% Task starting time reference
if isempty(starttime) | (fractiondone == 0)
starttime = clock;
end
end
% Enforce a minimum time interval between updates
if etime(clock,lastupdate) < 0.01
return
end
% Update progress patch
set(progpatch,'XData',[0 fractiondone fractiondone 0])
% Update progress figure title bar
if (fractiondone == 0)
titlebarstr = ' 0%';
else
runtime = etime(clock,starttime);
timeleft = runtime/fractiondone - runtime;
timeleftstr = sec2timestr(timeleft);
titlebarstr = sprintf('%2d%% %s remaining',percentdone,timeleftstr);
end
set(progfig,'Name',titlebarstr)
% Force redraw to show changes
drawnow
% Record time of this update
lastupdate = clock;
% ------------------------------------------------------------------------------
function changecolor(h,e,progpatch)
% Change the color of the progress bar patch
colorlim = 2.8; % Must be <= 3.0 - This keeps the color from being too light
thiscolor = rand(1,3);
while sum(thiscolor) > colorlim
thiscolor = rand(1,3);
end
set(progpatch,'FaceColor',thiscolor);
% ------------------------------------------------------------------------------
function timestr = sec2timestr(sec)
% Convert a time measurement from seconds into a human readable string.
% Convert seconds to other units
d = floor(sec/86400); % Days
sec = sec - d*86400;
h = floor(sec/3600); % Hours
sec = sec - h*3600;
m = floor(sec/60); % Minutes
sec = sec - m*60;
s = floor(sec); % Seconds
% Create time string
if d > 0
if d > 9
timestr = sprintf('%d day',d);
else
timestr = sprintf('%d day, %d hr',d,h);
end
elseif h > 0
if h > 9
timestr = sprintf('%d hr',h);
else
timestr = sprintf('%d hr, %d min',h,m);
end
elseif m > 0
if m > 9
timestr = sprintf('%d min',m);
else
timestr = sprintf('%d min, %d sec',m,s);
end
else
timestr = sprintf('%d sec',s);
end
% --- Executes on button press in about.
function about_Callback(hObject, eventdata, handles)
% hObject handle to about (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)