def test_2ports_complex_characteristic_impedance(self): ''' Connect two 2-ports networks in a resulting 2-ports network, same complex charact impedance (1+1j) for all ports ''' z0 = 1 + 1j freq = rf.Frequency(start=1, npoints=1) a = rf.Network(name='a') a.frequency = freq a.s = np.random.rand(4).reshape(2, 2) a.z0 = z0 b = rf.Network(name='b') b.frequency = freq b.s = np.random.rand(4).reshape(2, 2) b.z0 = z0 # classic connecting c = rf.connect(a, 1, b, 0) # Circuit connecting port1 = rf.Circuit.Port(freq, z0=z0, name='port1') port2 = rf.Circuit.Port(freq, z0=z0, name='port2') connections = [[(port1, 0), (a, 0)], [(a, 1), (b, 0)], [(b, 1), (port2, 0)]] circuit = rf.Circuit(connections) assert_array_almost_equal(c.s, circuit.s_external)
def test_2ports_different_characteristic_impedances(self): ''' Connect two 2-ports networks in a resulting 2-ports network, different characteristic impedances for each network ports ''' freq = rf.Frequency(start=1, npoints=1) a = rf.Network(name='a') a.frequency = freq a.s = np.random.rand(4).reshape(2,2) a.z0 = [1, 2] # Z0 should never be zero b = rf.Network(name='b') b.frequency = freq b.s = np.random.rand(4).reshape(2,2) b.z0 = [11, 12] # classic connecting c = rf.connect(a, 1, b, 0) # Circuit connecting port1 = rf.Circuit.Port(freq, z0=1, name='port1') port2 = rf.Circuit.Port(freq, z0=12, name='port2') connections = [[(port1, 0), (a, 0)], [(a, 1), (b, 0)], [(b, 1), (port2, 0)] ] circuit = rf.Circuit(connections) assert_array_almost_equal(c.s, circuit.s_external)
def test_4ports_different_characteristic_impedances(self): """ Connect two 4-ports networks in a resulting 4-ports network, with different characteristic impedances """ z0 = [1, 2, 3, 4] freq = rf.Frequency(start=1, npoints=1) a = rf.Network(name='a') a.frequency = freq a.s = np.random.rand(16).reshape(4, 4) a.z0 = z0 b = rf.Network(name='b') b.frequency = freq b.s = np.random.rand(16).reshape(4, 4) b.z0 = [11, 12, 13, 14] # classic connecting _c = rf.connect(a, 2, b, 0) c = rf.innerconnect(_c, 2, 3) # Circuit connecting port1 = rf.Circuit.Port(freq, z0=1, name='port1') port2 = rf.Circuit.Port(freq, z0=2, name='port2') port3 = rf.Circuit.Port(freq, z0=13, name='port3') port4 = rf.Circuit.Port(freq, z0=14, name='port4') connections = [[(port1, 0), (a, 0)], [(port2, 0), (a, 1)], [(a, 2), (b, 0)], [(a, 3), (b, 1)], [(b, 2), (port3, 0)], [(b, 3), (port4, 0)]] circuit = rf.Circuit(connections) assert_array_almost_equal(c.s, circuit.s_external)
def test_4ports_complex_characteristic_impedances(self): ''' Connect two 4-ports networks in a resulting 4-ports network, with same complex characteric impedances ''' z0 = 5 + 4j freq = rf.Frequency(start=1, npoints=1) a = rf.Network(name='a') a.frequency = freq a.s = np.random.rand(16).reshape(4, 4) a.z0 = z0 b = rf.Network(name='b') b.frequency = freq b.s = np.random.rand(16).reshape(4, 4) b.z0 = z0 # classic connecting c = rf.connect(a, 2, b, 0, 2) # circuit connecting port1 = rf.Circuit.Port(freq, z0=z0, name='port1') port2 = rf.Circuit.Port(freq, z0=z0, name='port2') port3 = rf.Circuit.Port(freq, z0=z0, name='port3') port4 = rf.Circuit.Port(freq, z0=z0, name='port4') connections = [ [(port1, 0), (a, 0)], [(port2, 0), (a, 1)], [(a, 2), (b, 0)], [(a, 3), (b, 1)], [(b, 2), (port3, 0)], [(b, 3), (port4, 0)]] circuit = rf.Circuit(connections) assert_array_almost_equal(c.s, circuit.s_external)
def setUp(self): # setup a test transmission line randomly excited self.P_f = np.random.rand() # forward power in Watt self.phase_f = np.random.rand() # forward phase in rad self.Z = np.random.rand( ) # source internal impedance, line characteristic impedance and load impedance self.L = np.random.rand() # line length in [m] self.freq = rf.Frequency(1, 10, 10, unit='GHz') self.line_media = rf.media.DefinedGammaZ0( self.freq, z0=self.Z) # lossless line medium self.line = self.line_media.line( d=self.L, unit='m', name='line') # transmission line Network # forward voltages and currents at the input of the test line self.V_in = np.sqrt(2 * self.Z * self.P_f) * np.exp(1j * self.phase_f) self.I_in = np.sqrt(2 * self.P_f / self.Z) * np.exp(1j * self.phase_f) # forward voltages and currents at the output of the test line theta = rf.theta(self.line_media.gamma, self.freq.f, self.L) # electrical length self.V_out, self.I_out = rf.tlineFunctions.voltage_current_propagation( self.V_in, self.I_in, self.Z, theta) # Equivalent model with Circuit port1 = rf.Circuit.Port(frequency=self.freq, name='port1', z0=self.Z) port2 = rf.Circuit.Port(frequency=self.freq, name='port2', z0=self.Z) cnx = [[(port1, 0), (self.line, 0)], [(port2, 0), (self.line, 1)]] self.crt = rf.Circuit(cnx) # power and phase arrays for Circuit.voltages() and currents() self.power = [self.P_f, 0] self.phase = [self.phase_f, 0]
def test_shunt_element(self): ''' Compare a shunt element network (here a capacitor) ''' freq = rf.Frequency(start=1, stop=2, npoints=101) line = rf.media.DefinedGammaZ0(frequency=freq, z0=50) # usual way cap_shunt_manual = line.shunt_capacitor(50e-12) # A Circuit way port1 = rf.Circuit.Port(frequency=freq, name='port1', z0=50) port2 = rf.Circuit.Port(frequency=freq, name='port2', z0=50) cap_shunt = line.capacitor(50e-12, name='cap_shunt') ground = rf.Circuit.Ground(frequency=freq, name='ground', z0=50) connections = [ [(port1, 0), (cap_shunt, 0), (port2, 0)], [(cap_shunt, 1), (ground, 0)] ] # # Another possibility could have been without ground : # shunt_cap = line.shunt_capacitor(50e-12) # shunt_cap.name='shunt_cap' # connections = [ # [(port1, 0), (shunt_cap, 0)], # [(shunt_cap ,1), (port2, 0)] # ] cap_shunt_from_circuit = rf.Circuit(connections).network assert_array_almost_equal(cap_shunt_manual.s, cap_shunt_from_circuit.s)
def test_1port_short(self): """ Connect a short directly to the port """ freq = rf.Frequency(start=1, npoints=1) port1 = rf.Circuit.Port(freq, name='port1') line = rf.media.DefinedGammaZ0(frequency=freq) short = line.short(name='short') gnd1 = rf.Circuit.Ground(freq, name='gnd') # method 1 : use the Ground Network (which 2 port actually) cnx = [[(port1, 0), (gnd1, 0)]] cir = rf.Circuit(cnx) assert_array_almost_equal(short.s, cir.s_external) # method 2 : use a short Network (1 port) cnx = [[(port1, 0), (short, 0)]] cir = rf.Circuit(cnx) assert_array_almost_equal(short.s, cir.s_external)
def setUp(self): """ Circuit setup """ self.test_dir = os.path.dirname(os.path.abspath(__file__)) + '/' self.freq = rf.Frequency(start=1, stop=2, npoints=101) # characteristic impedance of the ports Z0_ports = 50 # resistor self.R = 100 self.line_resistor = rf.media.DefinedGammaZ0(frequency=self.freq, Z0=self.R) self.resistor = self.line_resistor.resistor(self.R, name='resistor') # branches Z0_branches = np.sqrt(2) * Z0_ports self.line_branches = rf.media.DefinedGammaZ0(frequency=self.freq, Z0=Z0_branches) self.branch1 = self.line_branches.line(90, unit='deg', name='branch1') self.branch2 = self.line_branches.line(90, unit='deg', name='branch2') # ports port1 = rf.Circuit.Port(self.freq, name='port1') port2 = rf.Circuit.Port(self.freq, name='port2') port3 = rf.Circuit.Port(self.freq, name='port3') # Connection setup self.connections = [[(port1, 0), (self.branch1, 0), (self.branch2, 0)], [(port2, 0), (self.branch1, 1), (self.resistor, 0)], [(port3, 0), (self.branch2, 1), (self.resistor, 1)]] self.C = rf.Circuit(self.connections) # theoretical results from ref P.Hallbjörner (2003) self.X1_nn = np.array([1 - np.sqrt(2), -1, -1]) / (1 + np.sqrt(2)) self.X2_nn = np.array([ 1 - np.sqrt(2), -3 + np.sqrt(2), -1 - np.sqrt(2) ]) / (3 + np.sqrt(2)) self.X1_m1 = 2 / (1 + np.sqrt(2)) self.X1_m2 = np.sqrt(2) / (1 + np.sqrt(2)) self.X1_m3 = np.sqrt(2) / (1 + np.sqrt(2)) self.X2_m1 = 4 / (3 + np.sqrt(2)) self.X2_m2 = 2 * np.sqrt(2) / (3 + np.sqrt(2)) self.X2_m3 = 2 / (3 + np.sqrt(2)) self.X1 = np.array([[0, self.X1_m2, self.X1_m3], [self.X1_m1, 0, self.X1_m3], [self.X1_m1, self.X1_m2, 0]]) + np.diag(self.X1_nn) self.X2 = np.array([[0, self.X2_m2, self.X2_m3], [self.X2_m1, 0, self.X2_m3], [self.X2_m1, self.X2_m2, 0]]) + np.diag(self.X2_nn)
def test_cascade(self): ''' Compare ntwk3 to the Circuit of ntwk1 and ntwk2. ''' connections = [ [(self.port1, 0), (self.ntwk1, 0)], [(self.ntwk1, 1), (self.ntwk2, 0)], [(self.ntwk2, 1), (self.port2, 0)] ] circuit = rf.Circuit(connections) assert_array_almost_equal(circuit.s_external, self.ntwk3.s)
def test_cascade2(self): ''' Same thing with different ordering of the connections. Demonstrate that changing the connections setup order does not change the result. ''' connections = [ [(self.port1, 0), (self.ntwk1, 0)], [(self.ntwk2, 0), (self.ntwk1, 1)], [(self.port2, 0), (self.ntwk2, 1)] ] circuit = rf.Circuit(connections) assert_array_almost_equal(circuit.s_external, self.ntwk3.s)
def test_cascade3(self): ''' Inverting the cascading network order Demonstrate that changing the connections setup order does not change the result (at the requirement that port impedance are the same). ''' connections = [ [(self.port1, 0), (self.ntwk2, 0)], [(self.ntwk2, 1), (self.ntwk1, 0)], [(self.port2, 0), (self.ntwk1, 1)] ] circuit = rf.Circuit(connections) ntw = self.ntwk2 ** self.ntwk1 assert_array_almost_equal(circuit.s_external, ntw.s)
def test_s_active(self): ''' Test the active s-parameter of a 2-ports network ''' connections = [[(self.port1, 0), (self.ntwk1, 0)], [(self.ntwk1, 1), (self.ntwk2, 0)], [(self.ntwk2, 1), (self.port2, 0)]] circuit = rf.Circuit(connections) # s_act should be equal to s11 if a = [1,0] assert_array_almost_equal(circuit.s_active([1, 0])[:,0], circuit.s_external[:,0,0]) # s_act should be equal to s22 if a = [0,1] assert_array_almost_equal(circuit.s_active([0, 1])[:,1], circuit.s_external[:,1,1])
def to_network(self, skrf_frequency: rf.Frequency, name: str = 'front-face') -> rf.Network: """Convert into a skrf Network. Assume the scattering parameters of the network are the same for all the frequencies of the network. Parameters ---------- skrf_frequency : :class:`skrf.frequency.Frequency` Frequency of the network (for compatibility with skrf) name : string, optional name of the network. Default is 'front-face' Returns ------- network : :class:`skrf.network.Network` """ network = rf.Network(name=name) network.s = np.tile(self.s, (len(skrf_frequency), 1, 1)) network.z0 = self.z0 network.frequency = skrf_frequency # take into deembbeding if necessary if self._deemb != 0: # create a coaxial line of length = deemb coax_media = rf.DefinedGammaZ0(network.frequency, z0=self.z0) # Create a circuit in which we connect the inverse of the coaxial # lines to each ports coax1 = coax_media.line(d=self._deemb, unit='m', name='line1').inv coax2 = coax_media.line(d=self._deemb, unit='m', name='line2').inv coax3 = coax_media.line(d=self._deemb, unit='m', name='line3').inv coax4 = coax_media.line(d=self._deemb, unit='m', name='line4').inv port1 = rf.Circuit.Port(network.frequency, 'port1', z0=self.z0) port2 = rf.Circuit.Port(network.frequency, 'port2', z0=self.z0) port3 = rf.Circuit.Port(network.frequency, 'port3', z0=self.z0) port4 = rf.Circuit.Port(network.frequency, 'port4', z0=self.z0) cnx = [ [(port1, 0), (coax1, 0)], [(coax1, 1), (network, 0)], [(port2, 0), (coax2, 0)], [(coax2, 1), (network, 1)], [(port3, 0), (coax3, 0)], [(coax3, 1), (network, 2)], [(port4, 0), (coax4, 0)], [(coax4, 1), (network, 3)], ] network = rf.Circuit(cnx).network.copy() return (network)
def test_1port_matched_load(self): """ Connect a matched load directly to the port """ freq = rf.Frequency(start=1, npoints=1) port1 = rf.Circuit.Port(freq, name='port1') line = rf.media.DefinedGammaZ0(frequency=freq) match_load = line.match(name='match_load') cnx = [[(port1, 0), (match_load, 0)]] cir = rf.Circuit(cnx) assert_array_almost_equal(match_load.s, cir.s_external)
def test_1port_random_load(self): """ Connect a random load directly to the port """ freq = rf.Frequency(start=1, npoints=1) port1 = rf.Circuit.Port(freq, name='port1') line = rf.media.DefinedGammaZ0(frequency=freq) gamma = np.random.rand(1, 1) + 1j * np.random.rand(1, 1) load = line.load(gamma, name='load') cnx = [[(port1, 0), (load, 0)]] cir = rf.Circuit(cnx) assert_array_almost_equal(load.s, cir.s_external)
def test_1port_matched_network_default_impedance(self): ''' Connect a 2 port network to a matched load ''' freq = rf.Frequency(start=1, npoints=1) a = rf.Network(name='a') a.frequency = freq a.s = np.random.rand(4).reshape(2, 2) line = rf.media.DefinedGammaZ0(frequency=freq) match_load = line.match(name='match_load') # classic connecting b = a ** match_load # Circuit connecting port1 = rf.Circuit.Port(freq, name='port1') connections = [[(port1, 0), (a, 0)], [(a, 1), (match_load, 0)]] circuit = rf.Circuit(connections) assert_array_almost_equal(b.s, circuit.s_external)
def setUp(self): """ Dummy Circuit setup Setup a circuit which has various interconnections (2 or 3) """ self.freq = rf.Frequency(start=1, stop=2, npoints=101) # dummy components self.R = 100 self.line_resistor = rf.media.DefinedGammaZ0(frequency=self.freq, Z0=self.R) resistor1 = self.line_resistor.resistor(self.R, name='resistor1') resistor2 = self.line_resistor.resistor(self.R, name='resistor2') resistor3 = self.line_resistor.resistor(self.R, name='resistor3') port1 = rf.Circuit.Port(self.freq, name='port1') # Connection setup self.connections = [[(port1, 0), (resistor1, 0), (resistor3, 0)], [(resistor1, 1), (resistor2, 0)], [(resistor2, 1), (resistor3, 1)]] self.C = rf.Circuit(self.connections)
def test_1port_matched_network_complex_impedance(self): ''' Connect a 2 port network to a complex impedance. Both ports are complex. ''' z01, z02 = 1-1j, 2+4j freq = rf.Frequency(start=1, npoints=1) a = rf.Network(name='a') a.frequency = freq a.s = np.random.rand(4).reshape(2, 2) a.z0 = [z01, z02] line = rf.media.DefinedGammaZ0(frequency=freq, z0=z02) match_load = line.match(name='match_load') # classic connecting b = a ** match_load # Circuit connecting port1 = rf.Circuit.Port(freq, z0=z01, name='port1') connections = [[(port1, 0), (a,0)], [(a, 1), (match_load, 0)]] circuit = rf.Circuit(connections) assert_array_almost_equal(b.s, circuit.s_external)
def variable_coupler_circuit(self, phase_deg): ps = self.phase_shifter(phase_deg) ps.name = 'ps' # do not forget the name of the network ! hybrid1, hybrid2 = self.hybrid('hybrid1'), self.hybrid('hybrid2') port1 = rf.Circuit.Port(ps.frequency, 'port1') port2 = rf.Circuit.Port(ps.frequency, 'port2') port3 = rf.Circuit.Port(ps.frequency, 'port3') port4 = rf.Circuit.Port(ps.frequency, 'port4') # Note that the order of port appearance is important. # 1st port to appear in the connection setup will be the 1st port (0), # then second to appear the second port (1), etc... # There is no constraint for the order of the connections. connections = [ [(port1, 0), (hybrid1, 3)], [(port2, 0), (hybrid2, 2)], [(port3, 0), (hybrid2, 1)], [(port4, 0), (hybrid1, 0)], [(hybrid1, 2), (ps, 0)], [(hybrid1, 1), (hybrid2, 0)], [(ps, 1), (hybrid2, 3)], ] return rf.Circuit(connections)
def setUp(self): self.f0 = rf.Frequency(75.8, npoints=1, unit='GHz') # initial s-param values of A 2 ports network self.s0 = np.array([ # dummy values [-0.1000 -0.2000j, -0.3000 +0.4000j], [-0.3000 +0.4000j, 0.5000 -0.6000j]]).reshape(-1,2,2) # build initial network (under z0=50) self.ntw0 = rf.Network(frequency=self.f0, s=self.s0, z0=50, name='dut') # complex characteristic impedance to renormalize to self.zdut = 100 + 10j # reference solutions obtained from ANSYS Circuit or ADS (same res) # when z0=[50, zdut] self.z_ref = np.array([ # not affected by z0 [18.0000 -16.0000j, 20.0000 +40.0000j], [20.0000 +40.0000j, 10.0000 -80.0000j]]).reshape(-1,2,2) self.y_ref = np.array([ # not affected by z0 [0.0251 +0.0023j, 0.0123 -0.0066j], [0.0123 -0.0066j, 0.0052 +0.0055j]]).reshape(-1,2,2) self.s_ref = np.array([ # renormalized s (power-waves) [-0.1374 -0.2957j, -0.1995 +0.5340j], [-0.1995 +0.5340j, -0.0464 -0.7006j]]).reshape(-1,2,2) # Creating equivalent reference circuit port1 = rf.Circuit.Port(self.f0, z0=50, name='port1') port2 = rf.Circuit.Port(self.f0, z0=50, name='port2') ntw0 = rf.Network(frequency=self.f0, z0=50, s=self.s0, name='dut') cnx = [ # z0=[50,50] [(port1, 0), (ntw0, 0)], [(ntw0, 1), (port2, 0)] ] self.cir = rf.Circuit(cnx) # Creating equivalent circuit with z0 real port2_real = rf.Circuit.Port(self.f0, z0=100, name='port2') cnx_real = [ # z0=[50,100] [(port1, 0), (ntw0, 0)], [(ntw0, 1), (port2_real, 0)] ] self.cir_real = rf.Circuit(cnx_real) # Creating equivalent circuit with z0 complex port2_complex = rf.Circuit.Port(self.f0, z0=self.zdut, name='port2') cnx_complex = [ # z0=[50,zdut] [(port1, 0), (ntw0, 0)], [(ntw0, 1), (port2_complex, 0)] ] self.cir_complex = rf.Circuit(cnx_complex) # references for each s-param definition self.s_legacy = rf.renormalize_s(self.s0, [50,50], [50,self.zdut], s_def='traveling') self.s_power = rf.renormalize_s(self.s0, [50,50], [50,self.zdut], s_def='power') self.s_pseudo = rf.renormalize_s(self.s0, [50,50], [50,self.zdut], s_def='pseudo') # for real values, should be = whatever s-param definition self.s_real = rf.renormalize_s(self.s0, [50,50], [50,100])
def RLC2S_LPF(g, Wstart, Wstop, npoints=1001, dB_limit=-100, ladder_type="LC", W=True, plot=False): """ Function to plot S21 and S11 from g parameters. Main purpose of this function is to ensure that the g values are indeed correct. @param g: g parameters as a list [g_0, g_1, ..., g_{n+1}] @param start: starting frequency (avoid 0) @param stop: stop frequency @param npoints: number of points for plotting purpose """ if not ladder_type == "LC": warn("ladder_type must be LC for the time being") return freq = rf.Frequency(start=Wstart / (2 * pi), stop=Wstop / (2 * pi), unit="Hz", npoints=npoints) tl_media = rf.DefinedGammaZ0(freq, z0=1, gamma=1j * freq.w / rf.c) # GND and port elements gnd = rf.Circuit.Ground(freq, name="gnd") port1 = rf.Circuit.Port(freq, name="port1", z0=g[0]) port2 = rf.Circuit.Port(freq, name="port2", z0=g[-1]) g = g[1:-1] # L and C elements for LC ladder ckt_elements = [] ind_flag = True # L starts first for i in range(len(g)): ckt_elements.append(tl_media.inductor( g[i], name=str(i))) if ind_flag else ckt_elements.append( tl_media.capacitor(g[i], name=str(i))) ind_flag = not ind_flag # toggle between inductors and cpacitors # node connections cnx = [] for i in range(int(len(g) / 2) + 1): if i == 0: cnx.append([(port1, 0), (ckt_elements[i], 0)]) # starting elif i == int(len(g) / 2): if int(len(g)) % 2 == 0: # ending ... even order case cnx.append([ (ckt_elements[2 * i - 2], 1), (ckt_elements[2 * i - 1], 0), (port2, 0), ]) else: # ending ... odd order case cnx.append([ (ckt_elements[2 * i - 2], 1), (ckt_elements[2 * i - 1], 0), (ckt_elements[2 * i], 0), ]) cnx.append([ (ckt_elements[2 * i], 1), (port2, 0), ]) else: # in between cnx.append([ (ckt_elements[2 * i - 2], 1), (ckt_elements[2 * i - 1], 0), (ckt_elements[2 * i], 0), ]) # GND connections ... same for even and odd cases gnd_cnx = [] for i in range(int(len(g) / 2) + 1): if i == 0: gnd_cnx.append((gnd, 0)) elif i == int(len(g) / 2): gnd_cnx.append((ckt_elements[2 * i - 1], 1)) else: gnd_cnx.append((ckt_elements[2 * i - 1], 1)) cnx.append(gnd_cnx) # create a circuit out of all the elements and connections cir = rf.Circuit(cnx) ntw = cir.network freq = 2 * pi * ntw.frequency.f if W else ntw.frequency.f if plot: title = ("Transfer Functions vs Angular Frequency" if W else "Transfer Functions vs Frequency") xlabel = "w" if W else "f" ylabel = "|H(w)|" if W else "|H(f)|" rf.plotting.plot_rectangular( freq, cutoff(rf.mathFunctions.complex2dB(ntw.s[:, 1, 0]), dB_limit), x_label=xlabel, y_label=ylabel, title=title, show_legend=True, label="S21", ) rf.plotting.plot_rectangular( freq, cutoff(rf.mathFunctions.complex2dB(ntw.s[:, 0, 0]), dB_limit), label="S11", ) if not (port1.z0[0] == port2.z0[0]): warn("port2 and port2 impedances are not equal") s_ax = plt.gca() s_ax.axvspan(0, 1, facecolor="r", alpha=0.2) s_ax.axhspan(0, -3, facecolor="g", alpha=0.2) # s_ax.axvspan(0, 1.6, facecolor="r", alpha=0.2) plt.show() return freq, ntw.s[:, 0, 0], ntw.s[:, 1, 0]