Code covered by the BSD License  

Highlights from
plt

image thumbnail
from plt by Paul Mennen
An alternative to plot and plotyy optimized for data exploration

winplt(id,points,pwr,opt)
% winplt.m (ver 07Nov09) ---------------------------------------------------
%
% ----- Author: ----- Paul Mennen
% ----- Email:  ----- paul@mennen.org
%
% Struggling with Matlab's FFT window display tool (wintool), I found
% it cumbersome and limited. I wanted a way to quickly change window
% parameters and see the effect on the time and frequency shapes and
% the most common window measures (scalloping and processing loss,
% frequency resolution, and equivalent noise bandwidth). I couldn't
% modify wintool for my taste since most of the code was hidden (pcode).
% So I wrote winplt.m to create a more useable gui for displaying
% windows. (To start it just type winplt - with no arguments.)
%
% You can also use winplt's command line interface to return the window
% time shapes for use in your Matlab programs. See the description below
% about the command line interface. Winplt is also a great example of gui
% programing using the plt plotting tool, however if you are just getting
% started with gui programming I would recommend starting first with some
% of the shorter examples in the plt\demo folder.
%
% Thirteen of the windows are defined by Matlab functions contained in
% the signal processing toolbox. If that toolbox is not installed
% you will see a warning that the function is not defined. (A boxcar
% window is returned in that case). Seventeen of the windows are defined
% in terms of a convolution kernel. Those windows (and the user defined
% windows) will work correctly without any toolboxes installed.
%
% If you know of some FFT windows I have not included, feel free to let
% me know about them, and I will add them to the next release. Or if you
% want to keep them secret, you will find it easy to add them to winplt
% yourself. Also feel free to suggest new features. (You can always
% reach me at paul@mennen.org).
%
% winplt(-1) ---------------------------------------------------------------
%    Displays a list of all windows and their id codes.
%
% winplt(-2) ---------------------------------------------------------------
%    Returns a cell array containing all names of the defined windows.
%
% [name, kernel] = winplt(id) ----------------------------------------------
%    Returns the name of the window associated with that id number (0 to 31)
%    The second output argument (if given) will contain the convolution
%    kernel. For windows not defined by a kernel the Matlab function
%    used to compute it is returned, along with the window parameters.
%
% winplt(id,points) --------------------------------------------------------
%    The first parameter (id) refers to one of the 31 windows listed below,
%    although the last two are user defined windows and can only be used from
%    the gui interface. A column vector is returned (of length points)
%    containing the amplitude corrected window time shape. Amplitude
%    correction means that the average value is one, i.e.:
%    sum(winplt(id,points)) = points
%
% winplt(id,points,1) ------------------------------------------------------
%    Same as above except that the window is scaled using power correction.
%    This means that the average value of the square of the window is one, i.e.:
%    sum(winplt(id,points,1).^2) = points
%
% winplt(id,points,pwr,opt) ------------------------------------------------
%    id:     An index specifying one of the FFT windows
%    points: The number of points in the time window to be generated
%    pwr:    0=amplitude correction, 1=power correction (default = 0)
%    opt:    optional window parameter
%
% winplt (with no arguments) -----------------------------------------------
%   Opens a GUI to display fft window time and frequency shapes.
%
% Click on the HelpP tag (near the lower left corner of the figure) for
% instructions on zooming and panning the display, enabling traces, and
% using the cursors.
%
% Click on the HelpW tag (right below the HelpP tag) to view a help document
% giving details about the winplt user interface.

function [out1,out2] = winplt(id,points,pwr,opt)
VerString = 'Ver 07Nov09';
out1 = 0;
out2 = 0;
switch nargin
case 0,  % no arguments: Create the winplt window
  ylim = [-145,3];
  TRACEc = [0 1 0; 0 1 0; .2 .6 1; .2 .6 1; 1 .2 .2; 1 .2 .2];
  S.tr = plt(0,zeros(1,6),'FigName','winplt (FFT window shapes)',...
    'Styles','-:-:-:-:','TIDcback',@TIDbk,...
    'AxisPos',[1 1 1 .9 1 .93 1 1],'Ylim',ylim,'YlimR',3.51+ylim/40,...
    'LabelX','Bin number','Options','-Y','Right',[2 4 6],'User','--',...
    'LabelY',{'Frequency shape (dB)' 'Time shape'},'AxisLink',0,...
    'Position',[10 45 750 550],'DisTrace',[0 0 1 1 1 1],'TRACEc',TRACEc);
  ax = getappdata(gcf,'axis');
  set(get(ax(2),'ylabel'),'units','norm','pos',[1.05 .3]); % reposition right hand y label
  text(0,0,VerString,'Units','Norm','Position',[.97 -.08],...
           'color',[.4 .4 .4],'fontsize',8);
  % Since we don't need the LinX help tag (menu box) for this application, next we will rename
  % that tag to 'HelpW' and use it as a winplt help tag. (It opens the winplt.chm help file.)
  % We also rename the standard plt help tag (Help) to HelpP to indicate that it is just for
  % help on plotting and not for specific help about winplt.
  set(findobj(gcf,'string','LinX'),'string','HelpW','ButtonDownFcn',@helpWinP);
  set(findobj(gcf,'string','Help'),'string','HelpP'); % 
  % Next, create the window selection popup (large font), the power correction checkbox,
  % then number of bins slider, the hanning number slider (for the RifeVincent windows)
  % and the parameter slider. Note that the last two sliders have the same screen location,
  % but this is by design since only one of them will be set to visible at a time.
  S.sel = plt('pop','choices',winplt(-2),'index',2,'callbk',@winCB,'location',[.08 .905 .38 1],...
              'fontsize',18,'hide',[findobj(gcf,'type','uicontrol'); findobj(gcf,'type','line')]);
  S.cor = uicontrol('Style','CheckBox','Units','Norm','Position',[.96 .57 .03 .04],...
                    'Callback',@winCB,'BackGround',get(gcf,'Color'),'Value',1);
  S.bin = plt('slider',[.815 530 130],[10 1 60 1 500],'# of bins',  @winCB,2);
  S.han = plt('slider',[.62  530 130],[ 1 0 10 0  10], 'Hanning ^#',@winCB,2);
  S.par = plt('slider',[.62  530 130],[ 0 0  1],       ' ',         @winCB,1,['%4w';'%6w';'%3w']);
  % Next setup the user window string and the label for the power correction checkbox. Note that
  % a separate text object was used for the power correction label because the label provided for
  % a checkbox object can't be rotated.
  S.usr = plt('edit','length',0,'callbk',@winCB,'units','norm','pos',[.13 1.04],'color',[0 1 1],...
              'string','userwin(points,param)','horiz','left');
  S.lbl = text(0,0,'Power corrected','Units','Norm','Position',[1.06 .66],...
              'Color',[.7 .8 .95],'Rotation',90,'user',[1 -.5]);
  % Create the string used to display the convolution kernel for windows that are defined by one.
  % Also used to display error messages as well as a brief help string on startup.
  % A popup is used so we can use this string as a menu for modifying the kernel.
  S.ker = plt('pop','location',[.005 .955 .16 .3],'callbk',@kerCB,'color',[1 1 1],...
    'fontsize',9,'interp','tex','enable',0,'choices',{'Move right','Move left','Longer',...
    'Shorter','Save','Revert','Write winplt.mat','Load winplt.mat','-- cancel --'},...
    'string','Click on the window name for a menu. Right click to scroll thru the choices');
  % S.txt(1) & S.txt(5) - Display scalloping loss
  % S.txt(2) & S.txt(6) - Display the 6db bandwidth
  % S.txt(3) & S.txt(7) - Display the equivalent noise bandwidth
  % S.txt(4) & S.txt(8) - Display the worst case processing loss
  for m = 1:8  S.txt(m) = text(0,0,'');  end;  % define info text for two windows
  xp = .01*ones(4,1);  yp = (.962:-.032:.85)';   % x and y positions for info text
  set(S.txt,'fontsize',8,'units','norm','color',TRACEc(1,:),...
       {'pos'},num2cell([[xp; 75*xp] [yp; yp]],2));
  set(S.txt(5:8),'color',TRACEc(3,:),'vis','off');  % text for previous window
  ax = getappdata(gcf,'axis'); axr = ax(2);         % find right hand axis
  yt = get(axr,'ytick');  set(axr,'ytick',yt(1:5)); % remove some of the y tick labels
  S.cid = getappdata(gcf,'cid'); % get the cursor id for the callbacks
  S.adj = 2000;                  % default adjustment increment (.05%)
  set(gcf,'user',S);             % save the handle list for the callbacks
  winCB;                         % plot the default window (Hanning)

case 1, [out1 out2] = winplt(id,0);  % One argument

otherwise,                           % 2,3,or 4 arguments -----------------------------------
  if ~points  % if # of points is zero, just return the window name in Out1 and the amplitude
              % corrected convolution kernel in Out2
    switch id
      case -1, t = winplt(-2); % display a list of the window names and ID numbers
               for k=1:length(t) fprintf('ID %s\n',t{k}); end;
      case -2, out1 = {}; % create a cell array containing the window names with their ID numbers
               for k=0:99  t = winplt(k,0); if ~ischar(t) break; end;
               out1 = [out1 {sprintf('%02d: %s',k,t)}];
               end;
      case  0, out1='Boxcar';                     out2=1;
      case  1, out1='Hanning/Rife Vincent';       out2=0;
      case  2, out1='Chebyshev';                  out2={'chebwin',90,'Sidelobe level',[50 150]};
      case  3, out1='Kaiser';                     out2={'kaiser',12.26526,'Beta',[1 30]};
      case  4, out1='Gaussian';                   out2={'gausswin',4.3,'Alpha',[.1 8]};
      case  5, out1='Tukey';                      out2={'tukeywin',.5,'Taper size',[.01 1]};
      case  6, out1='Bartlett';                   out2={'bartlett'};
      case  7, out1='Modified Bartlett-Hanning';  out2={'barthannwin'};
      case  8, out1='Bohman';                     out2={'bohmanwin'};
      case  9, out1='Nuttall';                    out2={'nuttallwin'};
      case 10, out1='Parzen';                     out2={'parzenwin'};
      case 11, out1='Taylor';                     out2={'taylorwin',-50,'Sidelobe level ',[-120 -10],5};
      case 12, out1='Hamming';                    out2=[1, -.428752];
      case 13, out1='Exact Blackman';             out2=[1, -.58201156, .090007307];
      case 14, out1='Blackman';                   out2=[1, -.595238095, .095238095];
      case 15, out1='Blackman (Matlab)';          out2={'blackman','periodic'};
      case 16, out1='Blackman Harris (Matlab)';   out2={'blackmanharris'};
      case 17, out1='Blackman Harris 61dB';       out2=[1, -.54898908, .063135301];
      case 18, out1='Blackman Harris 67dB';       out2=[1, -.58780096, .093589774];
      case 19, out1='Blackman Harris 74dB';       out2=[1, -.61793520, .116766542, -.002275157];
      case 20, out1='B-Harris 92dB (min 4term)';  out2=[1, -.68054355, .196905923, -.016278746];
      case 21, out1='Potter 210';                 out2=[1, -.61129, .11129];
      case 22, out1='Potter 310';                 out2=[1, -.684988, .2027007, -.0177127];
      case 23, out1='FlatTop  (5 term)';          out2=[1,-.965, .645, -.194, .014];
      case 24, out1='FlatTop  43dB (Potter 201)'; out2=[.9990280, -.925752, .35196]; 
      case 25, out1='FlatTop  65dB (Potter 301)'; out2=[.9994484, -.955728, .539289, -.091581]; 
      case 26, out1='FlatTop  87dB (Potter 401)'; out2=[1, -.970179, .653919, -.201947, .017552];
      case 27, out1='FlatTop  98dB (Mennen 501)'; out2=[1, -.98069, .79548, -.41115, .104466 -.0080777];
      case 28, out1='FlatTop 101dB (Mennen 601)'; out2=[1 -.98927 .85809 -.56266 .24906 -.060084 4.762e-3];
      case 29, out1='FlatTop  (Matlab)';          out2={'flattopwin','periodic'};
      case 30, out1='adjust kernel';              out2={'adjustK',0,'Parameter',[-100 100],0,0};
      case 31, out1='user';                       out2={'userwin',0,'Parameter',[-100 100],0,0};
      case 32, out1=0; % indicates no more IDs
    end;
    return;
  end;  % end if ~points
  % here if number of points is non-zero: return the time domain window shape in Out1
  if nargin<3 pwr=0; end;   % use amplitude correction if not specified
  if nargin<4 opt=[]; end;  % optional window parameter
  [out1 c] = winplt(id,0);  % get convolution kernel
  if iscell(c) % Here for Matlab computed windows ----------------------------------
    if isempty(opt) & length(c)>1 opt = c{2}; end; % If the optional param is missing, use the default value
    fcn = c{1};
    if exist(fcn)
      switch length(c)
        case 1, out1 = feval(fcn,points);          % get computed time window (without option)
        case 2, out1 = feval(fcn,points,opt);      % get computed time window (with periodic option)
        case 4, out1 = feval(fcn,points,opt);      % get computed time window (with 1 parameters)
        case 5, out1 = feval(fcn,points,c{5},opt); % get computed time window (with 2 parameters)
      end;
      if pwr out1 = out1 * sqrt(points/sum(out1.^2));  % make it power corrected
      else   out1 = out1 * points/sum(out1);           % make it amplitude corrected
      end;
    else out1 = ['Function ' c{1} ' undefined (Signal Processing toolbox required)'];
    end; % end if exist(c{1})
  else % here for kernel defined windows ------------------------------------------
    if ~c(1)                       % RifeVincent windows
      if isempty(opt) opt=1; end;  % if # of HANNs not specified, assume 1
      c = RifeV(opt);
    end;
    out1 = timew(c,points);
    if pwr out1 = out1 / sqrt((sum(2*c.^2)-c(1)^2)); end;
  end; % end if iscell(c)
end;   % end switch nargin
% end function winplt

function TIDbk  % TraceID call back
  S = get(gcf,'user');
  v = get(S.tr(3:4),'vis');         % get visibilities of traces 3 and 4
  [a k] = min(cellfun('length',v)); % find shortest string ('on' is shorter than 'off')
  set(S.txt(5:8),'vis',v{k});       % enable text if either 3rd or 4th trace is visible
%end function TIDback

function winCB(i1,i2)  % callback function (for all objects with callbacks)
  S = get(gcf,'user');
  id = plt('pop',S.sel,'get','index')-1;
  [out1 c] = winplt(id,0);  % get convolution kernel
  par = 'visOFF';
  han = 'visOFF';
  ker = '';
  opt = [];
  b = plt('slider',S.bin,'get'); bins = 2*b;  % get number of bins
  if iscell(c) % Here for Matlab computed windows ----------------------------------
    if length(c)>3                            % Here if there is a window parameter
      par = 'visON';
      if length(findobj(gcf,'string',c{3}))   % if the correct slider is already there
            opt = plt('slider',S.par,'get');  % then just get the current slider value
      else  opt = c{2};                       % otherwise initialize the slider to the default
            plt('slider',S.par,'set','label',c{3});
            plt('slider',S.par,'set','minmax',[c{4} -1e99 1e99]);
            plt('slider',S.par,'set','val',opt);
      end;
    end;
  else         % Here for kernel defined windows ------------------------
    if ~c(1) han = 'visON';
             opt = plt('slider',S.han,'get');  % get number of HANNs
             if (opt>b) opt=b; plt('slider',S.han,'set',opt); end;
             c = RifeV(opt);
    end;
    S.c = c;  S.n = 0;  ker = putKern(S); % save kernel and convert to string
  end;
  set(S.usr,'vis','off');  plt('pop',S.ker,'enable',0);  % not a user window
  plt('slider',S.par,'set',par);
  plt('slider',S.han,'set',han);
  plt('slider',S.han,'set','minmax',[0 b 0 b]);
  plt('cursor',S.cid,'set','xlim',[-b b]);
  sz = 2048;                       % number of points to plot for each window
  pwr = get(S.cor,'value');        % value of checkbox (power or amplitude corrected)
  switch id
    case 30,                       % for adjustable kernel
       S = get(gcf,'user');
       c = S.c;
       n = S.n;                    % coefficient to modify with param slider
       if ~n n=1; S.n=1; end;      % select first coefficient if we just switched to id 30
       delta = '';
       v = plt('slider',S.par,'get');  va = abs(v);
       plt('slider',S.par,'set','val',0);
       if  v > 99 S.adj = v;  delta = ['  \Delta=' num2str(v)];
       elseif va==2 | va==20
                  r = 1 + .5*va/(S.adj*sqrt(abs(c(n)))); % here for slider arrow or trough
                  if v<0 r=1/r; end;
                  c(n) = r * c(n);
       elseif va  if va<1e-39 v = 0; end; % here if not (e-40 is considered small enough to be zero)
                  c(n) = v;
       end;
       S.c = c;
       ker = [putKern(S) delta];
       plt('pop',S.ker,'enable',1);
       ts = timew(c,bins);
       t  = timew(c,sz);
       if pwr t = t * sqrt(sz/sum(t.^2));          % make it power corrected
       else   t = t * sz/sum(t);                   % make it amplitude corrected
       end;
    case 31,  % user window selection
       f = strrep(get(S.usr,'str'),'param',num2str(opt));
       try,   ts = evalUsr(f,bins);
              t  = evalUsr(f,sz);
              if pwr t = t * sqrt(sz/sum(t.^2));  % make it power corrected
              else   t = t * sz/sum(t);           % make it amplitude corrected
              end;
       catch, t = cellstr(lasterr);                   % get error string
              t = char(t(find(cellfun('length',t)))); % remove blank lines
       end;
       set(S.usr,'vis','on');
    otherwise, t  = winplt(id,sz,pwr,opt);   % t = time window
               ts = winplt(id,bins,pwr,opt); % ts = short time window
  end;  % end switch id
  if ischar(t)
     ker = t;  t = ones(sz,1);  ts = zeros(bins,1);  % winplt returned an error message
  end;
  set(S.ker,'string',ker);
  sz2 = sz/2;  binres = bins/sz;
  h = 20*log10(eps+abs(fft([ts; zeros(sz-bins,1)]))); % freq shape
  h = h-h(1);                 % normalize to bin 0
  x = binres*(-sz2:sz2-1)';
  if gcbo==S.cor set(S.tr(2),'x',x,'y',t); return; end; % change in power factor correction
  y = get(S.tr(1:4),'y');     %          previous frequency trace
                              %          previous time trace
                              % previous previous frequency trace
                              % previous previous time trace
  y = [{[h(sz2+1:sz); h(1:sz2)]; t}; y]; % prepend current frequency and time traces (rotate h)
  i = int2str(id); u = [{['Freq' i]; ['Time' i]}; get(S.tr(1:4),'user')];
  set(S.tr(1:6),'x',x,{'y'},y,{'user'},u);
  for r6=1:sz2 if h(r6)<-6 break; end; end; % look for 6dB bandwidth
  h = h(1:1+round(.5/binres));  sloss = max(h)-min(h);
  enbw = sz*sum(t.^2)/(sum(t)^2);  ploss = sloss + 10*log10(enbw);
  if sloss < .1 sloss = sloss*1000; su = ' mdB'; else su = ' dB'; end;
  plt('rename',u); % set the trace IDs to indicate the correct window ID numbers
  set(S.txt,{'string'},...
    [{['scallop loss = '   plt('ftoa','%6w',sloss) su]
      ['6dB bandwidth = '  plt('ftoa','%4w',2*(r6-1)*binres) ' bins']
      ['equiv noise BW = ' plt('ftoa','%4w',enbw) ' bins']
      ['worst case PL = '  plt('ftoa','%4w',ploss) ' dB']}
     get(S.txt(1:4),{'string'})]);  % move current window info text to previous
  set(gcf,'user',S);
%end function winCB

function c = RifeV(n) % compute the kernel for the RifeVincent windows
  warning off;                           % ingore inexact nchoosek
  c = zeros(1,n+1);
  for k=0:n
    c(k+1) = (-1)^k * nchoosek(2*n,n-k); % kernel is row 2n Pascal's triangle
  end;
  c = c/c(1);                            % normalized result
%end function RifeV

function out = timew(c,sz)  % compute the length sz time window from kernel c
  out =  sz * real(ifft([c zeros(1,sz-2*length(c)+1) fliplr(c(2:end))]))';
  % x = (0:sz-1)'/sz;       % alternative version without ifft
  % out = 0*x + c(1);
  % for k = 2:length(c)
  %   out = out + 2*c(k)*cos(2*pi*(k-1)*x);
  % end;
% end function timew

function kerCB
  S = get(gcf,'user');
  l = length(S.c);  c = S.c;
  f = [which('winplt') 'at'];
  switch get(S.ker,'string')
    case 'Move right',       S.n = S.n+1; if S.n>l S.n=1; end;
    case 'Move left',        S.n = S.n-1; if S.n<1 S.n=l; end;
    case 'Longer',           c = [c -c(l)];
    case 'Shorter',          if l>1 c(l) = [];   S.n = min(S.n,l-1); end;
    case 'Save',             set(S.lbl,'user',c);
    case 'Revert',           c = get(S.lbl,'user');
    case 'Write winplt.mat', save(f,'c');
    case 'Load winplt.mat',  load(f);
  end; % end switch
  set(S.ker,'string',putKern(S));
  if ~isequal(c,S.c)  S.c = c;
                      if S.n > length(c)  S.n = 1;  end;
  end;
  set(gcf,'user',S);
  winCB;
%end function kerCB

function t = putKern(S)
  n = S.n;
  c = S.c;
  t = '';
  for k=1:n-1          t = [t num2str(c(k)) '   '];           end;
  if n                 t = [t '{\bf ' num2str(c(n)) ' }  '];  end;
  for k=n+1:length(c)  t = [t num2str(c(k)) '   '];           end;
  t = [t '<-- kernel'];
%end function putKern

function a = evalUsr(f,pts) % evaluate function f to get column vector of length pts
  a = eval(strrep(f,'points',int2str(pts)));
  a = a(:);
  n = length(a);
  if     n<pts a = [a; zeros(pts-n,1)];  % zero pad if too short
  elseif n>pts a = a(1:pts);             % truncate if too long
  end;
%end function evalUsr

function helpWinP(in1,in2)
  f = which('winplt.m');  f(end) = [];       % remove extension
  chm = [f 'chm'];                           % chm document file name
  if ispc & exist(chm) dos(['hh ' f 'chm']); % open .chm or .pdf
  else                 open([f 'pdf']); end;
%end function helpWinP

Contact us at files@mathworks.com