Code covered by the BSD License  

Highlights from
Analog Filter Design Toolbox

image thumbnail

Analog Filter Design Toolbox

by

 

27 Dec 2005 (Updated )

GUI to design and simulate active (opamp) LP and HP Bessel, Butter, Cheby, and Elliptic filters.

BuildCircuit_Zpk2Circuit(strFilterObject)
function strCircuit = BuildCircuit_Zpk2Circuit(strFilterObject)
% Zpk2Circuit is a subfile of the AnalogFilter GUI collection
%
% James C. Squire, 2002
% Assistant Professor, Virginia Military Institute
% ver 1.0

% Zpk2Circuit converts the zero and pole vectors and k scalar into 
% a set of (zero or more) biquads followed by (zero or one) final stage.
% The final stage implements a first or zero order stage.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                              Create Structure                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
z = cplxpair(strFilterObject.vZeros);
p = cplxpair(strFilterObject.vPoles);
k = strFilterObject.fK;
nStages = ceil(length(p)/2);
nBiquads = floor(length(p)/2);
bFirstOrderStage = nStages - nBiquads;
bGainStage = 0;
sPolarity = 'd';  % don't care
nRTol = 0; % exact
nCTol = 0;
if isequal(strFilterObject.sType,'Elliptic') || isequal(strFilterObject.sType,'Chebychev II')
    sPurpose = 'Notch';
elseif isequal(strFilterObject.sPurpose,'Lowpass')
    sPurpose = 'LP';
elseif isequal(strFilterObject.sPurpose,'Highpass')
    sPurpose = 'HP';
else
    error(['Problem with filter object: not a normal definition in ' mfilename]);
end
tStage.z = [];
tStage.p = [];
tStage.k = 1;
tStage.Q = [];
tStage.wp = [];
tStage.wz = [];
tStage.z1 = [];
tStage.p1 = [];
tStage.k1 = 1;
tStage.Q1 = [];
tStage.wp1 = [];
tStage.wz1 = [];
tStage.schName = '';
tStage.recName = '';
tStage.schTitle = '';
tStage.recTitle = '';
tStage.vfCSelect = [];    % vector of capacitors that the user chooses (NaN means does not exist)
tStage.vnCSelectExp = []; % vector of cap value exponents.  May be -3, -6, -9, or -12
tStage.csCSelectMan = {}; % cell array of the mantissa string.  E.g. 0.004 has a mantissa of -3 and Exp of 4
tStage.vfRSelect = [];    % vector of resistors that the user chooses
tStage.vnRSelectExp = []; % vector of resistor value exponents.  Either 0,3, or 6 for ohms, k, or M
tStage.csRSelectMan = {}; % cell array of the mantissa string.  E.g. 6040 has a mantissa of 6.04 and Exp of 3
tStage.vfCCalc = [];
tStage.csCCalc = {};
tStage.vfRCalc = [];   % vector of resistors that the program calculates
tStage.csRCalc = {};   % cell array of strings describing the computed values in engineering form
for i=1:5 % can't have more than 5 stages for a 8th order filter plus a gain
    vStage(i) = tStage;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                     Pair poles with zeros into biquads                    %
%                  by pairing similar-sized absolute values                 %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% sort complex pairs of poles into stages by order of increasing wp
for i=1:nBiquads
    tempP(i).p = p([-1 0]+(i*2));
    tp = tempP(i).p(1);
    tempP(i).wp = abs(tp);
    tempP(i).Q = abs(tp)/(-2*real(tp));
    tempToSortWp(i) = tempP(i).wp;
end
if nBiquads
    [dummy,index]=sort(tempToSortWp);
end
for i=1:nBiquads
    vStage(i).p = tempP(index(i)).p;
    vStage(i).Q = tempP(index(i)).Q;
    vStage(i).wp =tempP(index(i)).wp;
end
% sort complex pairs of zeros into stages by order of increasing wz
if length(z)==0  % if a LP filter
    for i=1:nBiquads
        vStage(i).z = [];
        vStage(i).wz = [];
    end
else
    for i=1:nBiquads
        tempZ(i).z = z([-1 0]+(i*2));
        tz = tempZ(i).z(1);
        tempZ(i).wz = abs(tz);
        tempToSortWz(i) = tempZ(i).wz;
    end
    if nBiquads
        [dummy,index]=sort(tempToSortWz);
    end
    for i=1:nBiquads
        vStage(i).z = tempZ(index(i)).z;
        vStage(i).wz =tempZ(index(i)).wz;
    end
end
% fill up the single order stage, if any
if bFirstOrderStage
    vStage(nStages).p = p(end);
    vStage(nStages).Q = 0;
    vStage(nStages).wp = abs(p(end));
    if length(z)==0 || length(z)<length(p)
        vStage(nStages).z = [];
        vStage(nStages).wz = [];
    else
        if vStage(nStages).z~=0, error('unexpected zero value'), end
        vStage(nStages).z = 0;
        vStage(nStages).wz = 0;        
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                        Order biquads by increasing Q                      %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if nBiquads
    for i=1:nBiquads
        Q(i) = vStage(i).Q;
    end
    [dummy,index]=sort(Q);
    for i=1:nBiquads
        vNewStage(i) = vStage(index(i));
    end
    vStage(1:nBiquads) = vNewStage;
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                            Assign k's to each stage                       %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spread the passband gain out evenly so each stage gets kAdditional in its passband.
% Note that each lowpass stage already has a built-in passband gain of 1/abs(p)^2 
% gain in its passband, so kAdditional is above and beyond this value.
kPassband = abs(k);
if isequal(sPurpose,'LP')
    for i=1:nBiquads
        kPassband = 1/abs(vStage(i).p(1))^2 * kPassband;
    end
    if bFirstOrderStage
        kPassband = 1/abs(vStage(nStages).p) * kPassband;
    end
end
kAdditional = kPassband^(1/nStages);
for i=1:nBiquads
    if isequal(sPurpose,'LP')
        vStage(i).k = kAdditional * abs((vStage(i).p(1)))^2;
    else
        vStage(i).k = kAdditional;
    end
end % updated May 03 so each stage has kAdditional extra passband gain
if bFirstOrderStage
    if isequal(sPurpose,'LP')
        vStage(nStages).k = kAdditional * abs(vStage(nStages).p);
    else
        vStage(nStages).k = kAdditional;
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                      Recommend circuit for each biquad                    %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
switch sPurpose
    case 'LP'
        for i=1:nBiquads
            if vStage(i).Q < 3
                vStage(i).schTitle = 'Sallen-Key';
                % kDC = k / pp* for LP or k otherwise - important because SK uses a different 
                % configuration for kDC <=1 and >1 
                kDC = abs(vStage(i).k/(abs(vStage(i).p(1))^2));
                if abs(kDC-1) < 0.00001 % make it exact
                    kDC = 1;
                end
                if kDC <= 1
                    vStage(i).schName = 'SK_LP_kLE1';
                else
                    vStage(i).schName = 'SK_LP_kGT1';
                end
            else
                vStage(i).schTitle = 'Ackerberg-Mossberg';
                vStage(i).schName = 'AM_LP_N';
            end
        end
    case 'HP'
        for i=1:nBiquads
            if vStage(i).Q < 3
                vStage(i).schTitle = 'Sallen-Key';
                if abs(vStage(i).k-1) < 0.00001 % make it exact
                    vStage(i).k = 1;
                end
                if abs(vStage(i).k) <= 1
                    vStage(i).schName = 'SK_HP_kLE1';
                else
                    vStage(i).schName = 'SK_HP_kGT1';
                end
            else
                vStage(i).schTitle = 'Ackerberg-Mossberg';
                vStage(i).schName = 'AM_HP';
            end
        end
    case 'Notch'
        for i=1:nBiquads
            if vStage(i).Q < 3
                if abs(vStage(i).p) < abs(vStage(i).z) % LP
                    vStage(i).schTitle = 'Multiple Feedback';
                    vStage(i).schName = 'MFB_Z_LP';
                else % HP
                    vStage(i).schTitle = 'Multiple Feedback';
                    vStage(i).schName = 'MFB_Z_HP';
                end
            else
                vStage(i).schTitle = 'Ackerberg-Mossberg';
                vStage(i).schName = 'AM_Z';
            end
        end
    otherwise
        error('Unrecognized sPurpose')
end
% make the recommended name the same as the current schematic name
for i=1:nStages
    vStage(i).recName = vStage(i).schName;
    vStage(i).recTitle = vStage(i).schTitle;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                fill up the strCircuit structure with results              %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
strCircuit.z = z;
strCircuit.p = p;
strCircuit.k = k;
strCircuit.nStages = nStages;
strCircuit.nBiquads = nBiquads;
strCircuit.bFirstOrderStage = bFirstOrderStage;
strCircuit.bGainStage = bGainStage;
strCircuit.sPolarity = sPolarity;
strCircuit.nRTol = nRTol;
strCircuit.nCTol = nCTol;
strCircuit.sPurpose = sPurpose;
strCircuit.vStage = vStage;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                fill up each biquad with default components                %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% do biquads 1st because some biquads have a k1 that influence gain stage
for i=1:nBiquads
    strCircuit.vStage(i) = BuildCircuit_UpdateComponents(strCircuit.vStage(i),nRTol,nCTol);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%          create the last stage (zero or first order), if any              %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% assumption at this point: never a final gain stage.  LastStage will add one if needed
% BuildCircuit_LastStage calls BuildCircuit_UpdateComponents
strCircuit = BuildCircuit_LastStage(strCircuit);

Contact us