2.3 MiB
2.3 MiB
In [39]:
import numpy as np import skrf from skrf.media import DistributedCircuit import skrf.frequency as freq import skrf.network as net import skrf.util import matplotlib.pyplot as plt %matplotlib inline plt.rcParams['figure.figsize'] = [10, 10] f = freq.Frequency(0.4, 2, 1001) tem = DistributedCircuit(f, z0=50) bjt = net.Network('BFU520_Spar_NF_400MHz-2GHz/BFU520_05V0_005mA_NF_SP.s2p').interpolate(f) bjt
Out[39]:
2-Port Network: 'BFU520_05V0_005mA_NF_SP', 0.4-2.0 GHz, 1001 pts, z0=[ 50.+0.j 50.+0.j]
In [40]:
bjt.plot_s_smith()
In [41]:
# let's add an inductor to the emitter # to do this we need a function that lets you combine two port networks in series # so we can model it from scipy.interpolate import interp1d def series(a, b): newz = a.z + b.z ret = net.Network.from_z(newz, frequency=a.frequency) noiseless = not a.noisy and not b.noisy if noiseless: return ret if a.noisy: an = a.n af = a.f_noise else: an = np.zeros(b.n.shape) af = b.f_noise if b.noisy: bn = b.n bf = b.f_noise else: bn = np.zeros(a.n.shape) bf = a.f_noise if bf != af: # TODO interpolate noise values for b into a frequency ranges # bn = ??? raise NotImplementedError a_real = interp1d(a.frequency.f, a.a.real, axis=0, kind=net.Network.noise_interp_kind) a_imag = interp1d(a.frequency.f, a.a.imag, axis=0, kind=net.Network.noise_interp_kind) a_abcd = a_real(af.f) + 1.j * a_imag(af.f) b_real = interp1d(b.frequency.f, b.a.real, axis=0, kind=net.Network.noise_interp_kind) b_imag = interp1d(b.frequency.f, b.a.imag, axis=0, kind=net.Network.noise_interp_kind) b_abcd = b_real(bf.f) + 1.j * b_imag(bf.f) # calculate noise # based on https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781119073093.app3 a1 = 1 b1 = (b_abcd[:,0,0] - a_abcd[:,0,0])/(a_abcd[:,1,0] + b_abcd[:,1,0]) c1 = 0 d1 = b_abcd[:,1,0]/(a_abcd[:,1,0] + b_abcd[:,1,0]) a2 = 1 b2 = (a_abcd[:,0,0] - b_abcd[:,0,0])/(a_abcd[:,1,0] + b_abcd[:,1,0]) c2 = 0 d2 = a_abcd[:,1,0]/(a_abcd[:,1,0] + b_abcd[:,1,0]) ee = (a1*np.conj(a1)*an[:,0,0] + b1*np.conj(b1)*bn[:,1,1] + a2*np.conj(a2)*bn[:,1,1] + b2*np.conj(b2)*bn[:,1,1] + a1*np.conj(b1)*an[:,0,1] + b1*np.conj(a1)*an[:,1,0] + a2*np.conj(b2)*bn[:,0,1] + b2*np.conj(a2)*bn[:,1,0]) ii = (c1*np.conj(c1)*an[:,0,0] + d1*np.conj(d1)*an[:,1,1] + c2*np.conj(c2)*bn[:,0,0] + d2*np.conj(d2)*bn[:,1,1] + c1*np.conj(d1)*an[:,0,1] + d1*np.conj(c1)*an[:,1,0] + c2*np.conj(d2)*bn[:,0,1] + d2*np.conj(c2)*bn[:,1,0]) ei = (a1*np.conj(c1)*an[:,0,0] + b1*np.conj(d1)*an[:,1,1] + a2*np.conj(c2)*bn[:,0,0] + b2*np.conj(d2)*bn[:,1,1] + a1*np.conj(d1)*an[:,0,1] + b1*np.conj(c1)*an[:,1,0] + a2*np.conj(d2)*bn[:,0,1] + b2*np.conj(c2)*bn[:,1,0]) ie = np.conj(ei) ret.noise = np.moveaxis(np.array([[ee, ei], [ie, ii]]), 2, 0) ret.noise_freq = af return ret
In [42]:
bjt_degen = series(bjt, tem.shunt_inductor(1e-9)) bjt_degen.plot_s_smith() idx_900mhz = skrf.util.find_nearest_index(bjt.f, 915.e+6) bjt_degen.z_opt[idx_900mhz]
Out[42]:
(58.285348012831371+12.185071598304109j)
In [43]:
# calculate the stability circles for the source and load impedances idx_900mhz = skrf.util.find_nearest_index(bjt_degen.f, 915.e+6) sqabs = lambda x: np.square(np.absolute(x)) delta = bjt_degen.s11.s*bjt_degen.s22.s - bjt_degen.s12.s*bjt_degen.s21.s rl = np.absolute((bjt_degen.s12.s * bjt_degen.s21.s)/(sqabs(bjt_degen.s22.s) - sqabs(delta))) cl = np.conj(bjt_degen.s22.s - delta*np.conj(bjt_degen.s11.s))/(sqabs(bjt_degen.s22.s) - sqabs(delta)) rl_900mhz = rl[idx_900mhz][0, 0] cl_900mhz = cl[idx_900mhz][0, 0] rl_900mhz, cl_900mhz
Out[43]:
(3.048555602935056, (3.001540263823371+2.6459699316881777j))
In [44]:
def calc_circle(c, r): theta = np.linspace(0, 2*np.pi, 1000) return c + r*np.exp(1.0j*theta) def plot_smith(pts): n = net.Network(s=pts) n.plot_s_smith() cl_points = calc_circle(cl_900mhz, rl_900mhz) plot_smith(cl_points)
In [45]:
rs = np.absolute((bjt_degen.s12.s * bjt_degen.s21.s)/(sqabs(bjt_degen.s11.s) - sqabs(delta))) cs = np.conj(bjt_degen.s11.s - delta*np.conj(bjt_degen.s22.s))/(sqabs(bjt_degen.s11.s) - sqabs(delta)) rs_900mhz = rs[idx_900mhz][0, 0] cs_900mhz = cs[idx_900mhz][0, 0] rs_900mhz, cs_900mhz
Out[45]:
(3.267143687859595, (1.6731769925739743-1.6577383735222198j))
In [46]:
cs_points = calc_circle(cs_900mhz, rs_900mhz) plot_smith(cs_points)
In [47]:
# let's plot all of them # output stability first for i, f in enumerate(bjt.f): # decimate it a little if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cl[i][0, 0], rl[i][0, 0])) n.plot_s_smith()
In [48]:
# input stability for i, f in enumerate(bjt.f): if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cs[i][0, 0], rs[i][0, 0])) n.plot_s_smith()
In [49]:
# let's draw some constant noise circles # first we grab the noise parameters for our target frequency from the network model idx_915mhz = skrf.util.find_nearest_index(bjt_degen.f, 915.e+6) # we need the normalized equivalent noise and optimum source coefficient to calculate the constant noise circles rn = bjt_degen.rn[idx_915mhz]/50 gamma_opt = bjt_degen.g_opt[idx_915mhz] fmin = bjt_degen.nfmin[idx_915mhz] for nf_added in [0, 0.05, 0.1, 0.2, 0.3]: nf = 10**(nf_added/10) * fmin N = (nf - fmin)*abs(1+gamma_opt)**2/(4*rn) c_n = gamma_opt/(1+N) r_n = 1/(1-N)*np.sqrt(N**2 + N*(1-abs(gamma_opt)**2)) n = net.Network(name=str(nf_added), s=calc_circle(c_n, r_n)) n.plot_s_smith() print("the optimum source reflection coefficient is ", gamma_opt)
the optimum source reflection coefficient is (0.0880613782526+0.102618106726j)
In [50]:
gamma_s = bjt_degen.g_opt[idx_900mhz] gamma_s
Out[50]:
(0.088061378252587419+0.10261810672607581j)
In [51]:
# so I need to calculate the load reflection coefficient to get a conjugate match when the input sees 50 ohms gamma_l = np.conj(bjt_degen.s22.s - bjt_degen.s21.s*gamma_s*bjt_degen.s12.s/(1-bjt_degen.s11.s*gamma_s)) is_gamma_l_stable = np.absolute(gamma_l[idx_900mhz, 0, 0] - cl_900mhz) > rl_900mhz gamma_l = gamma_l[idx_900mhz, 0, 0] gamma_l, is_gamma_l_stable
Out[51]:
((0.53933106007665532+0.26530128140907783j), True)
In [52]:
def calc_matching_network_vals(z1, z2): flipped = ((abs(np.imag(z2)) < 1e-6 and np.real(z1) < np.real(z2)) or (abs(np.imag(z2)) > 1e-6 and np.real(z1) < np.real(1/(1/z2-1/(1.j*np.imag(z2)))))) if flipped: z2, z1 = z1, z2 # cancel out the imaginary parts of both input and output impedances z1_par = 1e+10 if abs(np.imag(z1)) > 1e-6: # parallel something to cancel out the imaginary part of # z1's impedance z1_par = 1/(-1j*np.imag(1/z1)) z1 = 1/(1./z1 + 1/z1_par) z2_ser = 0.0 if abs(np.imag(z2)) > 1e-6: z2_ser = -1j*np.imag(z2) z2 = z2 + z2_ser Q = np.sqrt((np.real(z1) - np.real(z2))/np.real(z2)) x1 = -1.j * np.real(z1)/Q x2 = 1.j * np.real(z2)*Q x1_tot = 1/(1/z1_par + 1/x1) x2_tot = z2_ser + x2 if flipped: return x2_tot, x1_tot else: return x1_tot, x2_tot z_l = net.s2z(np.array([[[gamma_l]]]))[0,0,0] # note that we're matching against the conjugate; # this is because we want to see z_l from the BJT side # if we plugged in z the matching network would make # the 50 ohms look like np.conj(z) to match against it, so # we use np.conj(z_l) so that it'll look like z_l from the BJT's side z_par, z_ser = calc_matching_network_vals(np.conj(z_l), 50) z_l, z_par, z_ser
Out[52]:
((113.01057791499088+93.878513301282439j), -225.08350870216864j, 83.96308091204551j)
In [53]:
# let's calculate what the component values are c_par = np.real(1/(2j*np.pi*915e+6*z_par)) l_ser = np.real(z_ser/(2j*np.pi*915e+6)) c_par, l_ser
Out[53]:
(7.727790877202402e-13, 1.4604523895493778e-08)
In [54]:
# the capacitance is kind of low but the inductance seems reasonable # let's test it out output_network = tem.shunt_capacitor(c_par) ** tem.inductor(l_ser) amplifier = tem.inductor(0.9e-9) ** bjt_degen ** output_network amplifier.plot_s_smith()
In [55]:
amplifier.s11.plot_s_db() amplifier.s22.plot_s_db() 10*np.log10(amplifier.nf(50.)[idx_900mhz])
Out[55]:
0.7680949467294812
In [56]:
amplifier.s21.plot_s_db() 20*np.log10(np.abs(amplifier.s21.s[idx_900mhz,0,0]))
Out[56]:
16.199434396586287
In [93]:
# that's pretty good but let's try to optimize the inductor value so we can get conjugate matching # at the same time as minimum noise figure nf_min = 10e+9 lval = 0.0 gamma_s_opt = None gamma_l_opt = None best_zs_opt = None for l in np.linspace(0.1e-9, 5e-9, 50): bjt_degen = series(bjt, tem.shunt_inductor(l)) ** tem.shunt(tem.resistor(100.) ** tem.short()) #bjt_degen.plot_s_smith() idx_900mhz = skrf.util.find_nearest_index(bjt.f, 915.e+6) zs_opt = bjt_degen.z_opt[idx_900mhz] bjt_s11 = bjt_degen.s11.s[idx_900mhz,0,0] bjt_s12 = bjt_degen.s12.s[idx_900mhz,0,0] bjt_s21 = bjt_degen.s21.s[idx_900mhz,0,0] bjt_s22 = bjt_degen.s22.s[idx_900mhz,0,0] delta2 = bjt_s11*bjt_s22 - bjt_s12*bjt_s21 B1 = 1 + sqabs(bjt_s11) - sqabs(bjt_s22) - sqabs(delta2) B2 = 1 + sqabs(bjt_s22) - sqabs(bjt_s11) - sqabs(delta2) C1 = bjt_s11 - delta2*np.conj(bjt_s22) C2 = bjt_s22 - delta2*np.conj(bjt_s11) gamma_s = (B1 - np.sqrt(np.square(B1) - 4*sqabs(C1) + 0j))/(2*C1) gamma_l = (B2 - np.sqrt(np.square(B2) - 4*sqabs(C2) + 0j))/(2*C2) zs_loop = net.s2z(np.array([[[gamma_s]]]))[0,0,0] nf_loop = bjt_degen.nf(zs_loop)[idx_900mhz] print(l, nf_loop, zs_loop, gamma_s, gamma_l) if nf_loop < nf_min: nf_min = nf_loop lval = l gamma_s_opt = gamma_s gamma_l_opt = gamma_l best_zs_opt = zs_opt (lval, best_zs_opt, gamma_s_opt, gamma_l_opt, nf_min)
1e-10 1.39004755786 (15.5894842614+30.2467250818j) (-0.257263470087+0.579789625782j) (0.0192134416775+0.480457056109j) 2e-10 1.32148206215 (19.8712614478+30.3036449485j) (-0.204613767877+0.522449246878j) (0.0292175073951+0.414182880138j) 3e-10 1.28253228614 (23.7928639311+30.3956157423j) (-0.158574616622+0.477222145606j) (0.0366974628291+0.368110623199j) 4e-10 1.2575313022 (27.5095421512+30.5227916628j) (-0.116953597704+0.439849610945j) (0.0425440052985+0.333646196124j) 5e-10 1.24042749668 (31.0953186622+30.6853143492j) (-0.0786764504463+0.408155816009j) (0.0472485855604+0.306658112893j) 6e-10 1.2283136014 (34.5911194839+30.8833126905j) (-0.043119574361+0.380831796353j) (0.0511138574781+0.284840566038j) 7e-10 1.21959256626 (38.0217491471+31.1169026459j) (-0.0098765572615+0.357005294955j) (0.0543397866774+0.266779721751j) 8e-10 1.21330303444 (41.4032086725+31.3861870766j) (0.0213411269211+0.336053525043j) (0.0570652380034+0.251549939554j) 9e-10 1.2088264434 (44.7462893699+31.6912555883j) (0.0507520483881+0.317509631789j) (0.0593903369725+0.238514981895j) 1e-09 1.20574407703 (48.0585088546+32.0321843857j) (0.0785301505662+0.3010110236j) (0.061389445752+0.227220706591j) 1.1e-09 1.20376094356 (51.3452291889+32.4090361388j) (0.104819106483+0.286268531444j) (0.0631191378838+0.217332990455j) 1.2e-09 1.20266239564 (54.6103388437+32.8218598607j) (0.129740938105+0.273046825847j) (0.0646233253856+0.208599819861j) 1.3e-09 1.20228802942 (57.8566873392+33.2706907992j) (0.153401455452+0.261151339816j) (0.06593667766+0.200827106222j) 1.4e-09 1.20251526477 (61.0863725182+33.7555503395j) (0.175893845865+0.250419166099j) (0.0670869714519+0.193862691009j) 1.5e-09 1.20324862446 (64.3009363258+34.2764459208j) (0.197301142034+0.240712498779j) (0.06809674798+0.187585441497j) 1.6e-09 1.20441250989 (67.5015017825+34.8333709657j) (0.217697985026+0.231913770305j) (0.0689845074955+0.181897622071j) 1.7e-09 1.20594619939 (70.6888710229+35.426304823j) (0.237151928911+0.223921957932j) (0.0697655870273+0.176719436281j) 1.8e-09 1.20780030283 (73.8635968876+36.0552127237j) (0.255724437729+0.216649721121j) (0.0704528162811+0.17198504474j) 1.9e-09 1.20993419622 (77.0260361393+36.7200457505j) (0.273471669599+0.210021144816j) (0.0710570151369+0.167639609156j) 2e-09 1.21231413122 (80.1763896586+37.4207408217j) (0.290445109214+0.20396993446j) (0.0715873760745+0.163637064061j) 2.1e-09 1.21491181925 (83.3147332457+38.1572206881j) (0.306692089232+0.198437954395j) (0.0720517617025+0.159938413777j) 2.2e-09 1.2177033556 (86.4410415381+38.929393944j) (0.322256228045+0.193374031699j) (0.0724569387748+0.156510414546j) 2.3e-09 1.220668391 (89.5552068098+39.737155052j) (0.337177803018+0.188732968231j) (0.072808764091+0.153324543125j) 2.4e-09 1.22378948617 (92.6570539151+40.580384382j) (0.351494072724+0.184474718078j) (0.0731123335235+0.15035618126j) 2.5e-09 1.22705160354 (95.7463522925+41.4589482641j) (0.365239558067+0.180563697878j) (0.0733721024884+0.147583964745j) 2.6e-09 1.23044170288 (98.8228257042+42.3726990544j) (0.378446289589+0.176968204929j) (0.0735919840867+0.14498925934j) 2.7e-09 1.2339484168 (101.886160211+43.321475216j) (0.391144026558+0.173659923504j) (0.0737754296291+0.142555735435j) 2.8e-09 1.23756178808 (104.936010763+44.3051014128j) (0.403360452147+0.170613503887j) (0.0739254951466+0.140269020308j) 2.9e-09 1.24127305543 (107.972006687+45.3233886168j) (0.415121348133+0.167806201796j) (0.0740448966671+0.13811641184j) 3e-09 1.24507447751 (110.993756299+46.3761342295j) (0.426450751875+0.165217568245j) (0.0741360564229+0.136086641335j) 3.1e-09 1.24895918735 (114.000850808+47.4631222161j) (0.437371097827+0.162829181767j) (0.074201141686+0.13416967583j) 3.2e-09 1.25292107128 (116.992867644+48.5841232528j) (0.447903345477+0.160624416415j) (0.0742420975747+0.132356552398j) 3.3e-09 1.25695466754 (119.969373316+49.738894887j) (0.458067095275+0.158588240093j) (0.0742606749006+0.130639238546j) 3.4e-09 1.26105508101 (122.929925884+50.9271817107j) (0.467880693913+0.156707038728j) (0.0742584539118+0.129010513998j) 3.5e-09 1.265217911 (125.87407711+52.1487155452j) (0.477361330109+0.154968462533j) (0.0742368646251+0.127463870138j) 3.6e-09 1.26943918999 (128.801374338+53.4032156391j) (0.48652512189+0.153361291223j) (0.074197204306+0.125993424085j) 3.7e-09 1.27371533128 (131.71136216+54.6903888771j) (0.495387196259+0.15187531556j) (0.0741406525559+0.124593844977j) 3.8e-09 1.27804308406 (134.603583879+56.009930001j) (0.503961762004+0.150501232989j) (0.0740682843799+0.123260290467j) 3.9e-09 1.28241949481 (137.47758282+57.3615218415j) (0.512262176327+0.149230555487j) (0.0739810815454+0.121988351828j) 4e-09 1.28684187391 (140.332903498+58.7448355612j) (0.520301005897+0.148055528023j) (0.0738799424878+0.120774006312j) 4.1e-09 1.29130776665 (143.169092671+60.1595309072j) (0.52809008285+0.146969056248j) (0.0737656909757+0.119613575685j) 4.2e-09 1.29581492809 (145.98570029+61.6052564744j) (0.53564055621+0.14596464226j) (0.0736390837135+0.118503690005j) 4.3e-09 1.30036130097 (148.782280354+63.0816499786j) (0.542962939162+0.145036327422j) (0.0735008170314+0.117441255891j) 4.4e-09 1.30494499653 (151.558391692+64.5883385379j) (0.55006715255+0.144178641367j) (0.0733515327883+0.116423428645j) 4.5e-09 1.3095642775 (154.313598677+66.1249389638j) (0.556962564938+0.14338655646j) (0.0731918235928+0.115447587688j) 4.6e-09 1.31421754323 (157.047471868+67.6910580599j) (0.563658029545+0.142655447031j) (0.0730222374346+0.114511314857j) 4.7e-09 1.3189033165 (159.759588611+69.2862929289j) (0.570161918327+0.141981052862j) (0.0728432818008+0.113612375188j) 4.8e-09 1.32362023185 (162.44953358+70.9102312865j) (0.576482153451+0.141359446391j) (0.0726554273441+0.112748699843j) 4.9e-09 1.32836702521 (165.116899275+72.5624517829j) (0.582626236396+0.140787003248j) (0.0724591111582+0.111918370926j) 5e-09 1.3331425247 (167.761286483+74.2425243297j) (0.588601274865+0.140260375723j) (0.0722547397096+0.111119607937j)
Out[93]:
(1.3000000000000001e-09, (58.260760123906223+10.540266898440597j), (0.15340145545169601+0.26115133981594946j), (0.065936677660019843+0.20082710622244201j), 1.2022880294195262)
In [94]:
# looks like the best result's at 1.5 nH; let's figure out the matching network and see how it performs z_s_opt = net.s2z(np.array([[[gamma_s_opt]]]))[0,0,0] z_l_opt = net.s2z(np.array([[[gamma_l_opt]]]))[0,0,0] x_s_1, x_s_2 = calc_matching_network_vals(np.conj(z_s_opt), 50) x_l_1, x_l_2 = calc_matching_network_vals(np.conj(z_l_opt), 50) z_s_opt, z_l_opt, x_s_1, x_s_2, x_l_1, x_l_2
Out[94]:
((57.856687339212456+33.270690799230614j), (52.328809174501785+22.001076520402908j), -482.2511382820273j, 36.73493886780487j, -1013.0297250905733j, 24.061298387568115j)
In [95]:
c_s_shunt = np.real(1/(2j*np.pi*915e+6*x_s_1)) #c_s_ser = np.real(1/(2j*np.pi*915e+6*x_s_2)) l_s_ser = np.real(x_s_2/(2j*np.pi*915e+6)) c_l_shunt = np.real(1/(2j*np.pi*915e+6*x_l_1)) l_l_ser = np.real(x_l_2/(2j*np.pi*915e+6)) c_s_shunt, l_s_ser, c_l_shunt, l_l_ser
Out[95]:
(3.606830854466748e-13, 6.3896689672019e-09, 1.71702590958208e-13, 4.185218115399472e-09)
In [96]:
input_match_front_end = tem.inductor(l_s_ser) ** tem.shunt_capacitor(c_s_shunt) output_match_front_end = tem.shunt_capacitor(c_l_shunt) ** tem.inductor(l_l_ser) front_end = (input_match_front_end ** series(bjt, tem.shunt_inductor(lval)) ** tem.shunt(tem.resistor(100.) ** tem.short()) ** output_match_front_end) front_end.plot_s_smith()
In [97]:
front_end.s11.plot_s_db() front_end.s22.plot_s_db() 10*np.log10(front_end.nf(50.)[idx_900mhz]), 20*np.log10(np.abs(front_end.s21.s[idx_900mhz,0,0]))
Out[97]:
(0.80011649020042164, 11.466753557644294)
In [98]:
# so this design trades off 4 dB of gain to reduce the NF by ~1 dB, compared with the design below # let's check for stability, and then calculate the tradeoff to see if it's worthwhile rs = np.absolute((front_end.s12.s * front_end.s21.s)/(sqabs(front_end.s11.s) - sqabs(delta))) cs = np.conj(front_end.s11.s - delta*np.conj(front_end.s22.s))/(sqabs(front_end.s11.s) - sqabs(delta)) # input stability for i, f in enumerate(bjt.f): if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cs[i][0, 0], rs[i][0, 0])) n.plot_s_smith()
In [99]:
# output stability for i, f in enumerate(bjt.f): # decimate it a little if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cl[i][0, 0], rl[i][0, 0])) n.plot_s_smith()
In [100]:
# so it's stable at higher frequencies, but could oscillate if it sees too inductive a load at lower frequencies # I think it makes sense to accept the higher NF of the design below
In [101]:
# let's stabilize the transistor by adding some parallel resistance # and then simultaneous conjugate matching so that both the input and output are matched to 50 ohms # without any emitter degeneration # let's calculate the stability factor to see where it's unstable sqabs = lambda x: np.square(np.absolute(x)) delta = bjt.s11.s*bjt.s22.s - bjt.s12.s*bjt.s21.s K = ((1 - sqabs(bjt.s11.s) - sqabs(bjt.s22.s) + sqabs(delta))/(2*np.absolute(bjt.s12.s*bjt.s21.s)))[:,0,0] plt.plot(bjt.f, K)
Out[101]:
[<matplotlib.lines.Line2D at 0x7f62273ef400>]
In [102]:
#so it's basically always unstable #let's add a 250 ohm shunt to the output and see how much that improves it bjt_comp = bjt ** tem.shunt(tem.resistor(75.) ** tem.short()) bjt_comp.plot_s_smith()
In [103]:
# let's calculate K again delta2 = bjt_comp.s11.s*bjt_comp.s22.s - bjt_comp.s12.s*bjt_comp.s21.s K2 = ((1 - sqabs(bjt_comp.s11.s) - sqabs(bjt_comp.s22.s) + sqabs(delta2))/(2*np.absolute(bjt_comp.s12.s*bjt_comp.s21.s)))[:,0,0] plt.plot(bjt_comp.f, K2)
Out[103]:
[<matplotlib.lines.Line2D at 0x7f622765c198>]
In [104]:
# that's a pretty nice improvement! let's check the delta to be sure, and # find the new source stability circles to see if we need to add some series resistance there # K > 1 and |delta| < 1 for stability plt.plot(bjt_comp.f, np.absolute(delta2[:,0,0]))
Out[104]:
[<matplotlib.lines.Line2D at 0x7f62273b82b0>]
In [105]:
rs2 = np.absolute((bjt_comp.s12.s * bjt_comp.s21.s)/(sqabs(bjt_comp.s11.s) - sqabs(delta2))) cs2 = np.conj(bjt_comp.s11.s - delta2*np.conj(bjt_comp.s22.s))/(sqabs(bjt_comp.s11.s) - sqabs(delta2)) # input stability for i, f in enumerate(bjt_comp.f): if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cs2[i][0, 0], rs2[i][0, 0])) n.plot_s_smith()
In [106]:
# that doesn't look too bad, so let's move forward and try to conjugate match delta2 = bjt_comp.s11.s*bjt_comp.s22.s - bjt_comp.s12.s*bjt_comp.s21.s B1 = 1 + sqabs(bjt_comp.s11.s) - sqabs(bjt_comp.s22.s) - sqabs(delta2) B2 = 1 + sqabs(bjt_comp.s22.s) - sqabs(bjt_comp.s11.s) - sqabs(delta2) C1 = bjt_comp.s11.s - delta2*np.conj(bjt_comp.s22.s) C2 = bjt_comp.s22.s - delta2*np.conj(bjt_comp.s11.s) gamma_s_all = (B1 - np.sqrt(np.square(B1) - 4*sqabs(C1) + 0j))/(2*C1) gamma_l_all = (B2 - np.sqrt(np.square(B2) - 4*sqabs(C2) + 0j))/(2*C2) gamma_s = gamma_s_all[idx_900mhz, 0, 0] gamma_l = gamma_l_all[idx_900mhz, 0, 0] z_s = net.s2z(np.array([[[gamma_s]]]))[0,0,0] z_l = net.s2z(np.array([[[gamma_l]]]))[0,0,0] # this section broke when I switched to a lower bias current so just ignore this z_s, z_l
Out[106]:
((14.453769011425337+31.967494868137187j), (28.41148437701095+33.098424068469981j))
In [107]:
x_s_1, x_s_2 = calc_matching_network_vals(np.conj(z_s), 50) x_l_1, x_l_2 = calc_matching_network_vals(np.conj(z_l), 50) x_s_1, x_s_2, x_l_1, x_l_2
Out[107]:
(54.634144641220715j, (1.0165472266293926e-07-31.88333775860665j), 57.8645657612208j, (3.290115549532768e-07-57.35952884685131j))
In [108]:
c_s_shunt = np.real(1/(2j*np.pi*915e+6*x_s_1)) c_s_ser = np.real(1/(2j*np.pi*915e+6*x_s_2)) l_s_ser = np.real(x_s_2/(2j*np.pi*915e+6)) c_l_shunt = np.real(1/(2j*np.pi*915e+6*x_l_1)) l_l_ser = np.real(x_l_2/(2j*np.pi*915e+6)) c_s_shunt2 = np.real(1/(2j*np.pi*915e+6*x_s_2)) l_s_ser2 = np.real(x_s_1/(2j*np.pi*915e+6)) c_l_shunt2 = np.real(1/(2j*np.pi*915e+6*x_l_2)) l_l_ser2 = np.real(x_l_1/(2j*np.pi*915e+6)) #c_s_shunt, c_s_ser, c_l_shunt, l_l_ser c_s_shunt2, l_s_ser2, c_l_shunt2, l_l_ser2
Out[108]:
(5.4555087623716236e-12, 9.503053749997663e-09, 3.032448697062142e-12, 1.0064952645644079e-08)
In [109]:
#input_network2 = tem.capacitor(c_s_ser) ** tem.shunt_capacitor(c_s_shunt) #input_network2 = tem.inductor(l_s_ser) ** tem.shunt_capacitor(c_s_shunt) input_network2 = tem.shunt_capacitor(c_s_shunt2) ** tem.inductor(l_s_ser2) #output_network2 = tem.shunt_capacitor(c_l_shunt) ** tem.inductor(l_l_ser) output_network2 = tem.inductor(l_l_ser2) ** tem.shunt_capacitor(c_l_shunt2) amplifier2 = input_network2 ** bjt_comp ** output_network2 #amplifier2 = input_network2 ** bjt #** output_network2 amplifier2.plot_s_smith()
In [110]:
amplifier2.s21.plot_s_db()
In [111]:
amplifier2.s11.plot_s_db()
In [112]:
amplifier2.s22.plot_s_db()
In [113]:
10*np.log10(amplifier2.nf(50.)[idx_900mhz])
Out[113]:
1.5225393220600005
In [114]:
# check stability again delta2 = amplifier2.s11.s*amplifier2.s22.s - amplifier2.s12.s*amplifier2.s21.s K2 = ((1 - sqabs(amplifier2.s11.s) - sqabs(amplifier2.s22.s) + sqabs(delta2))/(2*np.absolute(amplifier2.s12.s*amplifier2.s21.s)))[:,0,0] plt.plot(amplifier2.f, K2)
Out[114]:
[<matplotlib.lines.Line2D at 0x7f62275332e8>]
In [ ]: