ax = fig.add_subplot(1, 1, 1) eee51.add_hline_text(ax, 0, 1e5, \ r'{:.1f}dB'.format(0)) ic_labels = [r'$I_C = 1mA$', r'$I_C = 10mA$'] files = [cfg['ac_sim_1mA'], cfg['ac_sim_10mA']] for file, ic_label in zip(files, ic_labels): freq = [] ic1 = [] with open(file, 'r') as f: for line in f: freq.append(float(line.split()[0])) ic1_real = float(line.split()[1]) ic1_imag = float(line.split()[2]) ic1.append(((ic1_real**2) + (ic1_imag**2))**0.5) index, ic1_sim = eee51.find_in_data(ic1, 1.0) ax.semilogx(freq, 20*np.log10(ic1), '-', \ label = ic_label) eee51.add_vline_text(ax, freq[index], 20, \ '{:.2f}MHz'.format(freq[index]/1e6)) eee51.label_plot(plt_cfg, fig, ax) plt.savefig('bjt_2n3904_ft.png')
si_format(bjt_Is, precision=2) + r'A, $n$={:.2f}, and '.format(bjt_n) + \ r'$|V_A|$={:.2f}'.format(abs(g51.bjt_VA))) eee51.add_vline_text(ax, reqd_vbe/g51.milli, 2.5, '$V_{BE}$ = ' + \ '{:.1f}mV'.format(reqd_vbe/g51.milli)) eee51.add_hline_text(ax, specs['ic']/g51.milli, 550, \ '$I_C$ = {:.1f}mA'.format(specs['ic']/g51.milli)) eee51.label_plot(plt_cfg, fig, ax) # get the derivative of ic with respect to vbe dic2 = np.diff(ic2) / np.diff(vbe) dic_ideal = np.diff(ic_ideal) / np.diff(vbe) index, vbe_sim = eee51.find_in_data(vbe, reqd_vbe) gm_sim = dic2[index - 1] gm_ideal = dic_ideal[index - 1] # calculate the bjt small signal parameters gm_calc = ic2[index] / (bjt_n * g51.VT) ro_calc = abs(g51.bjt_VA) / ic2[index] rpi_calc = bjt_beta[index] / gm_calc ao_calc = gm_calc * ro_calc # define the plot parameters plt_cfg = { 'grid_linestyle': 'dotted', 'title': r'BJT 2N2222A ${\partial I_C}/{\partial V_{BE}}$',
vo_mag = np.abs(vo) vo_phase = np.angle(vo, deg=True) labels = ['$f_{p1}$ = ', '$f_{p2}$ = ', '$f_z$ = '] angles = [135, 45, -45] guess = [1e2, 1e2, 1e6, 1e9] freq_rad = [2 * math.pi * f for f in freq] popt, pcov = curve_fit(fmag_2p1z, freq_rad, \ 20*np.log10( vo_mag ), \ p0=guess) Ao_dB_calc = 20 * np.log10(popt[0]) index_vo0dB, vo0dB = eee51.find_in_data(20 * np.log10(vo_mag), 0) fu = freq[index_vo0dB] dAdf = np.diff(20 * np.log10(vo_mag)) / np.diff(np.log10(freq_rad)) # define the plot parameters plt_cfg = { 'grid_linestyle': 'dotted', 'title': r'Common-Emitter Amplifier Frequency Response', 'xlabel': r'Frequency [Hz]', 'ylabel': r'Magnitude [dB]', 'legend_loc': 'lower left', 'add_legend': False, 'legend_title': None }
vswp0 = float(line.split()[0]) vswp = float(line.split()[0]) if i != 0 and vswp == vswp0: m += 1 if m == 0: vce.append(vswp) ic[m].append(float(line.split()[1])) # fit the data to the ideal BJT out characteristic and find the Early Voltage (VA) # fit the line over the linear range of the curves # e.g. from 1V to 4V ind1, vce1 = eee51.find_in_data(vce, 1) ind4, vce4 = eee51.find_in_data(vce, 4) line_m = [] # declare an array of 'slopes' line_b = [] # declare an array of 'y-intercepts' line_VA = [] # declare an array of 'x-intercepts' or in this case, VA # use curve_fit to get the slopes and y-intercepts then caculate VA for j, v in enumerate(vbe): popt, pcov = curve_fit(eee51.line_eq, vce[ind1:ind4], ic[j][ind1:ind4]) line_m.append(popt[0]) line_b.append(popt[1]) line_VA.append(-popt[1] / popt[0]) # declare the x values for plotting the curve-fitted lines line_x = np.linspace(math.floor(min(line_VA)), max(vce), 100)
eee51.run_spice(cfg) # open the transfer characteristics data file and get the data vin = [] # declare list for vin vout = [] # declare a list for vout ic1 = [] # declare a list for ic1 with open(cfg['transfer_data'], 'r') as f: for line in f: vin.append(float(line.split()[0])) vout.append(float(line.split()[1])) ic1.append(float(line.split()[3])) #g51.update_bjt_VA(-15.550605626760145) index, vin_sim = eee51.find_in_data(vin, 0.0) vo_dc = vout[index] # define the plot parameters plt_cfg = { 'grid_linestyle': 'dotted', 'title': r'Common Emitter CM Bias DC Transfer Curve', 'xlabel': r'$v_{in}$ [mV]', 'ylabel': r'$v_{out}$ [V]', 'legend_loc': 'upper left', 'add_legend': True, 'legend_title': None } # plot the amplifier transfer characteristics fig = plt.figure()
g51.update_bjt_vce(0.2) # use VA from our previous analysis g51.update_bjt_VA(-15.550605626760145) popt, pcov = curve_fit(eee51.ideal_bjt_transfer, vbe, ic) bjt_Is = popt[0] # yay! we get an estimate for Is and n bjt_n = popt[1] # generate the ic for the ideal bjt model # using our values for Is, n, and VA at vce = 0.2V ic_ideal = [eee51.ideal_bjt_transfer(v, bjt_Is, bjt_n) for v in vbe] ic_ideal_mA = eee51.scale_vec(ic_ideal, g51.milli) # get the value of vbe that will result in a 1mA ic using the simulation results index_spec_sim, ic_spec_sim = eee51.find_in_data(ic, specs['ic']) vbe_spec_sim = vbe[index_spec_sim] vbe_spec_sim_mV = vbe_spec_sim/g51.milli # get the value of vbe that will result in a 1mA ic using the our model index_spec_ideal, ic_spec_ideal = eee51.find_in_data(ic_ideal, specs['ic']) vbe_spec_ideal = vbe[index_spec_ideal] vbe_spec_ideal_mV = vbe_spec_ideal/g51.milli plt_cfg = { 'grid_linestyle' : 'dotted', 'title' : 'BJT 2N2222A Transfer Characteristics', 'xlabel' : r'$V_{BE}$ [mV]', 'ylabel' : r'$I_C$ [mA]', 'legend_loc' : 'upper left', 'add_legend' : True,
vx = [] ic1 = [] ic2 = [] itail = [] with open(dm_file, 'r') as f: for line in f: vid.append(float(line.split()[0])) vop.append(float(line.split()[1])) von.append(float(line.split()[3])) vx.append(float(line.split()[5])) ic1.append(float(line.split()[7])) ic2.append(float(line.split()[9])) itail.append(float(line.split()[11])) index, val = eee51.find_in_data(vid, 0) # define the plot parameters plt_cfg = { 'grid_linestyle' : 'dotted', 'title' : r'Differential Amplifier ($V_{IC}=$' + \ '{:.1f}V)'.format(vic_value), 'xlabel' : r'$V_{id}$ [mV]', 'ylabel' : r'Voltage [V]', 'legend_loc' : 'upper right', 'add_legend' : False, 'legend_title' : None } # plot the amplifier transfer characteristics fig = plt.figure()
# this was done here for convenience eee51.run_spice(cfg) vid = [] vo1 = [] vo2 = [] vo3 = [] with open(cfg['transfer_sim'], 'r') as f: for line in f: vid.append(float(line.split()[0])) vo1.append(float(line.split()[1])) vo2.append(float(line.split()[3])) vo3.append(float(line.split()[5])) index, vin_sim = eee51.find_in_data(vid, 0.0) indexp, vin_simp = eee51.find_in_data(vid, 100e-6) indexn, vin_simn = eee51.find_in_data(vid, -100e-6) # define the plot parameters plt_cfg = { 'grid_linestyle': 'dotted', 'title': r'3-Stage Op-Amp DC Transfer Curve', 'xlabel': r'$v_{id}$ [$\mu$V]', 'ylabel': r'Voltage [V]', 'legend_loc': 'lower left', 'add_legend': True, 'legend_title': None } fig = plt.figure()
return H, H_mag, H_phase T, T_mag, T_phase = TransferFunction(ifb, ierr) Rm, Rm_mag, Rm_phase = TransferFunction(vo1, ierr) F, F_mag, F_phase = TransferFunction(ifb, vo1) Finv, Finv_mag, Finv_phase = TransferFunction(vo1, ifb) Rcl, Rcl_mag, Rcl_pgase = TransferFunction(vo3, iin3) popt, pcov = curve_fit(fmag_2p1z, freq_rad, \ 20*np.log10( T_mag ), \ p0=guess) To_dB_calc = 20 * np.log10(popt[0]) index_To0dB, To0dB = eee51.find_in_data(20 * np.log10(T_mag), 0) fu = freq[index_To0dB] dTdf = np.diff(20 * np.log10(T_mag)) / np.diff(np.log10(freq_rad)) PM = T_phase[index_To0dB] - (-180) # define the plot parameters plt_cfg = { 'grid_linestyle': 'dotted', 'title': r'Shunt Amplifier Loop Gain (PM = {:.1f} deg)'.format(PM), 'xlabel': r'Frequency [Hz]', 'ylabel': r'Magnitude [dB]', 'legend_loc': 'upper right', 'add_legend': True, 'legend_title': None