function varargout = fdbutter(varargin)
%fdbutter  Butterworth Module for filtdes.

%   Author: T. Krauss
%   Copyright 1988-2000 The MathWorks, Inc.
%   $Revision: 1.6 $

% Change this global to static when available:
%  following are common to several modules
global minOrdCheckbox bandpop order pbspecs sbspecs pbmeasures sbmeasures
global passframe stopframe passframe1 stopframe1
%  following static to fdbutter
global f1 f2 f3 f4 w3db1 w3db2 Rp Rs 
global Rpm w3db1m w3db2m 
global order0 order1
global ax L1 L2 Lresp L3db1 L3db2
global Fs

switch varargin{1}
case 'init'

    filt = varargin{2};
    Fs = filt.Fs;
    [setOrderFlag_init, type_init, f_init, Rp_init, Rs_init, ...
      order_init, w3db_init] = initSpecs(filt);
      
    [minOrdCheckbox bandpop order pbspecs sbspecs ...
         pbmeasures sbmeasures passframe stopframe ...
         passframe1 stopframe1 ax Lresp L1 L2 order1 ...
         L3db1 L3db2] = fdutil('commonObjects');
    order0 = order;
             
    if ~strcmp(bandpop.userdata,'fdbutter')
       % this module is not current
        bandpop.userdata = 'fdbutter';
            
        minOrdCheckbox.callback = 'fdbutter(''checkbox'')';
        bandpop.callback = 'fdbutter(''newtype'')';         

        order0.callback = 'fdbutter(''dirty'')';
                
        f3 = pbspecs(2);
        f4 = sbspecs(2);
        Rp = pbspecs(3);
        Rs = sbspecs(3);
        w3db1 = pbspecs(1);
        w3db2 = sbspecs(2);
        w3dbm1 = sbmeasures(1);
        w3dbm2 = sbmeasures(2);
        
        Rpm = pbmeasures(3);
        
        set(ax,'title','Frequency Response',...
              'xlabel','Frequency',...
              'ylabel','Magnitude (dB)',...
              'ylim',[-150 10],'xlim',[0 Fs/2],...
              'xlimbound',[0 Fs/2]);

        sethelp

        
        set(Lresp,'buttondownfcn','fdbutter(''LrespDown'')')
                       
        set(L1,'buttondownfcn','fdbutter(''L1down'')',...
               'buttonupfcn','fdbutter(''L1up'')');
    
        set(L2,'buttondownfcn','fdbutter(''L2down'')',...
               'buttonupfcn','fdbutter(''L2up'')');
               
        set(L3db1,'xdata',w3db_init([1 1]),...
                  'segmentdragcallback',{'fdbutter(''3db_drag'',1)'},...
                  'buttondownfcn','fdbutter(''3db_down'',1)',...
                  'buttonupfcn','fdbutter(''L1up'')');
                  
        set(L3db2,'xdata',w3db_init([end end]),...
                  'segmentdragcallback',{'fdbutter(''3db_drag'',2)'},...
                  'buttondownfcn','fdbutter(''3db_down'',2)',...
                  'buttonupfcn','fdbutter(''L1up'')');
    end
    set(minOrdCheckbox,'visible','on')
    set(pbspecs(1),'visible','on')
    
    fdbutter('newfilt',setOrderFlag_init,type_init,f_init,Rp_init,...
               Rs_init,w3db_init,order_init)
               
    if setOrderFlag_init
    % set these fields since 'Apply' gets them from the specifications struc
    % to make the measurements
        filt.specs.fdbutter.Rp = Rp_init;
        filt.specs.fdbutter.Rs = Rs_init;
    end
    
    [filt, errstr] = fdutil('callModuleApply',...
                               'fdbutter',filt,'');
                               
    varargout{1} = filt;
    varargout{2} = errstr;

case 'apply'    
    
    filt = varargin{2};
    msg = varargin{3};
    if strcmp(msg,'motion') | strcmp(msg,'up')
        inputLine = varargin{4};
        if ~minOrdCheckbox.value & (isequal(inputLine,L1) | isequal(inputLine,L2))
            varargout{1} = filt;
            varargout{2} = '';
            return
        end
    end
    if strcmp(msg,'motion') & ~strcmp(get(Lresp,'erasemode'),'xor')
        Lresp.erasemode = 'xor';
        drawnow
    end
    
    % DESIGN FILTER!!!!
    type = bandpop.value;
    setOrderFlag = ~minOrdCheckbox.value;
    if ~setOrderFlag     % estimate order
        [n,w3db] = estimateOrder(type,Rp.value,Rs.value,Fs,...
                 f1.value,f2.value,f3.value,f4.value);
    else
        n = order.value;
        w3db = w3db1.value * 2/Fs;
        if type > 2  % pass/stop
            w3db(2) = w3db2.value * 2/Fs;
        end
    end
    
    % save specifications in specifications structure:
    specstruc.setOrderFlag = setOrderFlag;
    specstruc.type = type;
    if ~setOrderFlag
        f = getFrequencyValues(type,f1,f2,f3,f4,Fs);
        specstruc.f = f;
        specstruc.Rp = Rp.value;
        specstruc.Rs = Rs.value;
    else
        specstruc.f = [];  % place holder, will be defined by measureFilt
        specstruc.Rp = filt.specs.fdbutter.Rp;
        specstruc.Rs = filt.specs.fdbutter.Rs;
    end
    specstruc.w3db = w3db;
    specstruc.order = n;
                          
%     if ~setOrderFlag & isfield(filt.specs,'fdbutter') & ...
%         isequal(specstruc.w3db,filt.specs.fdbutter.w3db) ...
%         & isequal(specstruc.order,filt.specs.fdbutter.order)
%         filt.specs.fdbutter = specstruc;
%         varargout{1} = filt;
%         varargout{2} = '';
%         return
%     end
    if n>50
        [continueFlag,errstr] = fdutil('largeWarning',n,msg);
        if ~continueFlag
            varargout{1} = filt;
            varargout{2} = errstr;
            return
        end
    end
    
    % design filter:
    if type == 2
        [b,a] = butter(n,w3db,'high');
    elseif type == 4
        [b,a] = butter(n,w3db,'stop');
    else
        [b,a] = butter(n,w3db);
    end
        
    % compute frequency response:
    nfft = filtdes('nfft');
    [H,ff] = freqz(b,a,nfft,Fs);
    % avoid log of 0 at 0 and Fs/2:
    if H(1) == 0, H(1) = H(2); end
    if H(end) == 0, H(end) = H(end-1); end
    Hlog = 20*log10(abs(H(:)));
    set(Lresp,'xdata',ff,'ydata',Hlog);
    
    % make measurements:
    if ~ ((strcmp(msg,'motion') | strcmp(msg,'up')) & ~setOrderFlag ...
             & strcmp(get(order1,'visible'),'on') )  | ...
       (~setOrderFlag & (filt.specs.fdbutter.type ~= type))
        objSetupFlag = 1;
    else
        objSetupFlag = 0;
    end
    
    [Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4,specstruc] = ...
      measureFilt(objSetupFlag,specstruc,Fs,order,order1,stopframe1,ax,...
                  pbmeasures,sbmeasures,Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4,...
                  ff,Hlog,L1,L2);
                      
    if ~strcmp(msg,'motion')
        % if design is due to a drag operation, lines are already solid
        % so we can skip this step
        Lresp.linestyle = '-';
        L1.linestyle = '-';
        L2.linestyle = '-';
        set(L3db1,'linestyle','-')
        set(L3db2,'linestyle','-')
        if strcmp(L3db1.erasemode,'normal')
            warning('L3db1 erasemode is normal... why?')
            L3db1.erasemode = 'xor';
            L3db2.erasemode = 'xor';
        end
    end
    
    % alter filt fields:
    filt.tf.num = b;
    filt.tf.den = a;
    filt.specs.fdbutter = specstruc;
    filt.zpk = [];  % clear out in case filtview has written these
    filt.ss = [];
    filt.sos = [];
    filt.type = 'design';

    varargout{1} = filt;
    varargout{2} = '';
        
case 'revert'
    filt = filtdes('filt');
    oldtype = filt.specs.fdbutter.type;
    % need to restore filter type
    setOrderFlag = filt.specs.fdbutter.setOrderFlag;
    oldSetOrderFlag = ~minOrdCheckbox.value;
    f = filt.specs.fdbutter.f;
    Rpass = filt.specs.fdbutter.Rp;
    Rstop = filt.specs.fdbutter.Rs;
    w3db = filt.specs.fdbutter.w3db;
    N = filt.specs.fdbutter.order;
    fdbutter('newfilt',setOrderFlag,oldtype,f,Rpass,Rstop,w3db,N)

    [Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4] = ...
      measureFilt(1,...
                  filt.specs.fdbutter,Fs,order,order1,stopframe1,ax,...
                  pbmeasures,sbmeasures,Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4,...
                  Lresp.xdata,Lresp.ydata,L1,L2);    
    
    Lresp.linestyle = '-';
    L1.linestyle = '-';
    L2.linestyle = '-';
    set(L3db1,'linestyle','-')
    set(L3db2,'linestyle','-')
    
    for i=1:3
        set(pbmeasures(i),'enable','on')
        set(sbmeasures(i),'enable','on')
    end
    set(order1,'enable','on')
    
case 'help'
    str = fdhelpstr('fdbutter');
    varargout{1} = str{2};

case 'description'
    varargout{1} = 'Butterworth IIR';

case 'Fs'
% Sampling frequency has changed

%   The filter spec does not depend on the sampling frequency
    filt = varargin{2};
    varargout{1} = filt;
    
% update various lines and specifications:
    oldFs = varargin{3};
    Fs = filt.Fs;  % new Fs
    f1.value = f1.value*Fs/oldFs;
    f2.value = f2.value*Fs/oldFs;
    f3.value = f3.value*Fs/oldFs;
    f4.value = f4.value*Fs/oldFs;
    f1.range = f1.range*Fs/oldFs;
    f2.range = f2.range*Fs/oldFs;
    f3.range = f3.range*Fs/oldFs;
    f4.range = f4.range*Fs/oldFs;

    ax.xlimbound = [0 Fs/2];
    ax.xlim = ax.xlim*Fs/oldFs;
    ax.xlimpassband = ax.xlimpassband*Fs/oldFs;
    L1.xdata = L1.xdata*Fs/oldFs;
    L2.xdata = L2.xdata*Fs/oldFs;
    Lresp.xdata = Lresp.xdata*Fs/oldFs;
    L3db1.xdata = L3db1.xdata*Fs/oldFs;
    L3db2.xdata = L3db2.xdata*Fs/oldFs;
    
    if minOrdCheckbox.value
        w3db1m.value = w3db1m.value*Fs/oldFs;
        w3db2m.value = w3db2m.value*Fs/oldFs;
    elseif ~any(w3db1==[f1 f2 f3 f4])
        w3db1.value = w3db1.value*Fs/oldFs;
        w3db2.value = w3db2.value*Fs/oldFs;
        w3db1.range = w3db1.range*Fs/oldFs;
        w3db2.range = w3db2.range*Fs/oldFs;
    end
    
%---------------------------------------------------------------------
% -------- following cases are module specific --------- 
%---------------------------------------------------------------------

case 'dirty'
% fdbutter('dirty')
% Callback of a spec ... to change appearance so user can
%  see that the specs and the currently designed filter
%  don't match
    if Lresp.linestyle ~= ':'
        Lresp.linestyle = ':';
        L1.linestyle = ':';
        L2.linestyle = ':';
        L3db1.linestyle = ':';
        L3db2.linestyle = ':';
    end
    for i=1:3
        set(pbmeasures(i),'enable','off')
        set(sbmeasures(i),'enable','off')
    end
    set(order1,'enable','off')

%---------------------------------------------------------------------
% fdbutter('clean')
% opposite of 'dirty'
case 'clean'
    if Lresp.linestyle ~= '-'
        Lresp.linestyle = '-';
        L1.linestyle = '-';
        L2.linestyle = '-';
        L3db1.linestyle = '-';
        L3db2.linestyle = '-';
    end
    for i=1:3
        set(pbmeasures(i),'enable','on')
        set(sbmeasures(i),'enable','on')
    end
    set(order1,'enable','on')
    filtdes('setenable','off')
    
%---------------------------------------------------------------------
% fdbutter('fchange',i)
%    Callback when frequency i has changed
%    Need to update ranges and lines L1, L2
% fdbutter('fchange')
%    called w/o input i when a 3db frequency spec has 
%    changed (in case ~minOrdCheckbox.val==1)
case 'fchange'
    type = bandpop.value;
    if nargin == 1
    % a 3db freq spec has changed  (we are in set mode)
        
        if type >= 3
            w3db = [w3db1.value w3db2.value];
            set(L3db1,'xdata',w3db([1 1]))
            set(L3db2,'xdata',w3db([2 2]))
            w3db1.range = [0 w3db(2)];
            w3db2.range = [w3db(1) Fs/2];
        else
            w3db = w3db1.value;
            set(L3db1,'xdata',w3db([1 1]))
        end
        fdbutter('dirty')
        return
    end
    i = varargin{2};
    xd1 = L1.xdata;
    xd2 = L2.xdata;
    f = [f1.value,f2.value,f3.value,f4.value];
    % update lines
    switch type
    case 1
      % L1 xdata = [0 f1 NaN 0 f1]
      % L2 xdata = [f2 Fs/2]
        if i==1
            xd1([2 5]) = f(1);
        else
            xd2(1) = f(2);
        end
    case 2
      % L1 xdata = [f2 Fs/2 NaN f2 Fs/2]
      % L2 xdata = [0 f1]
        if i == 1
            xd2(2) = f(1);
        else
            xd1([1 4]) = f(2);
        end
    case 3  % bandpass
      % L1 xdata = [f2 f3 NaN f2 f3]
      % L2 xdata = [0 f1 NaN f4 Fs/2]
        if xd2(2) ~= f(1)
            xd2(2) = f(1);
        elseif xd1(1) ~= f(2)
            xd1([1 4]) = f(2);
        elseif xd1(2) ~= f(3)
            xd1([2 5]) = f(3);
        elseif xd2(4) ~= f(4)
            xd2(4) = f(4);
        end
    case 4
      % L1 xdata = [0 f1 NaN 0 f1 NaN f4 Fs/2 NaN f4 Fs/2]
      % L2 xdata = [f2 f3]
        if xd1(2) ~= f(1)
            xd1([2 5]) = f(1);
        elseif xd2(1) ~= f(2)
            xd2(1) = f(2);
        elseif xd2(2) ~= f(3)
            xd2(2) = f(3);
        elseif xd1(7) ~= f(4)
            xd1([7 10]) = f(4);
        end
    end
    
    if minOrdCheckbox.value
        [xd1,xd2] = fdutil('validateBands',xd1,xd2,...
                           type,i,f,f1,f2,f3,f4,Fs);
        fdbutter('dirty')
    else
        fChangeMeas(i,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
        return
    end

    dirty = 0; % Flag used to indicate that specs have changed.
    
    % If there's a NaN in the data being compared the if-statement will
    % fail (return false) even tough the two variables are equal.  So,
    % remove the NaNs before comparing.
    nonNaNindx_L1 = ~isnan(L1.xdata);
    nonNaNindx_xd1 = ~isnan(xd1);
    if ~isequal(xd1(nonNaNindx_xd1),L1.xdata(nonNaNindx_L1)) 
        % only set if changed
        L1.xdata = xd1;
        dirty = 1;
    end

    % If there's a NaN in the data being compared the if-statement will
    % fail (return false) even tough the two variables are equal.  So,
    % remove the NaNs before comparing.
    nonNaNindx_L2 = ~isnan(L2.xdata);
    nonNaNindx_xd2 = ~isnan(xd2);
    if ~isequal(xd2(nonNaNindx_xd2),L2.xdata(nonNaNindx_L2)) 
        % only set if changed
        L2.xdata = xd2;
        dirty = 1;
    end

    if dirty  % Specs have changed; update passband limits
        ax.xlimPassband = fdutil('xlimpassband',type,...
                         Fs,f1.value,f2.value,f3.value,f4.value);
    end
    
    % fdutil('updateRanges',i,f1,f2,f3,f4)    % update ranges
    
%---------------------------------------------------------------------
% fdbutter('Rpchange')
%  Callback when Rp has changed
%  Need to update line L1
case 'Rpchange'
    type = bandpop.value;
    if minOrdCheckbox.value
        Rpass = Rp.value;
        above = 0; below = -Rpass;
    
        if type ~= 4
        % 'ydata':[maxpass maxpass NaN minpass minpass]
            yd = [above above NaN below below];
        else
        % 'ydata': [ maxpass maxpass NaN minpass minpass NaN ...
        %                    maxpass maxpass NaN minpass minpass])
            yd = [above above NaN below below ...
                   above above NaN below below ];
        end
        L1.ydata = yd;
        ylim = [below above];
        dyl = (ylim(2)-ylim(1))*.15;
        
        ax.ylimPassband = ylim + [-dyl/2 dyl/2];
    
        fdbutter('dirty')
    else
        RChangeMeas(1,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
    end

    
%---------------------------------------------------------------------
% fdbutter('Rschange')
%  Callback when Rs has changed
%  Need to update line L2
case 'Rschange'
    type = bandpop.value;
    
    if minOrdCheckbox.value

        Rstop = Rs.value;
        if type ~= 3
            yd = [-Rstop -Rstop];
        else
            yd = [-Rstop -Rstop NaN -Rstop -Rstop];
        end
        L2.ydata = yd;
        fdbutter('dirty')
    else
        RChangeMeas(0,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
    end


%---------------------------------------------------------------------
% fdbutter('checkbox')
%  Callback of minimum order checkbox
case 'checkbox'
    filt = filtdes('filt');
    
    newSetOrderFlag = ~get(minOrdCheckbox,'value');
    type = bandpop.value;
    oldtype = filt.specs.fdbutter.type;
    
    if ~newSetOrderFlag   % from set to estimate order 
        if filt.specs.fdbutter.setOrderFlag
            % obtain frequencies from measurements
            c = {pbmeasures(1) pbmeasures(2) sbmeasures(1) sbmeasures(2)};
            if (type==2) | (type==3)
                ind = [3 1 2 4];
            else
                ind = [1 3 4 2];
            end
            f = getFrequencyValues(oldtype,c{ind},Fs);
        else
            % obtain frequencies from filter specs field
            f = filt.specs.fdbutter.f;
        end
        f = [0; fdutil('changeFilterType',type,oldtype,sort(f(2:end-1))); 1];
        w3db = [0 0];  % place holder - ignored by 'newfilt'     
    else   % from estimate to set order 
        w3db = sbmeasures(1).value * 2/Fs;
        if type > 2
            if oldtype > 2
                w3db(2) = sbmeasures(2).value * 2/Fs;
            else
                w3db(2) = (w3db(1)+1)/2;
            end
        end
        f = [];  % place holder - ignored by 'newfilt'
    end
    fdbutter('newfilt',newSetOrderFlag,...
                       type,f,Rp.value,...
                       Rs.value,w3db,order.value)
    fdbutter('dirty')


%---------------------------------------------------------------------
% fdbutter('newfilt',setOrderFlag,type,f,Rp,Rs,w3db,order)
%  set values of SPECIFICATIONS objects ...  DOES NOT DESIGN FILTER
case 'newfilt'
    setOrderFlag = varargin{2};
    type = varargin{3};
    f = varargin{4};     % in range (0,1)
    Rpass = varargin{5};
    Rstop = varargin{6};
    w3db = varargin{7};  % in range (0,1)
    N = varargin{8};

    co = get(0,'defaultaxescolororder');
    
    bandpop.value = type;
    % save last value of bandpop in passframe userdata:
    passframe.userdata = type;
    minOrdCheckbox.value = ~setOrderFlag;

    if ~setOrderFlag  % estimate order
        % initialize specs:
        passframe.visible = 'on';
        stopframe.visible = 'on';
        order = order1;
        set(order0,'visible','off')

        [f1,f2,f3,f4] = setupFrequencyObjects(type,'fdbutter',...
                           pbspecs,sbspecs,f,Fs,ax);
        
        pbspecs(3).visible = 'on';
        sbspecs(1).visible = 'on';
        sbspecs(3).visible = 'on';
                
        Rp = pbspecs(3);
        Rs = sbspecs(3);
        set(Rp,'value',Rpass,'label','Rp','callback','fdbutter(''Rpchange'')')
        set(Rs,'value',Rstop,'label','Rs','callback','fdbutter(''Rschange'')')
                
        L1.color = co(min(2,size(co,1)),:);
        L2.color = co(min(2,size(co,1)),:);   
        set(L3db1,'visible','off')     
        set(L3db2,'visible','off')     
    else  % set order
        passframe.visible = 'off';
        stopframe.visible = 'off';
        order = order0;
        set(order0,'visible','on')
                        
        w3db1 = pbspecs(1);
        w3db2 = pbspecs(2);
        set(pbspecs(3),'visible','off')
        set(sbspecs(1),'visible','off')
        set(sbspecs(2),'visible','off')
        set(sbspecs(3),'visible','off')
       
        set(L3db1,'visible','on','xdata',w3db([1 1])*Fs/2)     
        if type < 3  % low/high
            set(w3db1,'value',w3db(1)*Fs/2,'range',[0 1]*Fs/2,...
               'label','F3db','callback','fdbutter(''fchange'')',...
               'visible','on')
            w3db2.visible = 'off';
            set(L3db2,'visible','off')
        else  % pass/stop
            set(w3db1,'value',w3db(1)*Fs/2,'range',[0 w3db(2)]*Fs/2,...
               'label','F3db 1','callback','fdbutter(''fchange'')',...
               'visible','on')
            set(w3db2,'value',w3db(2)*Fs/2,'range',[w3db(1) 1]*Fs/2,...
               'label','F3db 2','callback','fdbutter(''fchange'')',...
               'visible','on')
            set(L3db2,'visible','on','xdata',w3db([2 2])*Fs/2)     
        end
                
        L1.color = co(min(3,size(co,1)),:);
        L2.color = co(min(3,size(co,1)),:);
        order.value = N;
    end

    if ax.xlimbound(2) ~= Fs/2
        set(ax,'xlimbound',[0 Fs/2],'xlim',[0 Fs/2])
    end
    
    if ~setOrderFlag  % estimate order
        minpass = -Rpass;
        maxpass = 0;
        minstop = -Rstop;
        fdutil('setLines','fdbutter',L1,L2,0,...
                           type,f(:)',Fs,minpass,maxpass,minstop)
    else  % set order
        set(L1,'segmentdragmode',{'none'},...
               'segmentpointer',{'forbidden'},...
               'vertexdragmode',{'none'},...
               'vertexpointer',{'forbidden'})
        set(L2,'segmentdragmode',{'none'},...
               'segmentpointer',{'forbidden'},...
               'vertexdragmode',{'none'},...
               'vertexpointer',{'forbidden'})
    end
    
%---------------------------------------------------------------------
% fdbutter('newtype')
%  callback of band configuration popup
case 'newtype'
    filt = filtdes('filt');
    newtype = bandpop.value;
    oldtype = get(passframe,'userdata');
    if isempty(oldtype)
        oldtype = filt.specs.fdbutter.type;
    end
    passframe.userdata = newtype;
    if (newtype ~= oldtype) | strcmp(filtdes('getenable'),'on')
        if oldtype < 3  % low or high pass
            edges = [f1.value f2.value]'*2/Fs;
        else
            edges = [f1.value f2.value f3.value f4.value]'*2/Fs;
        end
        edges = fdutil('changeFilterType',newtype,oldtype,edges);
        f = [0; edges(:); 1];
        
        if ~minOrdCheckbox.value
            w3db = w3db1.value * 2/Fs;
            if newtype > 2
                w3db(2) = w3db2.value * 2/Fs;
                if w3db(2) <= w3db(1) | w3db(2)>1
                    w3db(2) = (w3db(1) + 1)/2;
                    w3db2.value = w3db(2)*Fs/2;
                end
            end
        else
            w3db = [0 0]; % place holder - ignored by newfilt
        end
        fdbutter('newfilt',~minOrdCheckbox.value,newtype,f,Rp.value,...
                  Rs.value,w3db,order.value)
        fdbutter('dirty')
        
    else
       % disp('no change of type')
    end
    
%---------------------------------------------------------------------
% fdbutter('Lrespdown')
%  Button down fcn of Lresp (response line) - pan
case 'LrespDown'
    bounds.xlim = [0 Fs/2];
    bounds.ylim = [-500 30];
    h = ax.h;
    panfcn('Ax',h,...
           'Bounds',bounds,...
           'UserHand',get(h,'zlabel'),...
           'Invisible',[L3db1.h L3db2.h])
           
%---------------------------------------------------------------------
% fdbutter('L1down')
%  Button down fcn of L1
% fdbutter('L2down')
%  Button down fcn of L2
% fdbutter('3db_down',i)
%   buttondown fcn of 3db line 1 or 2 (i==1 or 2)
case {'L1down', 'L2down', '3db_down'}
    L1.erasemode = 'xor';
    L2.erasemode = 'xor';
   
%---------------------------------------------------------------------
% fdbutter('L1up')
%  Button up fcn of L1
% fdbutter('L2up')
%  Button up fcn of L2
case {'L1up', 'L2up'}
    L1.erasemode = 'normal';
    L2.erasemode = 'normal';
    Lresp.erasemode = 'normal';
    
%---------------------------------------------------------------------
% fdbutter('3db_drag',ind)
%  segment drag callback of L3db1 and L3db2 - 3db frequency lines
%  Inputs:
%     ind - index of line being dragged, 1 or 2
case '3db_drag'
    ind = varargin{2};
    minspacing = Fs/500;
    if ind == 1
        xd = L3db1.xdata;
        newf3db1 = inbounds(xd(1),[minspacing w3db1.range(2)-minspacing]);
        if newf3db1 ~= w3db1.value
            w3db1.value = newf3db1;
            w3db2.range = [newf3db1 Fs/2];
            if newf3db1 ~= xd(1)
                L3db1.xdata = newf3db1([1 1]);
            end
        end
    else
        xd = L3db2.xdata;
        newf3db2 = inbounds(xd(1),[w3db2.range(1)+minspacing Fs/2-minspacing]);
        if newf3db2 ~= w3db2.value
            w3db2.value = newf3db2;
            w3db1.range = [0 newf3db2];
            if newf3db2 ~= xd(1)
                L3db2.xdata = newf3db2([1 1]);
            end
        end
    end

%---------------------------------------------------------------------
% fdbutter('L1drag',type,ind)
%  vertex drag callback of L1 - passband line
%  Inputs:
%     type - band configuration 1==low, 2=high, 3=pass, 4=stop
%     ind - index of vertex being dragged
case 'L1drag'
    type = varargin{2};
    ind = varargin{3};
    xd = L1.xdata;
    minspacing = Fs/500;
    switch type
    case 1  % lowpass
        newf1 = inbounds(xd(ind),[minspacing f2.value-minspacing]);
        xd([2 5]) = newf1;
        L1.xdata = xd;
        f1.value = newf1;
        i = 1;
    case 2  % highpass
        newf2 = inbounds(xd(ind),[f1.value+minspacing Fs/2-minspacing]);
        xd([1 4]) = newf2;
        L1.xdata = xd;
        f2.value = newf2;
        i = 2;
    case 3  % bandpass
    % L1 xdata = [f2 f3 NaN f2 f3]
        if any(ind == [1 4])   % dragging f2
            newf2 = inbounds(xd(ind),[f1.value+minspacing f3.value-minspacing]);
            xd([1 4]) = newf2;
            L1.xdata = xd;
            f2.value = newf2;
            i = 2;
        else % dragging f3
            newf3 = inbounds(xd(ind),[f2.value+minspacing f4.value-minspacing]);
            xd([2 5]) = newf3;
            L1.xdata = xd;
            f3.value = newf3;
            i = 3;
        end
    case 4   % bandstop
    % L1 xdata = [0 f1 NaN 0 f1 NaN f4 Fs/2 NaN f4 Fs/2]
        if any(ind == [2 5])   % dragging f1
            newf1 = inbounds(xd(ind),[minspacing f2.value-minspacing]);
            xd([2 5]) = newf1;
            L1.xdata = xd;
            f1.value = newf1;
            i = 1;
        else % dragging f4
            newf4 = inbounds(xd(ind),[f3.value+minspacing Fs/2-minspacing]);
            xd([7 10]) = newf4;
            L1.xdata = xd;
            f4.value = newf4;
            i = 4;
        end
    end
    if minOrdCheckbox.value
%         [n,w3db] = estimateOrder(type,Rp.value,Rs.value,Fs,...
%                  f1.value,f2.value,f3.value,f4.value);
%         order.value = n;
%         w3db1m.value = w3db(1) * Fs/2;
%         if length(w3db)>1
%             w3db2m.value = w3db(2) * Fs/2;
%         end
    else
        fChangeMeas(i,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
        return
    end
    ax.xlimPassband = fdutil('xlimpassband',type,...
                         Fs,f1.value,f2.value,f3.value,f4.value);

    % fdutil('updateRanges',i,f1,f2,f3,f4)

%---------------------------------------------------------------------
% fdbutter('L2drag',type,ind)
%  drag callback of L2 - stopband line
%  Inputs:
%     type - band configuration 1==low, 2=high, 3=pass, 4=stop
%     ind - index of vertex being dragged
case 'L2drag'
    type = varargin{2};
    ind = varargin{3};
    xd = L2.xdata;
    minspacing = Fs/500;
    switch type
    case 1  % lowpass
        newf2 = inbounds(xd(ind),[f1.value+minspacing Fs/2-minspacing]);
        xd(1) = newf2;
        L2.xdata = xd;
        f2.value = newf2;
        i = 2;
    case 2  % highpass
        newf1 = inbounds(xd(ind),[minspacing f2.value-minspacing]);
        xd(2) = newf1;
        L2.xdata = xd;
        f1.value = newf1;
        i = 1;
    case 3  % bandpass
    % L2 xdata = [0 f1 NaN f4 Fs/2]
        if ind == 2   % dragging f1
            newf1 = inbounds(xd(ind),[minspacing f2.value-minspacing]);
            xd(2) = newf1;
            L2.xdata = xd;
            f1.value = newf1;
            i = 1;
        else % dragging f4
            newf4 = inbounds(xd(ind),[f3.value+minspacing Fs/2-minspacing]);
            xd(4) = newf4;
            L2.xdata = xd;
            f4.value = newf4;
            i = 4;
        end
    case 4   % bandstop
    % L2 xdata = [f2 f3]
        if ind == 1   % dragging f2
            newf2 = inbounds(xd(ind),[f1.value+minspacing f3.value-minspacing]);
            xd(1) = newf2;
            L2.xdata = xd;
            f2.value = newf2;
            i = 2;
        else % dragging f3
            newf3 = inbounds(xd(ind),[f2.value+minspacing f4.value-minspacing]);
            xd(2) = newf3;
            L2.xdata = xd;
            f3.value = newf3;
            i = 3;
        end
    end
    if ~minOrdCheckbox.value
        fChangeMeas(i,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
    end
    
%---------------------------------------------------------------------
% fdbutter('Rpdrag',type,ind)
%  drag callback of L1 - passband line
%  Inputs:
%     type - band configuration 1==low, 2=high, 3=pass, 4=stop
%     ind - index of segment being dragged
case 'Rpdrag'
    type = varargin{2};
    ind = varargin{3};
    yd = L1.ydata;
    
    below = yd(ind);
    if below >= 0 
        below = -.00001;
    elseif below < -10
        below = -10;
    end
    above = 0;
    
    newRp = above-below;

    if minOrdCheckbox.value
        if type ~= 4
        % 'ydata':[maxpass maxpass NaN minpass minpass]
            yd = [above above NaN below below];
        else
        % 'ydata': [ maxpass maxpass NaN minpass minpass NaN ...
        %                    maxpass maxpass NaN minpass minpass])
            yd = [above above NaN below below ...
                   NaN above above NaN below below ];
        end
        L1.ydata = yd;
        Rp.value = newRp;
        ylim = [below above];
        dyl = (ylim(2)-ylim(1))*.15;
        ax.ylimPassband = ylim + [-dyl/2 dyl/2];
    else
        Rp.value = newRp;
        RChangeMeas(1,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
    end
 
%---------------------------------------------------------------------
% fdbutter('Rsdrag',type,ind)
%  drag callback of L2 - stopband line
%  Inputs:
%     type - band configuration 1==low, 2=high, 3=pass, 4=stop
%     ind - index of segment being dragged
case 'Rsdrag'
    type = varargin{2};
    ind = varargin{3};
  
    yd = L2.ydata;
    newRs = -yd(ind);
    if newRs < 0
        newRs = 0;
    end
    if minOrdCheckbox.value
        switch type
        case {1,2,4}
            L2.ydata = [-newRs -newRs];
        case 3
            L2.ydata = [-newRs -newRs NaN -newRs -newRs];
        end
        set(Rs,'value', newRs)
    else
        set(Rs,'value', newRs)
        RChangeMeas(0,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
    end
    
end  % of function switch-yard

%---------------------------------------------------------------------
% -------- LOCAL FUNCTIONS START HERE  --------- 
%---------------------------------------------------------------------

function sethelp
global minOrdCheckbox bandpop 
global passframe stopframe passframe1 stopframe1
global f1 f2 f3 f4 w3db1 w3db2 order Rp Rm pbspecs sbspecs
global Rpm w3db1m w3db2m
global ax L1 L2 Lresp
global Fs

% disp('setting help ... stub')

function [n,w3db] = estimateOrder(type,Rp,Rs,Fs,f1,f2,f3,f4)
% [n,w3db] = estimateOrder(type,Rp,Rs,Fs,f1,f2,f3,f4)
%   estimate filter order
%   takes the specifications as given by the input
%   parameters and estimates the order and 3db frequencies
%   needed to meet those specifications.
% Inputs:
%  type - 1,2,3,4 specifies band configuration
%  Rp, Rs passband, stopband ripple
%  Fs - sampling frequency
%  f1,f2  first two frequencies in ascending order
%  f3,f4 only needed if type == 3 or 4, remaining frequencies
%    f1,f2,f3,f4 are assumed between 0 and Fs/2 on input
% Outputs:
%  n - filter order
%  w3db - filter band edges for butter, normalized to range [0...1]

if type == 1    % low pass
    Wp = f1;   Ws = f2;
elseif type == 2   % high pass
    Wp = f2;   Ws = f1;
elseif type == 3   % band pass
    Wp = [f2 f3];   Ws = [f1 f4];
elseif type == 4   % band stop
    Wp = [f1 f4];   Ws = [f2 f3];
end
[n,w3db] = buttord(Wp*2/Fs,Ws*2/Fs,Rp,Rs);
w3db = w3db(:)';   % make it a row 
 

function yd = passbandlimits(Rp)
% return ydata = [minpass maxpass] of passband 
%   given Rp decibels of ripple in passband (with maximum 1 in linear scale)
    above = 0; below = -Rp;
    yd = [below above];


function [maxpass,minpass,minstop] = getMagMeasurements(ff,Hlog,type,...
                                          f1,f2,f3,f4);
%getMagMeasurements
% Finds passband and stopband ripple for given band edges 
% given a filter's magnitude response
% Inputs:
%  ff - xdata of response
%  Hlog - magnitude of response at frequencies ff, in dB
%  type - band configuration (lp = 1, hp = 2, bp = 3, bs = 4)
%  f1, f2, f3, f4 - band edges (f3 and f4 ignored if type < 3)
%           in same units as ff
% Output:
%  fm - 2 or 4 element frequency vector in ascending order
    switch type
    case 1   % lowpass
        passInd = find(ff<=f1);
        stopInd = find(ff>=f2);
    case 2   % highpass
        stopInd = find(ff<=f1);
        passInd = find(ff>=f2);
    case 3   % bandpass
        stopInd = find((ff<=f1)|(ff>=f4));
        passInd = find((ff>=f2)&(ff<=f3));
    case 4   % bandstop
        passInd = find((ff<=f1)|(ff>=f4));
        stopInd = find((ff>=f2)&(ff<=f3));
    end
    maxpass = max(Hlog(passInd));
    minpass = min(Hlog(passInd));
    minstop = max(Hlog(stopInd));

function [fm,Rs] = getFreqMeasurements(ff,Hlog,type,Rp,Rs)
%getFreqMeasurements
% Finds band edges for a given passband and stopband ripple
% given a filter's magnitude response
% Inputs:
%  ff - xdata of response (assumed a column vector)
%  Hlog - magnitude of response at frequencies ff, in dB
%               (assumed a column vector)
%  type - band configuration (lp = 1, hp = 2, bp = 3, bs = 4)
%  Rp - passband ripple, in dB
%  Rs - stopband attenuation in dB
% Output:
%  fm - 2 or 4 element frequency vector in ascending order
%  Rs - changed value of Rs in case given Rs is unattainable
    passInd = find(Hlog(:)>=-Rp);
    stopInd = find((Hlog(:)<=-Rs) & (Hlog(:)<-Rp));
    switch type
    case 1   % lowpass
        if isempty(stopInd)
           [Rs1,stopInd] = min(Hlog);  Rs1 = -Rs1;
           if Rs1<Rs
               Rs = Rs1;
           else
               stopInd = passInd(end)+1;
               Rs = -Hlog(stopInd);
           end
        end
        fm = ff([passInd(end); stopInd(1)]);
    case 2   % highpass
        if isempty(stopInd)
           [Rs1,stopInd] = min(Hlog);  Rs1 = -Rs1;
           if Rs1<Rs
               Rs = Rs1;
           else
               stopInd = passInd(1)-1;
               Rs = -Hlog(stopInd);
           end
        end
        fm = ff([stopInd(end); passInd(1)]);
    case 3   % bandpass
        if isempty(stopInd)
           [Rs1,stopInd1] = min(Hlog(1:passInd(1)));    Rs1 = -Rs1;
           [Rs2,stopInd2] = min(Hlog(passInd(end):end));  Rs2 = -Rs2;
           Rs1 = max([Rs1 Rs2]);
           if Rs1<Rs
               Rs = Rs1;
               stopInd = [stopInd1 stopInd2];
           else
               stopInd = [passInd(1)-1 passInd(end)+1];
               Rs = -max(Hlog(stopInd));
           end
        end
        %  index of beginning of upper stop band
        i = min(find(stopInd>=passInd(1))); 
        if isempty(i)
            i = length(stopInd);
            if i == 1
                stopInd = [1 2];
                i = 2;
            end
        end
        fm = ff([stopInd(i-1); passInd([1 end]); stopInd(i)]);
    case 4   % bandstop
        if isempty(stopInd)
           [Rs1,stopInd] = min(Hlog);    Rs1 = -Rs1;
           if Rs1<Rs
               Rs = Rs1;
           else
               i = min(find(passInd>=stopInd(1)));
               stopInd = [passInd(i-1)+1 passInd(i)-1];
               Rs = -max(Hlog(stopInd))
           end
        end
        %  index of beginning of upper pass band
        i = min(find(passInd>=stopInd(1)));
        fm = ff([passInd(i-1); stopInd(1); stopInd(end); passInd(i)]);
    end

function fChangeMeas(i,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
%fChangeMeas - interactively track response when a frequency has changed
%  This function is meant to be called when the user
%     a) changes the f1,f2,f3, or f4 measurement
%  or b) drags the f1,f2,f3, f4 value
%  Xdata, Ydata, values of other frequencies and Rp and Rs are updated as
%  needed.
%  Inputs:
%    i - which frequency has just changed
%    type - band configuration (1 = lp, 2 = hp, 3 = bp, 4 = bs)
%    Fs - sampling frequency
%    Lresp, L1, L2 - fdline objects
%    f1,f2,f3,f4,Rp,Rs - fdmeas objects

    ff = Lresp.xdata;
    delf = ff(2)-ff(1);  % frequency spacing
    Hlog = Lresp.ydata;
    switch type
    case 1 % lowpass
        if i == 1     % passband edge
            ind = round(f1.value/delf) + 1;
            f1.value = ff(ind);
            Rp.value = -Hlog(ind);
            passbandChange = 1;
        else  % stopband edge
            ind = round(f2.value/delf) + 1;
            f2.value = ff(ind);
            set(Rs,'value', -Hlog(ind))
            passbandChange = 0;
        end
    case 2  % highpass
        if i == 1     % stopband edge
            ind = round(f1.value/delf) + 1;
            f1.value = ff(ind);
            set(Rs,'value', -Hlog(ind))
            passbandChange = 0;
        else  % passband edge
            ind = round(f2.value/delf) + 1;
            f2.value = ff(ind);
            Rp.value = -Hlog(ind);
            passbandChange = 1;
        end
    case 3 % bandpass
        switch i
        case 1     % lower stopband edge
            ind = round(f1.value/delf) + 1;
            f1.value = ff(ind);
            set(Rs,'value', -Hlog(ind))
            passbandChange = 0;
        case 2     % lower passband edge
            ind = round(f2.value/delf) + 1;
            f2.value = ff(ind);
            Rp.value = -Hlog(ind);
            passbandChange = 1;
        case 3     % upper passband edge
            ind = round(f3.value/delf) + 1;
            f3.value = ff(ind);
            Rp.value = -Hlog(ind);
            passbandChange = 1;
        case 4     % upper stopband edge
            ind = round(f4.value/delf) + 1;
            f4.value = ff(ind);
            set(Rs,'value', -Hlog(ind))
            passbandChange = 0;
        end
    case 4 % bandstop
        switch i
        case 1     % lower passband edge
            ind = round(f1.value/delf) + 1;
            f1.value = ff(ind);
            Rp.value = -Hlog(ind);
            passbandChange = 1;
        case 2     % lower stopband edge
            ind = round(f2.value/delf) + 1;
            f2.value = ff(ind);
            set(Rs,'value', -Hlog(ind))
            passbandChange = 0;
        case 3     % upper stopband edge
            ind = round(f3.value/delf) + 1;
            f3.value = ff(ind);
            set(Rs,'value', -Hlog(ind))
            passbandChange = 0;
        case 4     % upper passband edge
            ind = round(f4.value/delf) + 1;
            f4.value = ff(ind);
            Rp.value = -Hlog(ind);
            passbandChange = 1;
        end
    end
    if type >= 3  % find new freq. values in bp, bs case
        fm = getFreqMeasurements(ff,Hlog,type,Rp.value,Rs.value);
        switch type
        case 3 % bandpass - update f2 f3
            if passbandChange
                f2.value = fm(2);
                f3.value = fm(3);
            else
                f1.value = fm(1);
                f4.value = fm(4);
            end
        case 4 % bandstop - update f1 f4
            if passbandChange
                f1.value = fm(1);
                f4.value = fm(4);
            else
                f2.value = fm(2);
                f3.value = fm(3);
            end
        end
    end
    if type > 2
        fchange_ind = (i==(1:4)) * [1 4; 2 3; 2 3; 1 4];
    else
        fchange_ind = i;
    end
    updateLines(passbandChange,type,fchange_ind,f1,f2,f3,f4,Rp,Rs,Fs)
    fdutil('pokeFilterMeasurements','fdbutter',type,f1,f2,f3,f4,Rp,Rs,Fs)

function RChangeMeas(passbandChange,type,Fs,Lresp,L1,L2,f1,f2,f3,f4,Rp,Rs)
%RChangeMeas - interactively track response when Rp or Rs has changed
%  This function is meant to be called when the user
%     a) changes the Rp or Rs measurement
%  or b) drags the Rp or Rs value
%  Xdata, Ydata, values of other frequencies and Rp and Rs are updated as
%  needed.
%  Inputs:
%    passbandChange - boolean == 1 for Rp, 0 for Rs, or vector [0 1] for
%          both Rp and Rs
%    type - band configuration (1 = lp, 2 = hp, 3 = bp, 4 = bs)
%    Fs - sampling frequency
%    Lresp, L1, L2 - fdline objects
%    f1,f2,f3,f4,Rp,Rs - fdmeas objects

    ff = Lresp.xdata;
    delf = ff(2)-ff(1);  % frequency spacing
    Hlog = Lresp.ydata;
    Rs_val = get(Rs,'value');
    [fm,Rs_val1] = getFreqMeasurements(ff,Hlog,type,Rp.value,Rs_val);
    if Rs_val ~= Rs_val1
        set(Rs,'value',Rs_val1)
    end
    i = [];  % which frequencies have changed
    switch type
    case 1
        if any(passbandChange==1)
            f1.value = fm(1);
            i = 1;
        end
        if any(passbandChange==0)
            f2.value = fm(2);
            i = [i 2];
        end
    case 2
        if any(passbandChange==1)
            f2.value = fm(2);
            i = 2;
        end
        if any(passbandChange==0)
            f1.value = fm(1);
            i = [1 i];
        end
    case 3 % bandpass - update f2 f3
        if any(passbandChange==1)
            f2.value = fm(2);
            f3.value = fm(3);
            i = [2 3];
        end
        if any(passbandChange==0)
            f1.value = fm(1);
            f4.value = fm(4);
            i = [1 i 4];
        end
    case 4 % bandstop - update f1 f4
        if any(passbandChange==1)
            f1.value = fm(1);
            f4.value = fm(4);
            i = [1 4];
        end
        if any(passbandChange==0)
            f2.value = fm(2);
            f3.value = fm(3);
            i = sort([i 2 3]);
        end
    end
    updateLines(passbandChange,type,i,f1,f2,f3,f4,Rp,Rs,Fs)
    fdutil('pokeFilterMeasurements','fdbutter',type,f1,f2,f3,f4,Rp,Rs,Fs)
    
    
function updateLines(passbandChange,type,fchange_ind,f1,f2,f3,f4,Rp,Rs,Fs)
% assume values of f1,f2,f3,f4,Rp and Rs are correct now
% fchange_ind - vector of indices indicating which frequencies have
%               changed
    global L1 L2 ax
    
    f = getFrequencyValues(type,f1,f2,f3,f4,Fs)*Fs/2;
    
    if any(passbandChange==1)
        maxpass = 0;
        minpass = -Rp.value;
        % update L1 xdata and ydata
        switch type
        case 1   % lowpass
            set(L1,'xdata',[f(1:2) NaN f(1:2)],...
                   'ydata',[maxpass maxpass NaN minpass minpass])
        case 2   % highpass
            set(L1,'xdata',[f(3:4) NaN f(3:4)],...
                   'ydata',[maxpass maxpass NaN minpass minpass ])
        case 3   % bandpass
            set(L1,'xdata',[f(3:4) NaN f(3:4)],...
                   'ydata',[maxpass maxpass NaN minpass minpass])
        case 4   % bandstop
            set(L1,'xdata',[f(1:2) NaN f(1:2) NaN f(5:6) NaN f(5:6)],...
                   'ydata',[ maxpass maxpass NaN minpass minpass NaN ...
                             maxpass maxpass NaN minpass minpass])
        end
        ylim = [minpass maxpass];
        dyl = (ylim(2)-ylim(1))*.15;
        ax.ylimPassband = ylim + [-dyl/2 dyl/2];
        if length(f)==4
            f(6)=0;  % zeropad for call to xlimpassband
        end
        ax.xlimPassband = fdutil('xlimpassband',type,...
                             Fs,f(2),f(3),f(4),f(5));
    end
    if any(passbandChange==0)
        minstop = -Rs.value;
        % update L2 xdata and ydata
        switch type
        case 1   % lowpass
            set(L2,'xdata',[f(3:4)],'ydata',[minstop minstop])
        case 2   % highpass
            set(L2,'xdata',[f(1:2)],'ydata',[ minstop minstop])
        case 3   % bandpass
            set(L2,'xdata',[f(1:2) NaN f(5:6)],...
                   'ydata',[ minstop minstop NaN minstop minstop])
        case 4   % bandstop
            set(L2,'xdata',[f(3:4)],'ydata',[minstop minstop])
        end
    end
%     fdutil('updateRanges',fchange_ind,f1,f2,f3,f4)
    set(f1,'range',[0 Fs/2])
    set(f2,'range',[0 Fs/2])
    set(f3,'range',[0 Fs/2])
    set(f4,'range',[0 Fs/2])
    
function [f1,f2,f3,f4] = setupFrequencyObjects(type,module,pbobjects,...
                   sbobjects,f,Fs,ax,setValueFlag)

    if nargin<8
        setValueFlag = 1;
    end
    
    fdutil('changeToEdit',[pbobjects(1:2); sbobjects(1:2)])
    
    switch type
    case 1 % lowpass
        f1 = pbobjects(1);
        f2 = sbobjects(1);
        f3 = sbobjects(2);
        f4 = pbobjects(2);
        pbobjects(2).visible = 'off';
        sbobjects(2).visible = 'off';
        set(f1,'label','Fp')
        set(f2,'label','Fs')
    case 2 % highpass
        f1 = sbobjects(1);
        f2 = pbobjects(1);
        f3 = pbobjects(2);
        f4 = sbobjects(2);
        pbobjects(2).visible = 'off';
        sbobjects(2).visible = 'off';
        set(f1,'label','Fs')
        set(f2,'label','Fp')
    case 3 % bandpass
        f1 = sbobjects(1);
        f2 = pbobjects(1);
        f3 = pbobjects(2);
        f4 = sbobjects(2);
        pbobjects(2).visible = 'on';
        sbobjects(2).visible = 'on';
        set(f1,'label','Fs1')
        set(f2,'label','Fp1')
        set(f3,'label','Fp2')
        set(f4,'label','Fs2')
    case 4
        f1 = pbobjects(1);
        f2 = sbobjects(1);
        f3 = sbobjects(2);
        f4 = pbobjects(2);
        pbobjects(2).visible = 'on';
        sbobjects(2).visible = 'on';
        set(f1,'label','Fp1')
        set(f2,'label','Fs1')
        set(f3,'label','Fs2')
        set(f4,'label','Fp2')
    end
    if setValueFlag
        if type < 3
            set(f1,'value',f(2)*Fs/2,'range',[0 f(3)]*Fs/2)
            set(f2,'value',f(3)*Fs/2,'range',[f(2) 1]*Fs/2)
        else
            set(f1,'value',f(2)*Fs/2,'range',[0 f(3)*Fs/2])
            set(f2,'value',f(3)*Fs/2,'range',[f(2) f(4)]*Fs/2)
            set(f3,'value',f(4)*Fs/2,'range',[f(3) f(5)]*Fs/2)
            set(f4,'value',f(5)*Fs/2,'range',[f(4) 1]*Fs/2)
        end
        ax.xlimPassband = fdutil('xlimpassband',type,...
                             Fs,f1.value,f2.value,f3.value,f4.value);
    end

    f1.callback = [module '(''fchange'',1)'];
    f2.callback = [module '(''fchange'',2)'];
    f3.callback = [module '(''fchange'',3)'];
    f4.callback = [module '(''fchange'',4)'];

function [Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4] = ...
      setupMeasurementObjects(specStruc,Fs,order,order1,stopframe1,ax,...
                  pbmeasures,sbmeasures,Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4)
%  set values of MEASUREMENTS objects ... assumes specStruct is current

    setOrderFlag = specStruc.setOrderFlag;
    type = specStruc.type;
    f = specStruc.f;
    Rpass = specStruc.Rp;
    Rstop = specStruc.Rs;
    w3db = specStruc.w3db;
    N = specStruc.order;
    
    if ~setOrderFlag  % estimate order

        set(order1,'visible','on')

        stopframe1.visible = 'off';
        set(pbmeasures(2),'visible','off')
        set(pbmeasures(3),'visible','off')
        set(sbmeasures(3),'visible','off')
        
        Rpm = pbmeasures(1);
        set(Rpm,'visible','on','label','Actual Rp')
        w3db1m = sbmeasures(1);
        w3db2m = sbmeasures(2);
        set(w3db1m,'visible','on','value',w3db(1)*Fs/2,...
            'format','%1.4g')
        if type > 2
            set(w3db1m,'label','F3db 1')
            set(w3db2m,'visible','on','value',w3db(2)*Fs/2,...
                'format','%1.4g','label','F3db 2')
        else
            set(w3db1m,'label','F3db')
            set(w3db2m,'visible','off')
        end
        fdutil('changeToText',[Rpm w3db1m w3db2m])

        order.value = N;
        
    else
        set(order1,'visible','off')
    
        stopframe1.visible = 'on';
        
        pbmeasures(1).visible = 'on';
        sbmeasures(1).visible = 'on';

        [f1,f2,f3,f4] = setupFrequencyObjects(type,'fdbutter',pbmeasures,...
                                     sbmeasures,f,Fs,ax,0);
                                             
        Rp = pbmeasures(3);        
        Rs = sbmeasures(3);        
        set(Rp,'value',Rpass,'label','Rp',...
            'visible','on','callback','fdbutter(''Rpchange'')')
        set(Rs,'value',Rstop,'label','Rs',...
            'visible','on','callback','fdbutter(''Rschange'')')
    
    end
    for i=1:3
        set(pbmeasures(i),'enable','on')
        set(sbmeasures(i),'enable','on')
    end
    set(order1,'enable','on')


function [Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4,specStruc] = ...
      measureFilt(objSetupFlag,specStruc,Fs,order,order1,stopframe1,ax,...
                  pbmeasures,sbmeasures,Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4,...
                  ff,Hlog,L1,L2)

    if objSetupFlag
        [Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4] = ...
          setupMeasurementObjects(specStruc,Fs,order,order1,stopframe1,ax,...
                     pbmeasures,sbmeasures,Rp,Rs,w3db1m,w3db2m,Rpm,f1,f2,f3,f4);
    end

    n = specStruc.order;    
    f = specStruc.f;
    setOrderFlag = specStruc.setOrderFlag;
    w3db = specStruc.w3db;
    
    set(f1,'range',[0 Fs/2])
    set(f2,'range',[0 Fs/2])
    set(f3,'range',[0 Fs/2])
    set(f4,'range',[0 Fs/2])

    if ~setOrderFlag
        order.value = n;
        w3db1m.value = w3db(1) * Fs/2;
        if length(w3db)>1
            w3db2m.value = w3db(2) * Fs/2;
        end
        [maxpass,minpass,minstop] = getMagMeasurements(ff,Hlog,specStruc.type,...
                                          f1.value,f2.value,f3.value,f4.value);
        Rpm.value = maxpass - minpass;
    else
        [fm,Rs_val] = getFreqMeasurements(ff,Hlog,specStruc.type,Rp.value,Rs.value);
        Rs.value = Rs_val;
        f1.value = fm(1);  f2.value = fm(2);
        if length(fm)>2
            f3.value = fm(3);  f4.value = fm(4);
        end
%         fdutil('updateRanges',1:length(fm),f1,f2,f3,f4)    % update ranges

        f = [0 fm(:)' Fs/2 0];  % for updating L1 and L2 later;
                                % extra zero is for call to xlimpassband when
                                % type == 1 or 2
        ax.xlimPassband = fdutil('xlimpassband',specStruc.type,...
                             Fs,f(2),f(3),f(4),f(5));
        f = f*2/Fs;
        specStruc.f = f;    
    end
    maxpass = 0;   minpass = -Rp.value;   minstop = -Rs.value;
    ylim = [-Rp.value 0];
    dyl = (ylim(2)-ylim(1))*.15;
    ax.ylimPassband = ylim + [-dyl/2 dyl/2];
    
    % update L1 and L2 xdata and ydata, pointers, dragmodes
    fdutil('setLines','fdbutter',L1,L2,0,...
                      specStruc.type,f(:)',Fs,minpass,maxpass,minstop)


function f = getFrequencyValues(type,f1,f2,f3,f4,Fs);            
if type < 3  % low or high pass
    f = [0 f1.value f2.value Fs/2]*2/Fs;
else
    f = [0 f1.value f2.value f3.value f4.value Fs/2]*2/Fs;
end


function [setOrderFlag_init, type_init, f_init, Rp_init, Rs_init, ...
         order_init, w3db_init] = initSpecs(filt)
%initSpecs   Initial specifications for butterworth filter, from
%            filt input
%Switches off of filt.currentModule and if it finds any of
% fdcheby1, fdcheby2, fdellip, fdremez, fdfirls, or fdkaiser
% retains the type, order, band edge, and any other relevant
% parameters

    % first define default values
    setOrderFlag_init = 0;   % by default, estimate order 
    type_init = 1;  % 1=lowpass, 2=highpass, 3=bandpass, 4=bandstop
    f_init = [0 .1 .15 .5];
    Rp_init = 3;
    Rs_init = 20;
    order_init = 30;
    w3db_init = .22;
    if strcmp(filt.specs.currentModule,'fdpzedit')
        if isfield(filt.specs.fdpzedit,'oldModule')
            filt.specs.currentModule = filt.specs.fdpzedit.oldModule;
        end
    end
    switch filt.specs.currentModule
    case {'fdcheby1','fdcheby2','fdellip','fdremez','fdkaiser','fdfirls'}
        s = eval(['filt.specs.' filt.specs.currentModule]);
        setOrderFlag_init = s.setOrderFlag;
        type_init = s.type;
        f_init = s.f;
        Rp_init = s.Rp;
        Rs_init = s.Rs;
        order_init = s.order;
        switch filt.specs.currentModule
        case 'fdcheby1'
            w3db_init = s.Fpass;
        case 'fdcheby2'
            w3db_init = s.Fstop;
        case 'fdellip'
            w3db_init = s.Fpass;
        case {'fdremez','fdfirls'}
            switch s.type
            case {1,2}
                w3db_init = s.f(2);
            case 3
                w3db_init = s.f(3:4);
            case 4
                w3db_init = s.f([2 5]);
            end            
        case 'fdkaiser'
            w3db_init = s.Wn;
        end
        if any(strcmp(filt.specs.currentModule,...
                 {'fdremez','fdkaiser','fdfirls'}))
            order_init = ceil(order_init/10); 
             %  FIR filters are much higher order than IIR
        end

    case 'fdbutter'
        if isfield(filt.specs,'fdbutter')
            setOrderFlag_init = filt.specs.fdbutter.setOrderFlag;
            type_init = filt.specs.fdbutter.type;
            f_init = filt.specs.fdbutter.f;
            Rp_init = filt.specs.fdbutter.Rp;
            Rs_init = filt.specs.fdbutter.Rs;
            order_init = filt.specs.fdbutter.order;
            w3db_init = filt.specs.fdbutter.w3db;
        end
    end
