www.gusucode.com > sigdemos 工具箱matlab源码程序 > sigdemos/DFTExample.m

    function DFTExample(action)
%DFTExample Interactive DFT of a signal
%   This example shows how to use some basic signal processing concepts,
%   such as sampling, aliasing, and windowing.
% 
%   You are seeing discrete samples of a periodic waveform (the upper plot) and
%   the absolute value of its discrete Fourier transform (DFT), obtained using
%   a fast Fourier transform (FFT) algorithm (the lower plot).
% 
%   In the lower plot, frequencies from 0 to 100 Hertz are displayed. The DFT
%   at negative frequencies is a mirror image of the DFT at positive frequen-
%   cies.  The sampling rate is 200 Hertz, which means the "Nyquist frequency"
%   is 100 Hertz.  The DFT at frequencies above the Nyquist frequency is the
%   same as the DFT at lower (negative) frequencies.
% 
%   Click and drag a point on the waveform shown in the upper plot to move that
%   point to a new location. This sets a new fundamental frequency and
%   amplitude.
% 
%   The "Signal" pop-up menu lets you change the shape of the waveform.
% 
%   The "Window" menu lets you select a window function. This window is multi-
%   plied by the time waveform prior to taking the DFT.
% 
%   The fundamental frequency of the waveform is given in the text box. You can
%   change the fundamental frequency by clicking in the text box, editing the
%   number there, and pressing RETURN.  You can also change the fundamental by
%   clicking and dragging on the waveform.

%   Author: T. Krauss
%   11/3/92, updated 2/9/93
%   Adapted for Expo: dlc, 7-93
%   Copyright 1988-2012 The MathWorks, Inc.

%   possible actions:
%     'start'
%     'down'
%     'move'
%     'up'
%     'redraw'
%     'done'
%     'setfreq'
%     'setwindow'
%     'showwind'

if nargin<1,
    action='start';
end;

global SIGDEMO1_DAT
global ADDIT_DAT    % this is for WINDOW pop-up button

if strcmp(action,'start'),

    %====================================
    % Graphics initialization
    oldFigNumber = watchon;
    figNumber = figure;
    set(figNumber, ...
        'NumberTitle','off', ...
        'Name','Discrete Fourier Transform', ...
        'Units','normalized');

    %====================================
    % Information for all buttons
    labelColor=192/255*[1 1 1];
    top=0.95;
    bottom=0.05;
    yInitLabelPos=0.90;
    left = 0.78;
    labelWid=0.18;
    labelHt=0.05;
    btnWid = 0.18;
    btnHt=0.07;
    % Spacing between the label and the button for the same command
    btnOffset=0.003;
    % Spacing between the button and the next command's label
    spacing=0.05;
 
    %====================================
    % The CONSOLE frame
    frmBorder=0.02;
    yPos=0.05-frmBorder;
    frmPos=[left-frmBorder yPos btnWid+2*frmBorder 0.9+2*frmBorder];
    h=uicontrol( ...
        'Style','frame', ...
        'Units','normalized', ...
        'Position',frmPos);
    
    %====================================
    % The SIGNAL command popup button

    btnNumber=1;
    yLabelPos=top-(btnNumber-1)*(btnHt+labelHt+spacing);
    labelStr=' Signal';

    % Generic label information
    labelPos=[left yLabelPos-labelHt labelWid labelHt];
    uicontrol( ...
        'Style','text', ...
        'Units','normalized', ...
        'Position',labelPos, ...
        'HorizontalAlignment','left', ...
        'String',labelStr);

    btnPos=[left yLabelPos-labelHt-btnHt-btnOffset btnWid btnHt];
    popup=uicontrol('Style','Popup','String','sine|square|sawtooth',...
        'Units','normalized',...
        'Position', btnPos, ...
        'Callback','DFTExample(''redraw'')');

    %====================================
    % The WINDOW command popup button
    btnNumber=2;
    yLabelPos=top-(btnNumber-1)*(btnHt+labelHt+spacing);
    labelStr=' Window';
    popupStr= ' rectangle| triangular| hanning| hamming| chebyshev| kaiser';
    callbackStr= 'DFTExample(''setwindow'')';

    % Generic label information
    labelPos=[left yLabelPos-labelHt labelWid labelHt];
    uicontrol( ...
        'Style','text', ...
        'Units','normalized', ...
        'Position',labelPos, ...
        'HorizontalAlignment','left', ...
        'String',labelStr);

    % Generic popup button information
    btnPos=[left yLabelPos-labelHt-btnHt-btnOffset btnWid btnHt];
    winHndl = uicontrol( ...
        'Style','popup', ...
        'Units','normalized', ...
        'Position',btnPos, ...
        'String',popupStr, ...
        'Callback',callbackStr);

  %====================================
    % The FUNDAMENTAL editable text box
    btnNumber=3;
    yLabelPos=top-(btnNumber-1)*(btnHt+labelHt+spacing);
    labelPos=[left yLabelPos-labelHt labelWid labelHt];
    freq_text = uicontrol( ...
        'Style','text', ...
        'Units','normalized', ...
        'Position', labelPos, ...
        'String','Fundamental');

    btnPos=[left+0.02  yLabelPos-labelHt-btnHt-btnOffset ...
            0.5*btnWid+frmBorder  btnHt];
    freq_field = uicontrol( ...
        'Style','edit', ...
        'Units','normalized', ...
        'Position', btnPos, ...
        'BackgroundColor','w',...
        'String',' 5',...
        'Callback','DFTExample(''setfreq''); DFTExample(''redraw'')');

   %====================================
    % The INFO button
    uicontrol( ...
        'Style','push', ...
        'Units','normalized', ...
        'Position',[left bottom+(2*labelHt)+spacing btnWid 2*labelHt], ...
        'String','Info', ...
        'Callback','DFTExample(''info'')');

   %========================================
   % The CLOSE button
    done_button=uicontrol('Style','Pushbutton', ...
        'Units','normalized',...
        'Position',[left bottom btnWid 2*labelHt], ...
        'Callback','DFTExample(''done'')','String','Close');
   %====================================

    % Create initial signal
    N=201;     % number of samples
%    N=21;
    amp=0.5;
    freq=5;    % hertz
    t0=0;      % seconds
    t1=1;      % seconds
    t=linspace(t0,t1,N)';
    T=(t1-t0)/N;            % sampling rate in seconds
    M=256;     % length of fft
    window=ones(N,1);    % use window to lower side-lobes in the freq. domain
                             % (makes peaks wider)

    min_dB = -40; % freq. domain lower axis limit

    % create axes for time domain and frequency domain plot
    ax_freq=axes('Position',[.12 .14 .6 .3],'XLim',...
             [0 1/(2*T)],'YLim',[min_dB 50]);
    
    val = get(popup,'Value');
     
    % time domain
    f=local_dataSet(val,amp,t,freq);    

    % frequency domain
    [FF,w]=local_fftpsd(f,window,M,min_dB);    

    freq_line=plot(w/2/pi/T,FF);
    axis([0 1/(2*T)  min_dB 50]);
    grid on;
    ylabel('Magnitude (dB)');
    xlabel('Frequency (Hertz)');

    ax_time=axes('Position',[.12 .58 .6 .3],'XLim',[t0 t1],'YLim',[-1 1]);
    time_line=plot(t,f);
    axis([t0 t1 -1 1]);
    % (set to xor mode to prevent re-rendering, that is, for speed)
    grid on;
    ylabel('Waveform');
    xlabel('Time (Seconds)');
    text(0.12, 1.55,'  Click and drag waveform to change');
    text(0.12, 1.3,'fundamental frequency and amplitude');

    set(time_line,'ButtonDownFcn','DFTExample(''down'')');
    SIGDEMO1_DAT = {freq; amp; N; M; min_dB; 0; 0; ...
         time_line; freq_line; freq_field; popup; -1; gcf; t(:); window(:)};
    ADDIT_DAT = winHndl;

    watchoff(oldFigNumber);

elseif strcmp(action,'down'),
    % assumes that a line was clicked 
    time_line=SIGDEMO1_DAT{8};    
    axes(get(time_line,'Parent'));     % set the right axes

    % Obtain coordinates of mouse click location in axes units
    pt=get(gca,'CurrentPoint');
    x=pt(1,1);
    y=pt(1,2);


% find closest vertex of line to mouse click location (call it fixed_x, fixed_y)

    line_x=get(time_line,'XData');
    line_y=get(time_line,'YData');
    units_str = get(gca,'Units');  % save normalized state
    set(gca,'Units','pixels');     % distance must be in pixels
    p=get(gca,'Position');
    xa=get(gca,'XLim');
    ya=get(gca,'YLim');
    
    dist=((line_x-x)*p(3)/(xa(2)-xa(1))).^2 + ...
         ((line_y-y)*p(4)/(ya(2)-ya(1))).^2;
    [temp,i]=min(dist);
    fixed_x=line_x(i);
    fixed_y=line_y(i);
    set(time_line,'LineStyle','--');

    SIGDEMO1_DAT{6}=fixed_x;
    SIGDEMO1_DAT{7}=fixed_y;

    set(gca,'Units',units_str );
    set(gcf,'WindowButtonMotionFcn', 'DFTExample(''move'')');
    set(gcf,'WindowButtonUpFcn', 'DFTExample(''up'')');
    % set(gcf,'userdata',u);

elseif strcmp(action,'move'),
    % u = get(gcf,'userdata');
    freq=SIGDEMO1_DAT{1};
    amp=SIGDEMO1_DAT{2};
    N=SIGDEMO1_DAT{3};
    M=SIGDEMO1_DAT{4};
    min_dB=SIGDEMO1_DAT{5};
    fixed_x=SIGDEMO1_DAT{6};
    fixed_y=SIGDEMO1_DAT{7};
    time_line=SIGDEMO1_DAT{8};
    freq_line=SIGDEMO1_DAT{9};
    freq_field=SIGDEMO1_DAT{10};
    popup=SIGDEMO1_DAT{11};
    t=SIGDEMO1_DAT{14};
    window=SIGDEMO1_DAT{15};
    
    % Avoid divide by zero warnings
    if fixed_y==0,
        fixed_y=eps;
    end
    
    pt=get(gca,'CurrentPoint');
    x=pt(1,1);
    y=pt(1,2);
    
    % Avoid divide by zero warnings
    if x==0,
        x=eps;
    end
    
    amp1=y/fixed_y*amp;
    if (abs(amp1)>1.0),
       amp1=1.0*sign(amp1);
    end;
    if (abs(amp1)<0.05),
       amp1=0.05*sign(amp1);
    end;
    if (amp1 == 0),
        amp1=0.05;
    end;
    freq1=fixed_x/x*freq;

    val = get(popup,'Value');
    % time domain
    f=local_dataSet(val,amp1,t,freq1);

    set(time_line,'YData',f);
    % frequency domain
    [FF,w]=local_fftpsd(f,window,M,min_dB);

    set(freq_line,'YData',FF);
    set(freq_field,'String',num2str(freq1));

elseif strcmp(action,'up'),
    pt=get(gca,'CurrentPoint');
    x=pt(1,1);
    y=pt(1,2);

    set(gcf,'WindowButtonMotionFcn','');
    set(gcf,'WindowButtonUpFcn','');

    % u=get(gcf,'userdata');
    freq=SIGDEMO1_DAT{1};
    amp=SIGDEMO1_DAT{2};
    fixed_x=SIGDEMO1_DAT{6};
    fixed_y=SIGDEMO1_DAT{7};

    if fixed_y==0,
        fixed_y=eps;
    end

    amp1=y/fixed_y*amp;
    if (abs(amp1)>1.0),
       amp1=1.0*sign(amp1);
    end;
    if (abs(amp1)<0.05),
       amp1=0.05*sign(amp1);
    end;
    freq1=fixed_x/x*freq;

    set(SIGDEMO1_DAT{8},'LineStyle','-');
    SIGDEMO1_DAT{1}=freq1;  % set amplitude and frequency
    SIGDEMO1_DAT{2}=amp1;
    % set(gcf,'userdata',u);
    DFTExample('redraw');

elseif strcmp(action,'done'),
    % close the figure window that is showing the window function:
    % u = get(gcf,'userdata');
    if (SIGDEMO1_DAT{12}~=-1),
        close(SIGDEMO1_DAT{12});
    end;
    close(gcf);
    clear global SIGDEMO1_DAT
    clear global ADDIT_DAT

elseif strcmp(action,'redraw'),
    % recomputes time and frequency waveforms and updates display
    % u = get(gcf,'userdata');
    freq=SIGDEMO1_DAT{1};
    amp=SIGDEMO1_DAT{2};
    N=SIGDEMO1_DAT{3};
    M=SIGDEMO1_DAT{4};
    min_dB=SIGDEMO1_DAT{5};
    time_line=SIGDEMO1_DAT{8};
    freq_line=SIGDEMO1_DAT{9};
    freq_field=SIGDEMO1_DAT{10};
    popup=SIGDEMO1_DAT{11};
    t=SIGDEMO1_DAT{14};
    window=SIGDEMO1_DAT{15};
    val = get(popup,'Value');

    % time domain
    f=local_dataSet(val,amp,t,freq);

    set(time_line,'YData',f);

    % frequency domain
    [FF,w]=local_fftpsd(f,window,M,min_dB);

    set(freq_line,'YData',FF);
    set(freq_field,'String',num2str(freq));

    drawnow;

elseif strcmp(action,'setwindow'),
    % u = get(gcf,'userdata');
    winHndl = ADDIT_DAT;
    in1 = get(winHndl,'Value');
    in2 = 30;
    N=SIGDEMO1_DAT{3};

    if (in1==1),
        window = boxcar(N);
    elseif (in1==2),
        window = triang(N);
    elseif (in1==3),
        window = hanning(N);
    elseif (in1==4),
        window = hamming(N);
    elseif (in1==5),
        window = chebwin(N,30);
    elseif (in1==6),
        window = kaiser(N,4);
    end;

    SIGDEMO1_DAT{15}=window;
    % set(gcf,'userdata',u);
    DFTExample('redraw');
    if (SIGDEMO1_DAT{12}~=-1),
        DFTExample('showwind');
    end;

elseif strcmp(action,'showwind'),
    % u=get(gcf,'userdata');
    oldfig=gcf;
    N=SIGDEMO1_DAT{3};
    t=SIGDEMO1_DAT{14};
    window=SIGDEMO1_DAT{15};
    if (SIGDEMO1_DAT{12}==-1),
        SIGDEMO1_DAT{12}=figure;

        axes('Position',[.15 .62 .8 .3]);
        line1=plot(t,window); 
        title('Window function');
        xlabel('time (seconds)');
        grid on;
        ylabel('Window');

        axes('Position',[.15 .2 .8 .3]);
        W=fft(window,1024);
        line2=plot((0:(1/1024):(.5-(1/1024)))*N,20*log10(abs(W(1:512)))); 
        set(gca,'XLim',[0 N/2]);
        xlabel('Frequency (Hz)');
        ylabel('Magnitude (dB)');
        grid on;
        windclose=uicontrol('Style','Pushbutton','Units','Normalized',...
            'Position',[.85 .02 .12 .08],...
            'Callback',['THEHAND=get(gcf,''UserData'');'...
            'close; figure(THEHAND(1)); global SIGDEMO1_DAT, '...
            'SIGDEMO1_DAT{12}=-1; clear THEHAND SIGDEMO1_DAT'],...
            'String','Close');
        set(gcf,'UserData',[oldfig line1 line2]);
        % set(oldfig,'userdata',u);
        
    else
        figure(SIGDEMO1_DAT{12});
        lines=get(gcf,'UserData');
        set(lines(2),'YData',window);
        W=fft(window,1024);
        set(lines(3),'YData',20*log10(abs(W(1:512))));
        drawnow
    end

elseif strcmp(action,'setfreq'),
    x = str2double(get(SIGDEMO1_DAT{10},'String'));
    if isnan(x),   % handle the non-numeric case
        set(SIGDEMO1_DAT{10},'String',num2str(SIGDEMO1_DAT{1}));
    else
        SIGDEMO1_DAT{1}=x;
    end;

elseif strcmp(action,'info'),
    ttlStr = 'Discrete Fourier Transform'; 

    hlpStr= ...                                              
        ['You are seeing discrete samples of a periodic waveform (the upper plot) and'
         'the absolute value of its discrete Fourier transform (DFT), obtained using '
         'a fast Fourier transform (FFT) algorithm (the lower plot).                 '  
         '                                                                           '  
         'In the lower plot, frequencies from 0 to 100 Hertz are displayed. The DFT  '
         'at negative frequencies is a mirror image of the DFT at positive frequen-  '
         'cies.  The sampling rate is 200 Hertz, which means the "Nyquist frequency" '  
         'is 100 Hertz.  The DFT at frequencies above the Nyquist frequency is the   '
         'same as the DFT at lower (negative) frequencies.                           '    
         '                                                                           '
         'Click and drag a point on the waveform shown in the upper plot to move that'
         'point to a new location. This sets a new fundamental frequency and         '
         'amplitude.                                                                 '  
         '                                                                           '  
         'The "Signal" pop-up menu lets you change the shape of the waveform.        '  
         '                                                                           '  
         'The "Window" menu lets you select a window function. This window is multi- '
         'plied by the time waveform prior to taking the DFT.                        '  
         '                                                                           '  
         'The fundamental frequency of the waveform is given in the text box. You can' 
         'change the fundamental frequency by clicking in the text box, editing the  '
         'number there, and pressing RETURN.  You can also change the fundamental by '
         'clicking and dragging on the waveform.                                     '];

     helpwin(hlpStr, ttlStr);

end

%-------------------------------------------------------------
function [FF,w] = local_fftpsd(f,window,M,min_dB)
% LOCAL_FFTPSD Calculates the Power Spectral Density estimate 
%              via the FFT.
%

    F=fft(window.*f,2*M);
    F=F(1:M);
    w=(0:M-1)*pi/M;
    indxs = find(F==0);
    F(indxs) = eps;        % Avoid warning of divide by zero
    FF=20*log10(abs(F));
    ind=find(FF<min_dB);
    FF(ind)=NaN*ones(size(ind)); % put NaN's in where
         % min_dB shows up - this is to work around no clipping in xor mode
         
%-------------------------------------------------------------              
function f = local_dataSet(val,amp,t,freq)
% LOCAL_DATASET - Calculates the test data set based on the signal
%                 the user selected in the popupmenu.

    if (val == 1),
        f=amp*sin(freq*t*2*pi);
    elseif (val == 2),   % square wave
        tt=freq*t*2*pi;
        tmp=rem(tt,2*pi);
        f=amp*(2*rem((tt<0)+(tmp>pi | tmp<-pi)+1,2)-1);
    elseif (val == 3),   % sawtooth
        tt=freq*t*2*pi;
        f=amp*((tt < 0) + rem(tt,2*pi)/2/pi - .5)*2;
    end;