Ac9cal cb.m
Jump to navigation
Jump to search
% Title: ac9cal_cb.m
% Date: July 98
% Author: Stephen Wolfe
% Input: [action:string]
%modified 10/20/2010 by ES
function [fig]=ac9cal_cb(action,varargin)
narg=length(varargin);
nrow=narg/2;
if nrow==round(nrow),
for iarg=1:2:length(varargin),
eval([varargin{iarg} '= varargin{iarg+1};']);
end
else
display('ac9cal_cb: command line pairs not correctly formed');
return
end
action;
switch action
case 'init'
% Read default par file
% Update/define parameters with vlaues passed to function,
% list_file,autorun,filetype,yr_datum,ylimit,nyear
% Returns the updated par structure
parfile='ac9cal.par';
%S=which('ac9cal_def.par')
S = '/home/data65/pb/REPROCESS_2010/AC9/LIST/ac9cal_def.par';
[par,parnames]=par_input('parfile',parfile,'def_file',S);
if isempty(par),
disp('ac9cal_cb: par structure is empty, check par files');
return
end
par.offset=20;
if exist('list_file'),
par.list_file=list_file;
else
if ~isfield(par,'list_file'),
list_file='';
par.list_file=list_file;
end
end
if exist('autorun'),
par.autorun=autorun;
else
if ~isfield(par,'autorun'),
par.autorun=0;
end
end
if exist('filetype'),
par.filetype=filetype;
else
if ~isfield(par,'filetype'),
%old=0 new=1
par.filetype=1;
end
end
if ~isfield(par,'yr_datum'),
if exist('yr_datum'),
par.yr_datum=yr_datum;
else
par.yr_datum='01/01/1994';
end
end
if ~isfield(par,'ylimit'),
if exist('ylimit'),
par.ylimit=ylimit;
else
par.ylimit=[-.01 .01];
end
end
if ~isfield(par,'nyear'),
if exist('nyear'),
par.nyear=nyear;
end
end
fig=par;
case 'par_update'
%poll all pars and update ac9calFig.userdata
%object tag should have same name as the field in the par structure
%ADD: parse the par structure and then look for tags by those name
%ischar(ac9.par.prop{i})
% yes: update with ac9.prop.str
% no: update with ac9.prop.val
fig=figure(findall(0,'Tag','ac9calFig'));
%ac9 = get(fig)
%ac9.UserData
ac9 = get(fig,'Userdata');
%ac9 = get(fig)
%ac9.UserData
% Update fields for tags that begin with 'par_', looks in all figs
h_par=figbytag('par_');
for ihan=1:length(h_par),
field=deblank(char(get(h_par(ihan),'Tag')));
val=get(h_par(ihan));
if isfield(val,'Value'),
ac9=setfield2(ac9,['par.' field],val.Value);
end
end
% Update all fields for tags in current figure
h=findall(fig);
%deblank(char(get(h(1),'Type')))
% get(h(1))
% pause
%each object is field in par structure and has val and str fields
for ihan=1:length(h),
field=deblank(char(get(h(ihan),'Tag')));
val=get(h(ihan));
if isfield(val,'Value'),
ac9=setfield2(ac9,[field '.val'],val.Value);
end
if isfield(val,'String'),
ac9=setfield2(ac9,[field '.str'],val.String);
% val.String
% if isfield(ac9,'list_file')
% ihan
% disp('list file')
% end
% ihan
% val
end
end
%alternate way to store info in structure
%one structure array with name,val,str for each object
% $$$ for ihan=1:length(h),
% $$$ name=deblank(char(get(h(ihan),'Tag')));
% $$$ val=get(h(ihan));
% $$$ par=setfield(par,['fig(' num2str(ihan) ').name'],name);
% $$$ if isfield(val,'Value'),
% $$$ par=setfield(par,['fig(' num2str(ihan) ').val'],val.Value);
% $$$ end
% $$$ if isfield(val,'String'),
% $$$ par=setfield(par,['fig(' num2str(ihan) ').str'],val.String);
% $$$ end
% $$$ end
%Update fields in par structure with names found in ac9 structure
par_fields=fieldnames(ac9.par);
for ipar=1:length(par_fields),
parfield=par_fields{ipar};
if isfield(ac9,parfield),
p1=getfield(ac9,parfield);
p2=getfield(ac9.par,parfield);
if ischar(p2),
dumvar=p1.str;
else
dumvar=p1.val;
end
ac9=setfield2(ac9,['par.' parfield],dumvar);
end
end
% $$$ %Obsolete
% $$$ iop_han=[findobj('Tag','prop') findobj('Tag','abs')];
% $$$ fluid_han=[findobj('Tag','fluid') findobj('Tag','air')];
% $$$ %par=ac9.par;
% $$$ if ~isfield(ac9.par,'prop'),
% $$$ ac9.par.prop='';
% $$$ end
% $$$ if exist('prop'),
% $$$ ac9.par.prop=prop;
% $$$ elseif ~isempty(ishandle(iop_han)),
% $$$ at=get(iop_han(1),'Value');
% $$$ ab=get(iop_han(2),'Value');
% $$$ if at==1,
% $$$ ac9.par.prop='c';
% $$$ elseif ab==1,
% $$$ ac9.par.prop='a';
% $$$ end
% $$$ end
% $$$ if ~isfield(ac9.par,'fluid'),
% $$$ ac9.par.fluid='';
% $$$ end
% $$$ if exist('fluid'),
% $$$ ac9.par.fluid=fluid;
% $$$ elseif ~isempty(ishandle(fluid_han)),
% $$$ wat_but=get(fluid_han(1),'Value');
% $$$ air_but=get(fluid_han(2),'Value');
% $$$ if wat_but==1,
% $$$ ac9.par.fluid='w';
% $$$ elseif air_but==1,
% $$$ ac9.par.air='a';
% $$$ end
% $$$ end
% put this check into calview, calspec
% $$$ if isempty(par.prop),
% $$$ ButtonName=questdlg('Select an IOP','','c','a','');
% $$$ switch ButtonName,
% $$$ case 'c'
% $$$ iop_han(2)=0;
% $$$ %set(iop_han(1),'Value',1);
% $$$ at=1;
% $$$ par.prop='c';
% $$$ case 'a'
% $$$ iop_han(1)=0;
% $$$ %set(iop_han(2),'Value',1);
% $$$ ab=1;
% $$$ par.prop='a';
% $$$ end
% $$$ set(iop_han(find(iop_han)),'Value',1);
% $$$ end
[sort_channels,I]=sortrows(ac9.par.channels);
prop='a';
if ac9.par.prop,
prop='c';
end
isort_channels=strmatch(prop,sort_channels);
ac9.sort_channels=sort_channels;
ac9.I=I;
ac9.isort_channels=isort_channels;
%ac9.par=par;
% $$$ udata.par.autorun=get(findobj(fig,'Tag','AutoBut'),'Value')
% $$$ udata.par.usefile=get(findobj(fig,'Tag','UseFileBut'),'Value')
% $$$ udata.par.list_file=get(findobj(fig,'Tag','list_file'),'String')
set(fig,'Userdata',ac9);
fig=ac9;
case 'par'
[ac9cal_gui_par]=goac9cal_gui_par;
case 'test'
ac9cal_cb('ac9cal_merge');
case 'ac9cal_merge'
[filename,pathname]=uigetfile('*.txt','Select ''c'' cal file');
qcal=load(fullfile(pathname,filename));
qcal=qcal';
dcal=qcal(:,1:2);
[filename,pathname]=uigetfile('*.txt','Select ''a'' cal file');
qcal=load(fullfile(pathname,filename));
qcal=qcal';
dcal=[dcal qcal(:,2)]
[filename,pathname]=uiputfile('*.txt','Save merged cal file as...');
if ~isempty(filename) & ~filename==0,
save(fullfile(pathname,filename),'dcal','-ascii');
end
case 'gocalview_gui'
set(gcbo,'Enable','off')
filename=get(findobj(gcbf,'Tag','list_file'),'UserData');
if ~isempty(filename)
fig=ac9cal_cb('calview_gui');
set(findobj(fig,'Tag','list_file'),'String',filename);
set(findobj(fig,'Tag','list_file'),'UserData',filename);
% $$$ set(findobj(fig,'Tag','list_file'),'String',[file{1} file{2}])
% $$$ set(findobj(fig,'Tag','list_file'),'UserData',file)
% $$$ set(gcbo,'Enable','on')
else
fig=nofile_gui;
CbHndl=findobj(gcbo);
set(fig,'UserData',CbHndl(1));
end
case 'gocalspec_gui'
set(gcbo,'Enable','off')
filename=get(findobj(gcbf,'Tag','list_file'),'UserData');
if ~isempty(filename)
fig=ac9cal_cb('calspec_gui');
set(findobj(fig,'Tag','list_file'),'String',filename);
set(findobj(fig,'Tag','list_file'),'UserData',filename);
% $$$ set(findobj(fig,'Tag','list_file'),'String',[file{1} file{2}])
% $$$ set(findobj(fig,'Tag','list_file'),'UserData',file)
% $$$ set(gcbo,'Enable','on')
else
fig=nofile_gui;
CbHndl=findobj(gcbo);
set(fig,'UserData',CbHndl(1));
end
case 'view'
set(findobj('Tag','par_AttBut'),'Value',0);
set(findobj('Tag','par_AbsBut'),'Value',0);
set(findobj('Tag','par_h2oBut'),'Value',0);
set(findobj('Tag','par_airBut'),'Value',0)
ac9=ac9cal_cb('par_update');
list_file=ac9.list_file.str;
qcaled('list_file',list_file);
%qcal=qcaled_cb('parse');
case 'CloseAll'
file_gui_cb('CloseAll')
case 'calview'
set(gcbo,'Enable','off')
set(gcbo,'Userdata',[]);
set(findobj('Tag','goBut'),'String','Go');
set(findobj('Tag','goBut'),'Callback','ac9cal_cb(''go'')');
set(findobj('Tag','calspecBut'),'Enable','off')
set(findobj('Tag','ts_calBut'),'Enable','off')
qcaled_cb('add_off');
ac9=ac9cal_cb('par_update');
filename=ac9.list_file.str;
if ~isempty(filename),
%qcaled_cb('list_load');
fig=calview('list_file',filename,'mode','gui');
else
msgbox('Select a qcal file and rerun program');
end
qcaled_cb('add_on');
set(gcbo,'Enable','on')
set(findobj('Tag','calspecBut'),'Enable','on')
set(findobj('Tag','ts_calBut'),'Enable','on')
case 'calspec'
set(gcbo,'Enable','off')
set(gcbo,'Userdata',[]);
set(findobj('Tag','goBut'),'String','Go');
set(findobj('Tag','goBut'),'Callback','ac9cal_cb(''go'')');
set(findobj('Tag','calviewBut'),'Enable','off');
set(findobj('Tag','ts_calBut'),'Enable','off');
ac9=ac9cal_cb('par_update');
filename=ac9.list_file.str;
if ~isempty(filename),
%qcaled_cb('list_load');
fig=calspec('list_file',filename,'mode','gui');
else
msgbox('Select a qcal file and rerun program');
end
qcaled_cb('add_on');
set(gcbo,'Enable','on')
set(findobj('Tag','calviewBut'),'Enable','on');
set(findobj('Tag','ts_calBut'),'Enable','on');
case 'ts_cal'
set(gcbo,'Enable','off')
set(gcbo,'Userdata',[]);
set(findobj('Tag','calspecBut'),'Enable','off');
set(findobj('Tag','calviewBut'),'Enable','off');
filename=get(findobj(findobj('Tag','QcaledFig1'),'Tag','list_file'),... ...
'String');
ac9=ac9cal_cb('par_update');
filename=ac9.list_file.str;
if ~isempty(filename),
%qcaled_cb('list_load');
fig=ts_cal('list_file',filename);
else
msgbox('Select a qcal file and rerun program');
end
qcaled_cb('add_on');
set(gcbo,'Enable','on')
set(findobj('Tag','calviewBut'),'Enable','on');
set(findobj('Tag','calspecBut'),'Enable','on');
set(findobj('Tag','goBut'),'String','Save cal');
set(findobj('Tag','goBut'),'Callback','ac9cal_cb(''save_tsp'')');
case 'save_tsp'
figdata=get(findall(0,'Tag','ts_calBut'),'Userdata');
fig=figdata(1).fig;
udata=get(fig,'Userdata');
ts_cal_out=[-999 -999 -999 udata.tsp.wave;
-999 -999 -999 udata.tsp.mdcal;
-999 -999 -999 udata.tsp.ts_cal_std;
-999 -999 -999 udata.tsp.ndata;
];
[filename,pathname]=uiputfile('*.txt','Save average cal file');
if ~isempty(filename) & ~filename==0,
fid_out=fopen(fullfile(pathname,filename),'w');
foo=sprintf('%8d %6d %9d %9d %9d %9d %9d %9d %9d %9d %9d %9d ',...
ts_cal_out(1,:));
fprintf(fid_out,'%s\n',deblank(foo));
for ifile=2:3,
foo=sprintf(['%8d %6d %9d ', ...
'%9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f'],...
ts_cal_out(ifile,:));
fprintf(fid_out,'%s\n',deblank(foo));
end
foo=sprintf('%8d %6d %9d %9d %9d %9d %9d %9d %9d %9d %9d %9d ',...
ts_cal_out(4,:));
fprintf(fid_out,'%s\n',deblank(foo));
for ifile=1:size(udata.tsp.dcal,1),
foo=sprintf(['%s %s %9.2f ',...
'%9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f'],...
udata.tsp.date(ifile,:),udata.tsp.time(ifile,:), ...
udata.tsp.jday(ifile,:),udata.tsp.dcal(ifile,:));
fprintf(fid_out,'%s\n',deblank(foo));
end
fclose(fid_out);
% $$$ for ifile=1:size(ispec,1),
% $$$ foo=sprintf('%9.5f ',ispec(ifile,:));
% $$$ fprintf(fid_out,'%s\n',deblank(foo));
% $$$ end
% $$$ fclose(fid_out);
end
case 'gosavespec'
ac9cal_cb('showsavebox');
case 'savespec'
disp('both channels saved by calspec for now.');
ac9cal_cb('hidesavebox');
case 'showsavebox'
set(findobj('Tag','SaveBox'),'vis','on');
case 'hidesavebox'
set(findobj('Tag','SaveBox'),'vis','off');
case 'go'
h=get(gcbo,'Userdata');
fig=get(h,'Userdata');
if ishandle(fig),
uiresume(fig);
end
case 'stop'
fig=get(get(findall(0,'Tag','goBut'),'Userdata'),'Userdata');
if ishandle(fig),
set(findall(0,'Tag','goBut'),'Userdata',[]);
uiresume(fig);
end
case 'menus'
case 'reset'
ac9cal_cb('stop');
fig=findall(0,'Tag','ac9calFig');
set(get(fig,'Children'),'Enable','on');
set(findall(fig,'Tag','goBut'),'String','Go');
set(findall(fig,'Tag','goBut'),'Callback','ac9cal_cb(''go'')');
set(findobj('Tag','calviewBut'),'Userdata',[]);
set(findobj('Tag','calspecBut'),'Userdata',[]);
set(findobj('Tag','ts_calBut'),'Userdata',[]);
case 'dev_parse'
%parse device files readme, ask for date
%put date into instrdateBox
%parse device file and store in structure in instrnumList
ow=warning;
warning off;
instr_num='';
instr_date='';
ac9=get(gcbf,'User');
h_instr=findobj('Tag','instrnumList');
val=get(h_instr,'Value');
if val==1,
cal.list.num='';
cal.list.date='';
cal.list.air='';
cal.list.qwater='';
cal.list.soft='';
cal.list.Tref='';
ac9.par.Tref=[];
ac9.par.instr_num='';
ac9.par.instr_date='';
ac9.par.cal=cal;
set(gcbf,'User',ac9);
return
else
str=get(h_instr,'String');
ac9.par.instr_num=char(str(val));
end
h_date=findobj('Tag','instrdateList');
val=get(h_date,'Value');
%remember val is length(cal.list+1)
if val==1,
dev_file=fullfile(ac9.par.calroot_dir, ac9.par.instr_num, ...
ac9.par.dev_dir, ac9.par.dev_file)
name_list={'cal_num',
'cal_date',
'airfile',
'qwaterfile',
'softver',
'Tref'};
list_load(dev_file,[],name_list);
cal.list.num=cal_num;
cal.list.date=cal_date;
cal.list.air=airfile;
cal.list.qwater=qwaterfile;
cal.list.soft=softver;
cal.list.Tref=Tref;
ac9.par.Tref=[];
ac9.par.instr_date='';
ac9.par.cal=cal;
set(gcbf,'User',ac9);
datestr=cellstr(strvcat('Select factory cal', ac9.par.cal.list.date));
set(findall(0,'Tag','instrdateList'),'String',datestr)
else
str=get(h_date,'String');
ac9.par.instr_date=char(str(val));
fi = fopen('caldate_used_a.txt','r+');
if fi == -1
dlmwrite('caldate_used_a.txt',ac9.par.instr_date,'delimiter','')
else
dlmwrite('caldate_used_c.txt',ac9.par.instr_date,'delimiter','')
end
if fi ~= -1
fclose(fi);
end
ac9.par.Tref=str2num(ac9.par.cal.list.Tref(val-1,:));
set(gcbf,'User',ac9);
end
warning(ow);
case 'ac9cal_gui'
load ac9cal_gui
h0 = figure('Units','points', ...
'Color',[0.8 0.8 0.8], ...
'Colormap',mat0, ...
'HandleVisibility','callback', ...
'Name','ac9cal_gui.m', ...
'Menu','none', ...
'Number','off', ...
'Position',[10 10 430 100], ...
'Tag','ac9calFig');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[20 5 75 15], ...
'String','Use list file', ...
'Style','radiobutton', ...
'Value',[1], ...
'Tool',['On = use file, ', ...
'Off = use files selected from list'], ...
'Tag','UseFileBut');
h1 = uicontrol('Parent',h0, ...
'Callback',[...
' h_date=findobj(''Tag'',''instrdateList'');', ...
' set(h_date,''Value'',1), ', ...
' ac9cal_cb(''dev_parse'') '], ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[100 5 75 15], ...
'String',{'Select ac9','101','121','148','184'}, ...
'Style','popupmenu', ...
'Value',[1], ...
'Tool','Select instrument number to calibrate', ...
'Tag','instrnumList');
h1 = uicontrol('Parent',h0, ...
'Callback',' ac9cal_cb(''dev_parse'') ', ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[175 5 100 15], ...
'String',{'Select factory cal'}, ...
'Style','popupmenu', ...
'Value',[1], ...
'Tool','Select calibration date', ...
'Tag','instrdateList');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[20 22 75 15], ...
'String','Auto adv.', ...
'Style','radiobutton', ...
'Value',par.autorun, ...
'Tool',['On = Advance files automatically, ', ...
'Off = interactive'], ...
'Tag','autorun');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.70 0.70 0.70], ...
'Callback','ac9cal_cb(''view'')', ...
'ListboxTop',0, ...
'Position',[360 50 50 35], ...
'String','Get list', ...
'Tool','Open list file', ...
'Tag','configBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.70 0.70 0.70], ...
'Callback','ac9cal_cb(''calview'');', ...
'ListboxTop',0, ...
'Position',[100 50 50 35], ...
'String','calview', ...
'Tool','Screen data and select interval for averaging', ...
'Tag','calviewBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.70 0.70 0.70], ...
'Callback','ac9cal_cb(''calspec'')', ...
'ListboxTop',0, ...
'Position',[150 50 50 35], ...
'String','calspec', ...
'Tool','Calculate average spectra and overlay', ...
'Tag','calspecBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.70 0.70 0.70], ...
'Callback','tsp=ac9cal_cb(''ts_cal'')', ...
'ListboxTop',0, ...
'Position',[200 50 50 35], ...
'String','ts_cal', ...
'Tool','View time series and calc average value',...
'Tag','ts_calBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'Callback','ac9cal_cb(''go'');', ...
'ListboxTop',0, ...
'Position',[20 50 50 35], ...
'String','Go', ...
'Tool','Procede to next file', ...
'Tag','goBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'Callback','ac9cal_cb(''stop'');', ...
'ListboxTop',0, ...
'Position',[300 5 50 15], ...
'String','Stop', ...
'Tool','Bail out of program and return to ac9cal',...
'Tag','stopBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'ListboxTop',0, ...
'Position',[300 50 50 35], ...
'String','Report', ...
'Visible','off', ...
'Tag','repBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.70 0.70 0.70], ...
'Callback','ac9cal_cb(''reset'')', ...
'ListboxTop',0, ...
'Position',[330 22 80 15], ...
'String','Reset Programs', ...
'Tool','Use to get buttons back if programs die', ...
'Tag','ResetBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.70 0.70 0.70], ...
'Callback','file_gui_cb(''CloseAll'')', ...
'ListboxTop',0, ...
'Position',[360 5 50 15], ...
'String','Quit', ...
'Tool','Close all windows', ...
'Tag','CloseAllBut');
h1 = uicontrol('Parent',h0, ...
'Units','points', ...
'BackgroundColor',[0.56 0.68 0.65], ...
'ListboxTop',0, ...
'Position',[100 22 175 15], ...
'Style','text', ...
'Tag','list_file', ...
'Tool','Active list file');
h1 = uicontrol('Parent',h0, ...
'Callback',['val=get(gcbo,''Value''),',...
'set(findobj(''Tag'',''prop''), ', ...
' ''Value'',val), ', ...
'set(findobj(''Tag'',''abs''), ', ...
' ''Value'',abs(val-1)) ' ], ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[250 50 50 15], ...
'String',' c, atten', ...
'Value', par.prop, ...
'Style','radiobutton', ...
'Tag','prop');
h1 = uicontrol('Parent',h0, ...
'Callback',['val=get(gcbo,''Value''),',...
'set(findobj(''Tag'',''abs''), ', ...
' ''Value'',val), ', ...
'set(findobj(''Tag'',''prop''), ', ...
' ''Value'',abs(val-1)) ' ], ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[300 50 50 15], ...
'String',' a, absorp', ...
'Value', ~par.prop, ...
'Style','radiobutton', ...
'Tag','abs');
h1 = uicontrol('Parent',h0, ...
'Callback',['val=get(gcbo,''Value''),',...
'set(findobj(''Tag'',''fluid''), ', ...
' ''Value'',val), ', ...
'set(findobj(''Tag'',''air''), ', ...
' ''Value'',abs(val-1)) ' ], ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[250 70 50 15], ...
'String','Water', ...
'Value', par.fluid, ...
'Style','radiobutton', ...
'Tag','fluid');
h1 = uicontrol('Parent',h0, ...
'Callback',['val=get(gcbo,''Value''),',...
'set(findobj(''Tag'',''air''), ', ...
' ''Value'',val), ', ...
'set(findobj(''Tag'',''fluid''), ', ...
' ''Value'',abs(val-1)) ' ], ...
'Units','points', ...
'BackgroundColor',[.8 .8 .8], ...
'Position',[300 70 50 15], ...
'String','Air', ...
'Value', ~par.fluid, ...
'Style','radiobutton', ...
'Tag','air');
if nargout > 0, fig = h0; end
end
return
% TODO
% Program headers and notes
% Update readme
% Update TRANSFER
% Require saveBox to be low before programs can be called
% Add close windows button