850 KiB
850 KiB
In [1]:
import numpy as np import skrf import skrf.media as media 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 = media.DistributedCircuit(f, z0=50) bjt = net.Network('BFU520_Spar_NF_400MHz-2GHz/BFU520_05V0_020mA_NF_SP.s2p').interpolate(f) bjt bjt.plot_s_smith()
In [2]:
# calculate the stability circles for the source and load impedances idx_900mhz = skrf.util.find_nearest_index(bjt.f, 915.e+6) sqabs = lambda x: np.square(np.absolute(x)) delta = bjt.s11.s*bjt.s22.s - bjt.s12.s*bjt.s21.s rl = np.absolute((bjt.s12.s * bjt.s21.s)/(sqabs(bjt.s22.s) - sqabs(delta))) cl = np.conj(bjt.s22.s - delta*np.conj(bjt.s11.s))/(sqabs(bjt.s22.s) - sqabs(delta)) rl_900mhz = rl[idx_900mhz][0, 0] cl_900mhz = cl[idx_900mhz][0, 0] rl_900mhz, cl_900mhz
Out[2]:
(3.6140400937211887, (3.1801811635684145+3.261206811652097j))
In [3]:
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 [4]:
rs = np.absolute((bjt.s12.s * bjt.s21.s)/(sqabs(bjt.s11.s) - sqabs(delta))) cs = np.conj(bjt.s11.s - delta*np.conj(bjt.s22.s))/(sqabs(bjt.s11.s) - sqabs(delta)) rs_900mhz = rs[idx_900mhz][0, 0] cs_900mhz = cs[idx_900mhz][0, 0] print(rs_900mhz, cs_900mhz) cs_points = calc_circle(cs_900mhz, rs_900mhz) plot_smith(cs_points)
1.6708525437591126 (-2.5981379513480034+0.3651291372436944j)
In [5]:
# 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 [6]:
# 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 [7]:
# so not very useful, because the transistor isn't unconditionally stable # time to draw the circles of constant gain and try to find a useful point K = (1 - sqabs(bjt.s11.s) - sqabs(bjt.s22.s) - sqabs(delta))/(2*np.absolute((bjt.s12.s)*(bjt.s21.s))) G_msg = np.absolute(bjt.s21.s)/np.absolute(bjt.s12.s) print(10*np.log10(G_msg[idx_900mhz, 0, 0])) # let's draw a bunch of curves I guess b1 = 1 + sqabs(bjt.s11.s) - sqabs(bjt.s22.s) - sqabs(delta) b2 = 1 + sqabs(bjt.s22.s) - sqabs(bjt.s11.s) - sqabs(delta) c1 = bjt.s11.s - delta * np.conj(bjt.s22.s) c2 = bjt.s22.s - delta * np.conj(bjt.s11.s) gamma_ms = (b1 - np.sqrt(np.square(b1) - 4 * sqabs(c1) + 0.j))/(2*c1) gamma_ml = (b2 - np.sqrt(np.square(b2) - 4 * sqabs(c2) + 0.j))/(2*c2) for G_p in [1, 2, 5, 10, 10**(15/10)]: g_p = G_p/sqabs(bjt.s21.s) r_p = (1 - 2*K*np.absolute(bjt.s12.s*bjt.s21.s)*g_p + sqabs(bjt.s12.s*bjt.s21.s)*(g_p**2))**(0.5)/np.absolute(1 + g_p * (sqabs(bjt.s22.s) - sqabs(delta))) c_p = (g_p * np.conj(c2))/(1 + g_p*(sqabs(bjt.s22.s) - sqabs(delta))) r, c = r_p[idx_900mhz, 0, 0], c_p[idx_900mhz, 0, 0] #print(g_p, r, c) points = calc_circle(c, r) n = net.Network(name = 'G_p = {}'.format(G_p), s=points) n.plot_s_smith() cl_points_net = net.Network(name='stability circle', s=cl_points) cl_points_net.plot_s_smith()
22.571169647701225
In [8]:
load_points = calc_circle(c_p[idx_900mhz, 0, 0], r_p[idx_900mhz, 0, 0]) load_points_net = net.Network(name='gain circle', s=load_points) cl_points_net = net.Network(name='stability circle', s=cl_points) load_points_net.plot_s_smith() cl_points_net.plot_s_smith()
In [9]:
#np.absolute(bjt.s21.s[idx_900mhz, 0, 0]) c_p_900mhz, r_p_900mhz = c_p[idx_900mhz, 0, 0], r_p[idx_900mhz, 0, 0] #d = c_p_900mhz - cl_900mhz #gamma_l = c_p_900mhz + r_p_900mhz * d/np.absolute(d) gamma_l = c_p_900mhz + r_p_900mhz * (np.exp(1.0j*np.pi*1.5)) gamma_l = 0.0 # check stability against the stability circle is_gamma_l_stable = np.absolute(gamma_l - cl_900mhz) > rl_900mhz gamma_l, is_gamma_l_stable
Out[9]:
(0.0, True)
In [10]:
# no output network right now #z_l = net.s2z(np.array([[[gamma_l]]]))[0, 0, 0] #print(z_l) #Q = np.sqrt((50 - np.real(z_l))/np.real(z_l)) #x_1 = 50/Q #x_2 = Q * np.real(z_l) #print(x_1, x_2) #z_tmp = 1./(1./50 + 1./(1.0j * x_1)) #x_cl = 1.0j*(np.imag(z_l) - np.imag(z_tmp)) #z_eff = z_tmp + x_cl #print(z_l, z_tmp, z_eff, x_cl) # TODO set fc somewhere up above #l_l = x_1/(2*np.pi*915e+6) #c_l = 1./(1.0j*2*np.pi*915e+6*x_cl) #print(l_l, c_l)
In [11]:
# just to double check, let's apply these two components to S22 and see if the load effectively sees 50 ohms #z_tmp = z_l - 1.0j/(2*np.pi*915e+6*c_l) #z_eff = 1/(1.0/z_tmp + 1.0/(1.0j*2*np.pi*915e+6*l_l)) #z_tmp, z_eff
In [12]:
#output_network = tem.capacitor(c_l)**tem.shunt_inductor(l_l) #output_network_backwards = tem.shunt_inductor(l_l) ** tem.capacitor(c_l) #output_network #(output_network_backwards ** tem.load(np.conj(gamma_l))).plot_s_smith()
In [13]:
# woo, it probably works! time to work out the source matching network # and then cascade all three of them to see if that works gamma_in = bjt.s11.s + bjt.s12.s*bjt.s21.s*gamma_l/(1 - bjt.s22.s*gamma_l) gamma_s = np.conj(gamma_in) gamma_s = gamma_s[idx_900mhz, 0, 0] # check stability against the stability circle is_gamma_s_stable = np.absolute(gamma_s - cs_900mhz) > rs_900mhz print(gamma_s, is_gamma_s_stable)
(-0.49050989386248095+0.06079170987484189j) True
In [54]:
# time to calculate the source matching network r_series = 10. z_s = net.s2z(np.array([[[gamma_s]]]))[0, 0, 0] + r_series print(z_s) Q = np.sqrt((50 - np.real(z_s))/np.real(z_s)) x_1 = 50.0/Q x_2 = Q*np.real(z_s) print(x_1, x_2) z_tmp = 1./(1./50. + 1./(1.0j * x_1)) z_tmp x_l_s = x_1 x_c_s = -np.imag(z_s) + np.imag(z_tmp) print(x_l_s, x_c_s) #c_shunt_s = -1.0/(2*np.pi*915e+6*x_shunt_s) l_shunt_s = x_l_s/(2*np.pi*915e+6) c_series_s = 1.0/(2*np.pi*915e+6*x_c_s) print(l_shunt_s, c_series_s) # double checking to make sure the match works as expected z_tmp = 1.0/(1.0/50.0 + 1/(1.0j*2*np.pi*915e+6*l_shunt_s)) z_eff = z_tmp + 1/(1.0j*2*np.pi*915e+6*c_series_s) print(z_eff)
(26.97971488195685+2.7318244658221826j) 54.129414947805486 24.92149130742698 54.129414947805486 22.189666841604797 9.415261153678219e-09 7.838776028381008e-12 (26.979714881956852+2.7318244658221786j)
In [55]:
# now to implement it using two port networks input_network = tem.shunt_inductor(l_shunt_s) ** tem.capacitor(c_series_s) ** tem.resistor(r_series) #check stability by seeing how it looks from the transistor input_network_backwards = tem.resistor(r_series) ** tem.capacitor(c_series_s) ** tem.shunt_inductor(l_shunt_s) print(np.all(np.absolute(input_network_backwards.s11.s - cs) > rs)) input_network_backwards.s11.plot_s_smith()
True
In [56]:
# see how it looks from the source net.connect(input_network, 1, tem.load(gamma_in), 0).plot_s_smith()
In [57]:
# let's see what the final network looks like #output_network = tem.capacitor(c_l) ** tem.shunt_inductor(l_l) input_network = tem.shunt_inductor(l_shunt_s) ** tem.capacitor(c_series_s) ** tem.resistor(r_series) amplifier = input_network ** bjt #** output_network amplifier.plot_s_smith()
In [58]:
gain = np.square(np.absolute(amplifier.s21.s[idx_900mhz])) 10*np.log10(gain), gain
Out[58]:
(array([[17.28204483]]), array([[53.48161129]]))
In [59]:
np.absolute(amplifier.s12.s[idx_900mhz])
Out[59]:
array([[0.04045622]])
In [60]:
amplifier.s11.plot_s_db()
In [61]:
amplifier.s21.plot_s_db()
In [64]:
# let's see what the final network looks like with imperfect components #print(c_l, l_l, l_shunt_s, c_series_s) #output_network2 = tem.capacitor(3.9e-12) ** tem.shunt_inductor(12e-9) input_network2 = tem.shunt_inductor(9.1e-9) ** tem.capacitor(7.5e-12) ** tem.resistor(10.) amplifier2 = input_network2 ** bjt #** output_network2 amplifier2.plot_s_smith()
In [65]:
amplifier2.s11.plot_s_db()
In [66]:
print(np.square(np.absolute(amplifier2.s21.s[idx_900mhz]))) amplifier2.s21.plot_s_db()
[[53.4512497]]
In [67]:
amplifier2.s22.plot_s_db()
In [69]:
delta_full = amplifier2.s11.s*amplifier2.s22.s - amplifier2.s12.s*amplifier2.s21.s rl_full = np.absolute((amplifier2.s12.s * amplifier2.s21.s)/(sqabs(amplifier2.s22.s) - sqabs(delta_full))) cl_full = np.conj(amplifier2.s22.s - delta_full*np.conj(amplifier2.s11.s))/(sqabs(amplifier2.s22.s) - sqabs(delta_full)) # let's check the finished amplifier's stability # 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_full[i][0, 0], rl_full[i][0, 0])) n.plot_s_smith()
In [70]:
rs_full = np.absolute((amplifier2.s12.s * amplifier2.s21.s)/(sqabs(amplifier2.s11.s) - sqabs(delta_full))) cs_full = np.conj(amplifier2.s11.s - delta*np.conj(amplifier2.s22.s))/(sqabs(amplifier2.s11.s) - sqabs(delta_full)) # 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_full[i][0, 0], rs_full[i][0, 0])) n.plot_s_smith()
In [ ]: