#include #include #include #include #include #include #include #include #include /* Calculation of impedance matching networks and other design quantities from s-parameters S-parameter formulas cf. Agilent application note "S-Parameter techniques for faster, more accurate network design" Matching method based on Chapters 4 and 6 of Bowick's RF circuit design book Outputs several possibilities separated by semicolons for input and output matching to 50 ohms at each frequency Selection of matching network topology and possibly Q-value at the command line (see code for details) Supports L, T, PI, and LL matching network shapes. Network components are printed in the ordering from nearest to the transistor towards the external impedance Input consists of lines with the structure FREQ(GHz) S11Magnitude S11Angle(degrees) S21M S21A S12M S12A S22M S22A Copyright (C) 2017 Jiri Pittner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using namespace std; //define standard impedance const double Z0=50.; //signs + for serial inductor and parallel capacitor int matchreal(double rs, double rp, int signs, double &xs, double &xp, double &q) { if(rs>rp) return 1; q=sqrt(rp/rs-1.); xs = rs*q; xp = rp/q; if(signs<0) xs= -xs; else xp= -xp; return 0; } //match two complex impedances, fixed which serially and which parallelly connected LC //input are the existing impedances as is, not their complex conjugates int matchcomplex(complex zs, complex zp, int signs, double &xs, double &xp, double &q) { complex zpinv= 1./zp; complex zpequiv= complex(1./zpinv.real(),-1./zpinv.imag()); //equivalent parallel connection of real and imag. impedance double xsshouldbe,xpshouldbe; int r=matchreal(zs.real(),zpequiv.real(),signs,xsshouldbe,xpshouldbe,q); if(r) return r; //impossible to match real parts of impedance //try absorbing or resonating the additional reactances (formally it is the same if we allow general X sign) //if we allow also sign flip of X, C-C and L-L networks will be generated if needed xs=xsshouldbe - zs.imag(); //serial one xp = 1./(1./xpshouldbe + zpinv.imag()); //parallel one, avoid 1/(1/0) return 0; } //reactance to value inline double x2v(double x, double f) { return x>0. ? x/(2*M_PI*f) : 1./(2*M_PI*f*x); } //value to reactance (negative value = capacitor) inline double v2x(double v, double f) { return v>0. ? 2*M_PI*f*v : 1./(2*M_PI*f*v); } //negative value means capacitor, positive inductor int findLmatch(complex zs, complex zp, int signs, double freq, double &values, double &valuep, double &q) { double xs,xp; int r=matchcomplex(zs,zp,signs,xs,xp,q); if(r) return r; values = x2v(xs,freq); valuep = x2v(xp,freq); return 0; } int findLLmatch(complex zs,complex zp,int signs1,int signs2, double freq,double &values1,double &valuep1,double &values2,double &valuep2, double &q) { complex zpinv= 1./zp; complex zpequiv= complex(1./zpinv.real(),-1./zpinv.imag()); //equivalent parallel connection of real and imag. impedance double rvirtual=sqrt(zs.real()*zpequiv.real()); double xs1,xp1,xs2,xp2,q1,q2; int r1,r2; r1=matchcomplex(zs,rvirtual,signs1,xs1,xp1,q1); r2=matchcomplex(rvirtual,zp,signs2,xs2,xp2,q2); if(r1||r2) return r1+r2; q=sqrt(q1*q2); values1 = x2v(xs1,freq); valuep1 = x2v(xp1,freq); values2 = x2v(xs2,freq); valuep2 = x2v(xp2,freq); return 0; } int findPImatch(complex zp1,int signs1,complex zp2,int signs2,double freq,double qtarget,double &valuep1,double &values,double &valuep2) { complex zp1inv= 1./zp1; complex zp1equiv= complex(1./zp1inv.real(),-1./zp1inv.imag()); //equivalent parallel connection of real and imag. impedance complex zp2inv= 1./zp2; complex zp2equiv= complex(1./zp2inv.real(),-1./zp2inv.imag()); //equivalent parallel connection of real and imag. impedance double rmax=zp1equiv.real(); if(zp2equiv.real()>rmax) rmax=zp2equiv.real(); double rvirtual = rmax/(qtarget*qtarget+1.); double q1,xs1,xp1,q2,xs2,xp2; int r1= matchcomplex(rvirtual,zp1,signs1,xs1,xp1,q1); int r2= matchcomplex(rvirtual,zp2,signs2,xs2,xp2,q2); if(r1||r2) return r1+r2; //assert max(q1,q2)=qtarget double xs=xs1+xs2; values = x2v(xs,freq); valuep1 = x2v(xp1,freq); valuep2 = x2v(xp2,freq); return 0; } int findTmatch(complex zs1,int signs1,complex zs2,int signs2,double freq,double qtarget,double &values1,double &valuep,double &values2) { double rmin=zs1.real(); if(zs2.real()(0.,xs1)+1./(1./complex(0.,xp)+1./(complex(0.,xs2)+zs2))<(0.,xs2)+1./(1./complex(0.,xp)+1./(complex(0.,xs1)+zs1))< vs1 = 1.-i*zs1; complex vt = vs1-i*complex(0.,xs1); complex vs2 = vt/(zs2+complex(0.,xs2))*zs2; complex transfer = vs2/vs1; cout <<"TEST "< z, complexztarget, double freq) { int r; double values,valuep,q; r=findLmatch(z,ztarget,1,freq,values,valuep,q); if(!r) cout << " serial "< z, complexztarget, double freq) { int r; double valuep1,valuep2,values1,values2,q; r=findLLmatch(z,ztarget,1,1,freq,values1,valuep1,values2,valuep2,q); if(!r) cout << " serial "< z, complexztarget, double freq, double qtarget) { int r; double values,valuep1,valuep2; r=findPImatch(z,1,ztarget,1,freq,qtarget,valuep1,values,valuep2); if(!r) cout << " parallel "< z, complexztarget, double freq, double qtarget) { int r; double valuep,values1,values2; r=findTmatch(z,1,ztarget,1,freq,qtarget,values1,valuep,values2); if(!r) cout << " serial "< GfromZ(const complex Z) {return (Z-Z0)/(Z+Z0);} inline complex ZfromG(const complex G) {return Z0*(1.+G)/(1.-G);} int main(int argc, char **argv) { //read options double qtarget; typedef enum {L_match=0,T_match=1,PI_match=2,LL_match=3} MATCHTYPE; MATCHTYPE matchtype=L_match; double Pdesired=10.; bool constgain=false; bool unilateral_match = false; if(argc>=3 && !strcmp(argv[1],"-T")) { argv+=2; argc-=2; sscanf(*argv,"%lf",&qtarget); matchtype=T_match; } if(argc>=3 && !strcmp(argv[1],"-P")) { argv+=2; argc-=2; sscanf(*argv,"%lf",&qtarget); matchtype=PI_match; } if(argc>=2 && !strcmp(argv[1],"-L")) { argv+=1; argc-=1; matchtype=LL_match; } if(argc>=2 && !strcmp(argv[1],"-u")) { argv+=1; argc-=1; unilateral_match = true; } if(argc>=3 && !strcmp(argv[1],"-g")) { argv+=2; argc-=2; sscanf(*argv,"%lf",&Pdesired); constgain=true; } cc:cout.setf(ios::fixed); cout.precision(10); cin.exceptions ( ifstream::eofbit | ifstream::failbit | ifstream::badbit ); double f,sm[3][3],sa[3][3]; complex s[3][3]; while(!cin.eof()) //loop over all frequencies from the input { try { cin>>f; f*=1e9; for(int i=1;i<=2;++i) for(int j=1; j<=2;++j) { cin >>sm[j][i] >>sa[j][i]; s[j][i]= complex (sm[j][i] * cos(M_PI*sa[j][i]/180.), sm[j][i] * sin(M_PI*sa[j][i]/180.)); } } catch(std::ios_base::failure e) {exit(0);}//end of file or non-numeric text in the file //impedance from s-parameters (input and output assumed isolated, unilateral) complex Z[3]; for(int i=1;i<=2;++i) { Z[i] = ZfromG(s[i][i]); } //compute some aux quantitites double umerit = abs(s[1][1]*s[1][2]*s[2][1]*s[2][2])/abs((1.-sm[1][1]*sm[1][1])*(1.-sm[2][2]*sm[2][2])); complex D = s[1][1]*s[2][2]-s[1][2]*s[2][1]; complex M = s[1][1]-D*conj(s[2][2]); complex N = s[2][2]-D*conj(s[1][1]); complex C1 = M; //other notation complex C2 = N; //other notation double K = (1.+norm(D)-norm(s[1][1])-norm(s[2][2]))/(2.*abs(s[1][2]*s[2][1])); double Clinvill = 1./K; //stability conditions bool cond1 = sm[1][1]<1. && sm[2][2]<1.; bool cond2 = abs((abs(s[1][2]*s[2][1])-abs(M))/(norm(s[1][1])-norm(D)))>1.; bool cond3 = abs((abs(s[1][2]*s[2][1])-abs(N))/(norm(s[2][2])-norm(D)))>1.; bool cond4 = K > 1.; bool isstable = cond1&&cond2&&cond3&&cond4; //conjugate matching (with S12 != 0) double B1 = 1.+norm(s[1][1])-norm(s[2][2])-norm(D); double B2 = 1.-norm(s[1][1])+norm(s[2][2])-norm(D); complex Gsm = (B1+(B1>0.?-1.:1.)*sqrt(B1*B1-4.*norm(M)))/(2.*M); complex Glm = (B2+(B2>0.?-1.:1.)*sqrt(B2*B2-4.*norm(N)))/(2.*N); //these are impedances which the transistor should "see", i.e. complex conjugates of its impedances //therefore their conjugates must be input to the matching subroutines complex Zsm = ZfromG(Gsm); complex Zlm = ZfromG(Glm); //Max available power gain double Pavailmax = abs(s[2][1]/s[1][2]*(K+(B1>0.?-1.:1.)*sqrt(K*K-1.))); //undefined for K<1 //compute other types of transistor parameters complex z[3][3],y[3][3],h[3][3]; complex tmp=(1.-s[1][1])*(1.-s[2][2])-s[1][2]*s[2][1]; z[1][1] = ((1.+s[1][1])*(1.-s[2][2])+s[1][2]*s[2][1])/tmp; z[1][2] = 2.*s[1][2]/tmp; z[2][1] = 2.*s[2][1]/tmp; z[2][2] = ((1.+s[2][2])*(1.-s[1][1])+s[1][2]*s[2][1])/tmp; z[1][1] *= Z0; z[1][2] *= Z0; z[2][1] *= Z0; z[2][2] *= Z0; tmp=(1.+s[1][1])*(1.+s[2][2])-s[1][2]*s[2][1]; y[1][1] = ((1.+s[2][2])*(1.-s[1][1])+s[1][2]*s[2][1])/tmp; y[1][2] = -2.*s[1][2]/tmp; y[2][1] = -2.*s[2][1]/tmp; y[2][2] = ((1.+s[1][1])*(1.-s[2][2])+s[1][2]*s[2][1])/tmp; y[1][1] /= Z0; y[1][2] /= Z0; y[2][1] /= Z0; y[2][2] /= Z0; tmp=(1.-s[1][1])*(1.+s[2][2])+s[1][2]*s[2][1]; h[1][1] = ((1.+s[1][1])*(1.+s[2][2])-s[1][2]*s[2][1])/tmp; h[1][2] = 2.*s[1][2]/tmp; h[2][1] = -2.*s[2][1]/tmp; h[2][2] = ((1.-s[1][1])*(1.-s[2][2])-s[1][2]*s[2][1])/tmp; h[1][1] *= Z0; h[2][2] /= Z0; //properties under general unmatched conditions #if 0 //input reflection for unmatched load Zl complex Zl = 30.; //CHANGE HERE //output reflection for unmatched source Zs complex Zs = 30.; //CHANGE HERE #else complex Zl = Zlm; complex Zs = Zsm; #endif complex Gl = GfromZ(Zl); complex s11prime = s[1][1] + (s[1][2]*s[2][1]*Gl)/(1.-s[2][2]*Gl); complex Gs = GfromZ(Zs); complex s22prime = s[2][2] + (s[1][2]*s[2][1]*Gs)/(1.-s[1][1]*Gs); //voltage gain complex Vgain = (s[2][1]*(1.+Gl))/((1.-s[2][2]*Gl)*(1.+s11prime)); //power gain double Pgain = norm(s[2][1])*(1.-norm(Gl))/((1.-norm(s[1][1]))+norm(Gl)*(norm(s[2][2])-norm(D))-2.*(Gl*N).real()); //available power gain double Pavail = norm(s[2][1])*(1.-norm(Gs))/((1.-norm(s[2][2]))+norm(Gs)*(norm(s[1][1])-norm(D))-2.*(Gs*M).real()); //transducer power gain double Ptrans = norm(s[2][1])*(1.-norm(Gs))*(1.-norm(Gl))/norm((1.-s[1][1]*Gs)*(1.-s[2][2]*Gl)-s[1][2]*s[2][1]*Gl*Gs); //constant gain circles in the Smith chart (for broadband design) double g = Pdesired/norm(s[2][1]); double D2 = norm(s[2][2])-norm(D); complex gain_center = g*conj(C2)/(1.+g*D2); double gain_radius = sqrt(1.-2.*K*g*abs(s[1][2]*s[2][1])+norm(g*s[1][2]*s[2][1]))/(1.+g*D2); //input stability circle complex center1 = conj(C1)/(norm(s[1][1])-norm(D)); double radius1 = abs(s[1][2]*s[2][1]/(norm(s[1][1])-norm(D))); //output stability circle complex center2 = conj(C2)/(norm(s[2][2])-norm(D)); double radius2 = abs(s[1][2]*s[2][1]/(norm(s[2][2])-norm(D))); //outputs cout.precision(5); cout.setf(ios::fixed); cout < fifty(Z0,0.); //calculate matching to Z0 complex Zsmatch,Zlmatch; if(unilateral_match || !isstable) { Zsmatch = Z[1]; Zlmatch = Z[2]; } else { Zsmatch = conj(Zsm); Zlmatch = conj(Zlm); } cout <