function varargout = sampling_demo(varargin)
% SAMPLING_DEMO Application M-file for sampling_demo.fig
%    FIG = SAMPLING_DEMO launch sampling_demo GUI.
%    SAMPLING_DEMO('callback_name', ...) invoke the named callback.

% Last Modified by GUIDE v2.0 20-Feb-2002 11:45:11

if nargin == 0  % LAUNCH GUI

	fig = openfig(mfilename,'new');

	% Use system color scheme for figure:
	set(fig,'Color',get(0,'defaultUicontrolBackgroundColor'));

	% Generate a structure of handles to pass to callbacks, and store it. 
	handles = guihandles(fig);

    % [CT, fs] 
    handles.samples = 16;
    handles.frqs = [ 1, handles.samples ];
    handles.mode = 0;
    handles.phase = 0;
    
    set(fig,'HandleVisibility','On');
    
    set(fig,'Name','Sampling Demo');
    
    axes(handles.imag_axes);
    hold on;
    handles.ct_imag = plot(0,1,'b-');
    set(handles.ct_imag,'EraseMode','xor','Visible','off');
    handles.im_recon = plot(0,1,'r-');
    set(handles.im_recon,'EraseMode','xor','Visible','off');
    handles.dt_imag = stem(0,1,'ro');
    set(handles.dt_imag,'EraseMode','xor','Visible','off');
    xlabel('Time (seconds)');
    ylabel('Imaginary Part');
    hold off;

    axes(handles.sin_axes);
    hold on;
    handles.ct_sin = plot(0,1,'b-');
    set(handles.ct_sin,'EraseMode','xor');
    handles.ct_recon = plot(0,1,'r-');
    set(handles.ct_recon,'EraseMode','xor','Visible','off');
    handles.dt_sin = stem(0,1,'ro');
    set(handles.dt_sin,'EraseMode','xor');
    xlabel('Time (seconds)');
    ylabel('Amplitude');
    hold off;

    axes(handles.CT_axes);
    hold on;
    handles.ct_neg = stem([0],[.501],'g-^');
    set(handles.ct_neg,'ButtonDownFcn',sprintf('sampling_demo(''ct_neg'',gcbf,1,guidata(gcbf));'));
    set(handles.ct_neg,'EraseMode','xor');
    handles.ct_pos = stem([0],[.501],'b-^');
    set(handles.ct_pos,'ButtonDownFcn',sprintf('sampling_demo(''ct_pos'',gcbf,1,guidata(gcbf));'));
    set(handles.ct_pos,'EraseMode','xor');
    xlabel('Continuous-Time Frequency (Hz)');
    ylabel('|C_k|');
    hold off;
    
    axes(handles.DT_axes);
    hold on;
    plot([-1.2 1.2],[0 0],'k:',[0 0],[-1.2 1.2],'k:');
    plot(cos(0:.01:(2*pi+.01)),sin(0:.01:(2*pi+.01)),'k:');
    handles.dt_neg(1) = plot([cos(-pi/4)],[sin(-pi/4)],'go');
    handles.dt_neg(2) = plot([0, cos(-pi/4)],[0, sin(-pi/4)],'g-');
    set(handles.dt_neg,'ButtonDownFcn',sprintf('sampling_demo(''dt_neg'',gcbf,1,guidata(gcbf));'));
    set(handles.dt_neg,'EraseMode','xor');
    handles.dt_pos(1) = plot([cos(pi/4)],[sin(pi/4)],'bo');
    handles.dt_pos(2) = plot([0,cos(pi/4)],[0,sin(pi/4)],'b-');
    set(handles.dt_pos,'ButtonDownFcn',sprintf('sampling_demo(''dt_pos'',gcbf,1,guidata(gcbf));'));
    set(handles.dt_pos,'EraseMode','xor');
    hold off;
    title('Discrete-Time Frequency, \omega');
    ylabel('Imag(e^{j\omega})');
    xlabel('Real(e^{j\omega})');
    
    
    guidata(fig, handles);

    update_slow(fig,[],handles,handles.frqs);
    set(fig,'HandleVisibility','Off');
    

	if nargout > 0
		varargout{1} = fig;
	end

elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK

	try
		if (nargout)
			[varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard
		else
			feval(varargin{:}); % FEVAL switchyard
		end
	catch
		disp(lasterr);
	end

end


%| ABOUT CALLBACKS:
%| GUIDE automatically appends subfunction prototypes to this file, and 
%| sets objects' callback properties to call them through the FEVAL 
%| switchyard above. This comment describes that mechanism.
%|
%| Each callback subfunction declaration has the following form:
%| <SUBFUNCTION_NAME>(H, EVENTDATA, HANDLES, VARARGIN)
%|
%| The subfunction name is composed using the object's Tag and the 
%| callback type separated by '_', e.g. 'slider2_Callback',
%| 'figure1_CloseRequestFcn', 'axis1_ButtondownFcn'.
%|
%| H is the callback object's handle (obtained using GCBO).
%|
%| EVENTDATA is empty, but reserved for future use.
%|
%| HANDLES is a structure containing handles of components in GUI using
%| tags as fieldnames, e.g. handles.figure1, handles.slider2. This
%| structure is created at GUI startup using GUIHANDLES and stored in
%| the figure's application data using GUIDATA. A copy of the structure
%| is passed to each callback.  You can store additional information in
%| this structure at GUI startup, and you can change the structure
%| during callbacks.  Call guidata(h, handles) after changing your
%| copy to replace the stored original so that subsequent callbacks see
%| the updates. Type "help guihandles" and "help guidata" for more
%| information.
%|
%| VARARGIN contains any extra arguments you have passed to the
%| callback. Specify the extra arguments by editing the callback
%| property in the inspector. By default, GUIDE sets the property to:
%| <MFILENAME>('<SUBFUNCTION_NAME>', gcbo, [], guidata(gcbo))
%| Add any extra arguments after the last argument, before the final
%| closing parenthesis.



% --------------------------------------------------------------------
function varargout = CTfrq_Callback(h, eventdata, handles, varargin)

frqs = handles.frqs;
ct = eval(get(h,'String'));
if isempty(ct)
    set(h,'String',num2string(frqs(1)));
else
    frqs(1) = ct;
    update_slow(gcbf,eventdata,handles,frqs);
end


% --------------------------------------------------------------------
function varargout = fs_Callback(h, eventdata, handles, varargin)

frqs = handles.frqs;
fs = eval(get(h,'String'));
if isempty(fs)
    set(h,'String',num2string(frqs(fs)));
else
    frqs(2) = fs;
    update_slow(gcbf,eventdata,handles,frqs);
end



% --------------------------------------------------------------------
function varargout = DTfrq_Callback(h, eventdata, handles, varargin)

frqs = handles.frqs;
dt = eval(get(h,'String'));
if isempty(dt)
    set(h,'String',num2string(frqs(1)));
else
    frqs(3) = dt;
    frqs(1) = dt*frqs(2)/2/pi;
    if frqs(3) > pi
        frqs(3) = frqs(3)-2*pi;
    end
    update_slow(gcbf,eventdata,handles,frqs);
end




% --------------------------------------------------------------------
function varargout = update_slow(h, eventdata, handles, frqs)

frqs = [frqs(1) frqs(2) mod(2*pi*frqs(1)/frqs(2),2*pi)];
if frqs(3) > pi
    frqs(3) = frqs(3) - 2*pi;
end
handles.frqs = frqs;
handles.n = 0:(handles.samples);
handles.nt = (handles.n)/frqs(2);
handles.t = linspace(handles.nt(1),handles.nt(end),5000);

guidata(h, handles);
update_fast(h,eventdata,handles,frqs);

axis(handles.sin_axes,'tight')
axis(handles.imag_axes,'tight')

axis(handles.CT_axes,[min(-abs(frqs(1))*2.2,-1.1*frqs(2)), max(1.1*frqs(2),abs(frqs(1))*2.2),0,1.5])

axis(handles.DT_axes,'tight')
axis(handles.DT_axes,'square')

guidata(h, handles);


% --------------------------------------------------------------------
function varargout = update_fast(h, eventdata, handles, frqs)

recon_frq = frqs(3)*frqs(2);
if recon_frq > frqs(2)/2*2*pi
    recon_frq = 2*pi*frqs(2) - recon_frq;
end

set(handles.ct_imag,'XData',handles.t,'YData',sin(handles.t*2*pi*frqs(1) + handles.phase));
set(handles.im_recon,'XData',handles.t,'YData',sin(handles.t*recon_frq + handles.phase));
set(handles.dt_imag(1),'XData',handles.nt,'YData',sin(handles.nt*2*pi*frqs(1) + handles.phase));
set(handles.dt_imag(2),'XData',kron(handles.nt,[1 1 NaN]),'YData',kron(sin(handles.nt*2*pi*frqs(1) + handles.phase),[0 1 NaN]));

set(handles.ct_sin,'XData',handles.t,'YData',cos(handles.t*2*pi*frqs(1) + handles.phase));
set(handles.ct_recon,'XData',handles.t,'YData',cos(handles.t*frqs(3)*frqs(2) + handles.phase));
set(handles.dt_sin(1),'XData',handles.nt,'YData',cos(handles.nt*2*pi*frqs(1) + handles.phase));
set(handles.dt_sin(2),'XData',kron(handles.nt,[1 1 NaN]),'YData',kron(cos(handles.nt*2*pi*frqs(1) + handles.phase),[0 1 NaN]));

set(handles.ct_neg(1),'XData',[-frqs(1)]);
set(handles.ct_neg(2),'XData',[-frqs(1) -frqs(1) NaN]);
set(handles.ct_pos(1),'XData',[frqs(1)]);
set(handles.ct_pos(2),'XData',[frqs(1) frqs(1) NaN]);

set(handles.dt_neg(1),'XData',[cos(-frqs(3))],'YData',[sin(-frqs(3))]);
set(handles.dt_neg(2),'XData',[0 cos(-frqs(3))],'YData',[0 sin(-frqs(3))]);
set(handles.dt_pos(1),'XData',[cos(frqs(3))],'YData',[sin(frqs(3))]);
set(handles.dt_pos(2),'XData',[0 cos(frqs(3))],'YData',[0 sin(frqs(3))]);

set(handles.CTfrq,'string',num2str(frqs(1)));
set(handles.fs,'string',num2str(frqs(2)));
set(handles.DTfrq,'string',num2str(frqs(3)));



% --------------------------------------------------------------------
function ct_pos(h, eventdata, handles, varargin);

handles.mode = 1;
handles.cur_ax = handles.CT_axes;
guidata(h, handles);

% --------------------------------------------------------------------
function ct_neg(h, eventdata, handles, varargin);

handles.mode = 2;
handles.cur_ax = handles.CT_axes;
guidata(h, handles);


% --------------------------------------------------------------------
function dt_pos(h, eventdata, handles, varargin);

handles.mode = 3;
handles.cur_ax = handles.DT_axes;
guidata(h, handles);


% --------------------------------------------------------------------
function dt_neg(h, eventdata, handles, varargin);

handles.mode = 4;
handles.cur_ax = handles.DT_axes;
guidata(h, handles);



% --------------------------------------------------------------------
function varargout = figure1_WindowButtonUpFcn(h, eventdata, handles, varargin)

handles.mode = 0;
guidata(h, handles);
update_slow(h,eventdata,handles,handles.frqs);


% --------------------------------------------------------------------
function varargout = figure1_WindowButtonMotionFcn(h, eventdata, handles, varargin)

if handles.mode ~= 0
    pt = get(handles.cur_ax,'currentpoint');
    pt = pt(1,1:2);
    
    frqs = handles.frqs;
    
    if handles.mode == 1
        frqs(1) = pt(1);
        frqs(3) = mod(2*pi*frqs(1)/frqs(2),2*pi);
        if frqs(3) > pi;
            frqs(3) = frqs(3) - 2*pi;
        end
    elseif handles.mode == 2
        frqs(1) = -pt(1);
        frqs(3) = mod(2*pi*frqs(1)/frqs(2),2*pi);
    elseif handles.mode == 3
        frqs(3) = angle(pt(1) + j*pt(2));
        new_ct = frqs(3)/2/pi*frqs(2);
        ints = (round(frqs(1)/frqs(2)) + [-1 0 1])*frqs(2);
        [mn,ind] = min(abs((new_ct + ints) - frqs(1)));
        frqs(1) = new_ct + ints(ind);
    elseif handles.mode == 4
        frqs(3) = angle(pt(1) - j*pt(2));
        new_ct = frqs(3)/2/pi*frqs(2);
        ints = (round(frqs(1)/frqs(2)) + [-1 0 1])*frqs(2);
        [mn,ind] = min(abs((new_ct + ints) - frqs(1)));
        frqs(1) = new_ct + ints(ind);
    else    
    end

    update_fast(h,eventdata,handles,frqs);
    handles.frqs = frqs;
    guidata(h, handles);
end


% --------------------------------------------------------------------
function varargout = disp_CT_Callback(h, eventdata, handles, varargin)

if get(h,'Value') == 1
    set(handles.ct_sin,'Visible','off');
    set(handles.ct_imag,'Visible','off');
    set(h,'String','Display CT');
else
    set(handles.ct_sin,'Visible','on');
    if get(handles.real_cplx,'Value') == 1
        set(handles.ct_imag,'Visible','on');
    end
    set(h,'String','Hide CT');
end


% --------------------------------------------------------------------
function varargout = real_cplx_Callback(h, eventdata, handles, varargin)

if get(h,'Value') == 0
    set(h,'String','Sinusoid');
    set(handles.sin_axes,'Position',[9.8 27.769 140.2 19.692])
    set(handles.imag_axes,'Visible','off');
    set(handles.ct_imag,'Visible','off');
    set(handles.dt_imag,'Visible','off');
    set(handles.im_recon,'Visible','off');
    set(get(handles.sin_axes,'YLabel'),'String','Amplitude');
    set(handles.dt_neg,'Visible','on');
    set(handles.ct_neg,'Visible','on');
    set(handles.ct_neg(1),'YData',0.501);
    set(handles.ct_neg(2),'YData',[0 0.501 NaN]);
    set(handles.ct_pos(1),'YData',0.501);
    set(handles.ct_pos(2),'YData',[0 0.501 NaN]);
else
    update_fast(h,eventdata,handles,handles.frqs);
    set(h,'String','Complex Exp');
    set(handles.sin_axes,'Position',[9.8 27.769+11.692 140.2 8])
    set(handles.imag_axes,'Visible','on');
    if get(handles.disp_CT,'Value') == 0
        set(handles.ct_imag,'Visible','on');
    end
    if get(handles.recon_toggle,'Value')
        set(handles.im_recon,'Visible','on');
    end    
    set(handles.dt_imag,'Visible','on');
    set(get(handles.sin_axes,'YLabel'),'String','Real Part');
    set(handles.dt_neg,'Visible','off');
    set(handles.ct_neg,'Visible','off');
    set(handles.ct_neg(1),'YData',1.01);
    set(handles.ct_neg(2),'YData',[0 1.01 NaN]);
    set(handles.ct_pos(1),'YData',1.01);
    set(handles.ct_pos(2),'YData',[0 1.01 NaN]);
end    


% --------------------------------------------------------------------
function varargout = real_cplx_ButtonDownFcn(h, eventdata, handles, varargin)


% --------------------------------------------------------------------
function varargout = phase_ctl_Callback(h, eventdata, handles, varargin)

phase = eval(get(handles.phase_ctl,'String'));
if isempty(phase)
    set(h,'String',handles.phase);
else
    handles.phase = mod(phase,2*pi);
    set(h,'String',handles.phase);
    update_slow(gcbf,eventdata,handles,handles.frqs);
end


% --------------------------------------------------------------------
function varargout = recon_toggle_Callback(h, eventdata, handles, varargin)

if get(h,'Value') == 1
    set(h,'String','Hide Recon');
    set(handles.ct_recon,'Visible','on');
    if get(handles.real_cplx,'Value')
        set(handles.im_recon,'Visible','on');
    end
else
    set(h,'String','Show Recon');
    set(handles.ct_recon,'Visible','off');
    set(handles.im_recon,'Visible','off');
end
