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);