class PlotPZ(QtGui.QMainWindow): def __init__(self, parent=None, DEBUG=False): # default parent = None -> top Window super(PlotPZ, self).__init__(parent) # initialize QWidget base class # QtGui.QMainWindow.__init__(self) # alternative syntax self.DEBUG = DEBUG self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) self.mplwidget = MplWidget() self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) self.initAxes() self.draw() # calculate and draw phi(f) # #============================================= # # Signals & Slots # #============================================= # self.btnWrap.clicked.connect(self.draw) # self.cmbUnitsPhi.currentIndexChanged.connect(self.draw) def initAxes(self): """Initialize and clear the axes """ # self.ax = self.mplwidget.ax self.ax = self.mplwidget.fig.add_subplot(111) self.ax.clear() def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_pz() def draw_pz(self): """ (re)draw P/Z plot """ zpk = fb.fil[0]['zpk'] self.ax.clear() [z, p, k] = pyfda_lib.zplane(self.ax, zpk, verbose=False) self.ax.set_title(r'Pole / Zero Plot') self.ax.set_xlabel('Real axis') self.ax.set_ylabel('Imaginary axis') self.ax.axis([-1.1, 1.1, -1.1, 1.1]) self.mplwidget.redraw()
class PlotPZ(QtGui.QMainWindow): def __init__(self, parent = None, DEBUG = False): # default parent = None -> top Window super(PlotPZ, self).__init__(parent) # initialize QWidget base class # QtGui.QMainWindow.__init__(self) # alternative syntax self.DEBUG = DEBUG self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) self.mplwidget = MplWidget() self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) self.initAxes() self.draw() # calculate and draw phi(f) # #============================================= # # Signals & Slots # #============================================= # self.btnWrap.clicked.connect(self.draw) # self.cmbUnitsPhi.currentIndexChanged.connect(self.draw) def initAxes(self): """Initialize and clear the axes """ # self.ax = self.mplwidget.ax self.ax = self.mplwidget.fig.add_subplot(111) self.ax.clear() def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_pz() def draw_pz(self): """ (re)draw P/Z plot """ zpk = fb.fil[0]['zpk'] self.ax.clear() [z, p, k] = pyfda_lib.zplane(self.ax, zpk, verbose = False) self.ax.set_title(r'Pole / Zero Plot') self.ax.set_xlabel('Real axis') self.ax.set_ylabel('Imaginary axis') self.ax.axis([-1.1, 1.1, -1.1, 1.1]) self.mplwidget.redraw()
class PlotPhi(QtGui.QMainWindow): def __init__(self, parent=None, DEBUG=False): # default parent = None -> top Window super(PlotPhi, self).__init__(parent) # initialize QWidget base class self.DEBUG = DEBUG self.cmbUnitsPhi = QtGui.QComboBox(self) units = ["rad", "rad/pi", "deg"] scales = [1., 1. / np.pi, 180. / np.pi] for unit, scale in zip(units, scales): self.cmbUnitsPhi.addItem(unit, scale) self.cmbUnitsPhi.setObjectName("cmbUnitsA") self.cmbUnitsPhi.setToolTip("Set unit for phase.") self.cmbUnitsPhi.setCurrentIndex(0) self.cmbUnitsPhi.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.lblWrap = QtGui.QLabel("Wrapped Phase") self.btnWrap = QtGui.QCheckBox() self.btnWrap.setChecked(False) self.btnWrap.setToolTip("Plot phase wrapped to +/- pi") self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) self.layHChkBoxes.addWidget(self.cmbUnitsPhi) self.layHChkBoxes.addWidget(self.lblWrap) self.layHChkBoxes.addWidget(self.btnWrap) self.layHChkBoxes.addStretch(10) self.mplwidget = MplWidget() # self.mplwidget.setParent(self) self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # self.mplwidget.setFocus() # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) self.initAxes() self.draw() # calculate and draw phi(f) # #============================================= # # Signals & Slots # #============================================= # self.mplwidget.sldLw.valueChanged.connect(lambda:self.draw()) self.btnWrap.clicked.connect(self.draw) self.cmbUnitsPhi.currentIndexChanged.connect(self.draw) def initAxes(self): """Initialize and clear the axes """ # self.ax = self.mplwidget.ax self.ax = self.mplwidget.fig.add_subplot(111) self.ax.clear() self.ax.hold(False) def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_phi() def draw_phi(self): """ Re-calculate phi(f) and draw the figure """ self.unitPhi = self.cmbUnitsPhi.currentText() self.bb = fb.fil[0]['ba'][0] self.aa = fb.fil[0]['ba'][1] if self.DEBUG: print("--- plotPhi.draw() ---") print("b,a = ", self.bb, self.aa) wholeF = fb.fil[0]['freqSpecsRangeType'] != 'half' f_S = fb.fil[0]['f_S'] [W, H] = sig.freqz(self.bb, self.aa, worN=fb.gD['N_FFT'], whole=wholeF) F = W / (2 * np.pi) * f_S if fb.fil[0]['freqSpecsRangeType'] == 'sym': H = np.fft.fftshift(H) F = F - f_S / 2. # scale = self.cmbUnitsPhi.itemData(self.cmbUnitsPhi.currentIndex()) y_str = r'$\angle H(\mathrm{e}^{\mathrm{j} \Omega})$' if self.unitPhi == 'rad': y_str += ' in rad ' + r'$\rightarrow $' scale = 1. elif self.unitPhi == 'rad/pi': y_str += ' in rad' + r'$ / \pi \;\rightarrow $' scale = 1. / np.pi else: y_str += ' in deg ' + r'$\rightarrow $' scale = 180. / np.pi fb.fil[0]['plt_phiLabel'] = y_str fb.fil[0]['plt_phiUnit'] = self.unitPhi if self.btnWrap.isChecked(): phi_plt = np.angle(H) * scale else: phi_plt = np.unwrap(np.angle(H)) * scale self.ax.clear() #--------------------------------------------------------- line_phi, = self.ax.plot(F, phi_plt, lw=fb.gD['rc']['lw']) #--------------------------------------------------------- self.ax.set_title(r'Phase Frequency Response') self.ax.set_xlabel(fb.fil[0]['plt_fLabel']) self.ax.set_ylabel(y_str) self.ax.set_xlim(fb.fil[0]['freqSpecsRange']) self.mplwidget.redraw()
class PlotTauG(QtGui.QMainWindow): def __init__(self, parent = None, DEBUG = False): # default parent = None -> top Window super(PlotTauG, self).__init__(parent) # initialize QWidget base class # QtGui.QMainWindow.__init__(self) # alternative syntax self.DEBUG = DEBUG # # self.lblWrap = QtGui.QLabel("Wrapped Phase") # self.btnWrap = QtGui.QCheckBox() # self.btnWrap.setChecked(False) # self.btnWrap.setToolTip("Plot phase wrapped to +/- pi") self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) # self.layHChkBoxes.addWidget(self.cmbUnitsPhi) self.mplwidget = MplWidget() # self.mplwidget.setParent(self) self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # self.mplwidget.setFocus() # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) self.initAxes() self.draw() # calculate and draw phi(f) # #============================================= # # Signals & Slots # #============================================= # self.btnWrap.clicked.connect(self.draw) # self.cmbUnitsPhi.currentIndexChanged.connect(self.draw) def initAxes(self): """Initialize and clear the axes """ # self.ax = self.mplwidget.ax self.ax = self.mplwidget.fig.add_subplot(111) self.ax.clear() self.ax.set_title(r'Group Delay $ \tau_g$') self.ax.hold(False) #plt.gca().cla() #p.clf() def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_taug() def draw_taug(self): """ Draw group delay """ bb = fb.fil[0]['ba'][0] aa = fb.fil[0]['ba'][1] wholeF = fb.fil[0]['freqSpecsRangeType'] != 'half' f_S = fb.fil[0]['f_S'] # scale = self.cmbUnitsPhi.itemData(self.cmbUnitsPhi.currentIndex()) [tau_g, w] = pyfda_lib.grpdelay(bb,aa, fb.gD['N_FFT'], whole = wholeF) F = w / (2 * np.pi) * fb.fil[0]['f_S'] if fb.fil[0]['freqSpecsRangeType'] == 'sym': tau_g = np.fft.fftshift(tau_g) F = F - f_S / 2. self.ax.plot(F, tau_g, lw = fb.gD['rc']['lw'], label = "Group Delay") self.ax.set_xlabel(fb.fil[0]['plt_fLabel']) self.ax.set_ylabel(r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega}) / T_S \; \rightarrow $') # widen limits to suppress numerical inaccuracies when tau_g = constant self.ax.axis(fb.fil[0]['freqSpecsRange'] + [max(min(tau_g)-0.5,0), max(tau_g) + 0.5]) self.mplwidget.redraw()
class PlotHf(QtGui.QMainWindow): # TODO: inset plot should have useful preset range, depending on filter type, # stop band or pass band should be selectable as well as lin / log scale # TODO: position and size of inset plot should be selectable def __init__(self, parent = None, DEBUG = False): # default parent = None -> top Window super(PlotHf, self).__init__(parent) # initialize QWidget base class # QtGui.QMainWindow.__init__(self) # alternative syntax self.DEBUG = DEBUG modes = ['| H |', 're{H}', 'im{H}'] self.cmbShowH = QtGui.QComboBox(self) self.cmbShowH.addItems(modes) self.cmbShowH.setObjectName("cmbUnitsH") self.cmbShowH.setToolTip("Show magnitude, real / imag. part of H or H \n" "without linear phase (acausal system).") self.cmbShowH.setCurrentIndex(0) self.lblIn = QtGui.QLabel("in") units = ["dB", "V", "W"] self.cmbUnitsA = QtGui.QComboBox(self) self.cmbUnitsA.addItems(units) self.cmbUnitsA.setObjectName("cmbUnitsA") self.cmbUnitsA.setToolTip("Set unit for y-axis:\n" "dB is attenuation (positive values)\nV and W are less than 1.") self.cmbUnitsA.setCurrentIndex(0) self.cmbShowH.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.cmbUnitsA.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.lblLinphase = QtGui.QLabel("Acausal system") self.chkLinphase = QtGui.QCheckBox() self.chkLinphase.setToolTip("Remove linear phase according to filter order.\n" "Attention: this makes no sense for a non-linear phase system!") self.lblInset = QtGui.QLabel("Inset") self.cmbInset = QtGui.QComboBox(self) self.cmbInset.addItems(['off', 'edit', 'fixed']) self.cmbInset.setObjectName("cmbInset") self.cmbInset.setToolTip("Display/edit second inset plot") self.cmbInset.setCurrentIndex(0) self.inset_idx = 0 # store previous index for comparison self.lblSpecs = QtGui.QLabel("Show Specs") self.chkSpecs = QtGui.QCheckBox() self.chkSpecs.setChecked(False) self.chkSpecs.setToolTip("Display filter specs as hatched regions") self.lblPhase = QtGui.QLabel("Phase") self.chkPhase = QtGui.QCheckBox() self.chkPhase.setToolTip("Overlay phase") self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) self.layHChkBoxes.addWidget(self.cmbShowH) self.layHChkBoxes.addWidget(self.lblIn) self.layHChkBoxes.addWidget(self.cmbUnitsA) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblLinphase) self.layHChkBoxes.addWidget(self.chkLinphase) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblInset) self.layHChkBoxes.addWidget(self.cmbInset) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblSpecs) self.layHChkBoxes.addWidget(self.chkSpecs) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblPhase) self.layHChkBoxes.addWidget(self.chkPhase) self.layHChkBoxes.addStretch(10) self.mplwidget = MplWidget() # self.mplwidget.setParent(self) self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # self.mplwidget.layVMainMpl1.addWidget(self.mplwidget) # self.mplwidget.setFocus() # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) self.initAxes() self.draw() # calculate and draw |H(f)| # #============================================= # # Signals & Slots # #============================================= self.cmbUnitsA.currentIndexChanged.connect(self.draw) self.cmbShowH.currentIndexChanged.connect(self.draw) self.chkLinphase.clicked.connect(self.draw) self.cmbInset.currentIndexChanged.connect(self.draw_inset) self.chkSpecs.clicked.connect(self.draw) self.chkPhase.clicked.connect(self.draw_phase) def initAxes(self): """Initialize and clear the axes """ # self.ax = self.mplwidget.ax self.ax = self.mplwidget.fig.add_subplot(111) self.ax.clear() def plotSpecLimits(self, specAxes): """ Plot the specifications limits (F_SB, A_SB, ...) as lines and as hatched areas. """ # fc = (0.8,0.8,0.8) # color for shaded areas fill_params = {'facecolor':'none','hatch':'/', 'edgecolor':'k', 'lw':0.0} line_params = {'linewidth':1.0, 'color':'blue', 'linestyle':'--'} ax = specAxes # extract from filterTree the parameters that are actually used # myParams = fb.filTree[rt][ft][dm][fo]['par'] # freqParams = [l for l in myParams if l[0] == 'F'] if fb.fil[0]['ft'] == "FIR": A_PB_max = self.A_PB # 20*log10(1+del_PB) A_PB2_max = self.A_PB2 else: # IIR log A_PB_max = A_PB2_max = 0 if self.unitA == 'V': dBMul = 20. elif self.unitA == 'W': dBMul = 10. if self.unitA == 'dB': A_PB_min = -self.A_PB A_PB2_min = -self.A_PB2 A_PB_minx = min(A_PB_min, A_PB2_min) - 10# 20*log10(1-del_PB) A_SB = -self.A_SB A_SB2 = -self.A_SB2 A_SBx = A_SB - 10 else: A_PB_max = 10**(A_PB_max/dBMul)# 1 + del_PB A_PB2_max = 10**(A_PB2_max/dBMul)# 1 + del_PB A_PB_min = 10**(-self.A_PB/dBMul) #1 - del_PB A_PB2_min = 10**(-self.A_PB2/dBMul) #1 - del_PB A_PB_minx = A_PB_min / 2 A_SB = 10**(-self.A_SB/dBMul) A_SB2 = 10**(-self.A_SB2/dBMul) A_SBx = A_SB / 5 F_max = self.f_S/2 F_PB = self.F_PB F_SB = fb.fil[0]['F_SB'] * self.f_S F_SB2 = fb.fil[0]['F_SB2'] * self.f_S F_PB2 = fb.fil[0]['F_PB2'] * self.f_S y_min = A_PB_minx y_max = ax.get_ylim()[1] F_lim_lor = [] A_lim_lor = [] if fb.fil[0]['rt'] == 'LP': F_lim_up = [0, F_SB, F_SB, F_max] A_lim_up = [A_PB_max, A_PB_max, A_SB, A_SB] F_lim_lo = [0, F_PB, F_PB] A_lim_lo = [A_PB_min, A_PB_min, A_PB_minx] if fb.fil[0]['rt'] == 'HP': F_lim_up = [0, F_SB, F_SB, F_max] A_lim_up = [A_SB, A_SB, A_PB_max, A_PB_max] F_lim_lo = [F_PB, F_PB, F_max] A_lim_lo = [A_PB_minx, A_PB_min, A_PB_min] if fb.fil[0]['rt'] == 'BS': F_lim_up = [0, F_SB, F_SB, F_SB2, F_SB2, F_max] A_lim_up = [A_PB_max, A_PB_max, A_SB, A_SB, A_PB2_max, A_PB2_max] # lower limits left: F_lim_lo = [0, F_PB, F_PB] A_lim_lo = [A_PB_min, A_PB_min, A_PB_minx] # lower limits right: F_lim_lor = [F_PB2, F_PB2, F_max] A_lim_lor = [A_PB_minx, A_PB2_min, A_PB2_min] if fb.fil[0]['rt'] in {"BP", "HIL"}: F_lim_up = [0, F_SB, F_SB, F_SB2, F_SB2, F_max] A_lim_up = [A_SB, A_SB, A_PB_max, A_PB_max, A_SB2, A_SB2] F_lim_lo = [F_PB, F_PB, F_PB2, F_PB2] A_lim_lo = [A_PB_minx, A_PB_min, A_PB_min, A_PB_minx] F_lim_up = np.array(F_lim_up) F_lim_lo = np.array(F_lim_lo) F_lim_lor = np.array(F_lim_lor) # upper limits: ax.plot(F_lim_up, A_lim_up, **line_params) ax.fill_between(F_lim_up, y_max, A_lim_up, **fill_params) # lower limits: ax.plot(F_lim_lo, A_lim_lo, F_lim_lor, A_lim_lor, **line_params) ax.fill_between(F_lim_lo, y_min, A_lim_lo, **fill_params) ax.fill_between(F_lim_lor, y_min, A_lim_lor, **fill_params) if fb.fil[0]['freqSpecsRangeType'] != 'half': # frequency axis +/- f_S/2 # plot limits for other half of the spectrum if fb.fil[0]['freqSpecsRangeType'] == 'sym': # frequency axis +/- f_S/2 F_lim_up = -F_lim_up F_lim_lo = -F_lim_lo F_lim_lor = -F_lim_lor else: # -> 'whole' F_lim_up = self.f_S - F_lim_up F_lim_lo = self.f_S - F_lim_lo F_lim_lor = self.f_S - F_lim_lor # upper limits: ax.plot(F_lim_up, A_lim_up, **line_params) ax.fill_between(F_lim_up, y_max, A_lim_up, **fill_params) # lower limits: ax.plot(F_lim_lo, A_lim_lo, F_lim_lor, A_lim_lor, **line_params) ax.fill_between(F_lim_lo, y_min, A_lim_lo, **fill_params) ax.fill_between(F_lim_lor, y_min, A_lim_lor, **fill_params) def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_hf() def draw_hf(self): """ Re-calculate |H(f)| and draw the figure """ self.unitA = self.cmbUnitsA.currentText() # Linphase settings only makes sense for amplitude plot self.chkLinphase.setCheckable(self.unitA == 'V') self.chkLinphase.setEnabled(self.unitA == 'V') self.lblLinphase.setEnabled(self.unitA == 'V') self.specs = self.chkSpecs.isChecked() self.phase = self.chkPhase.isChecked() self.linphase = self.chkLinphase.isChecked() self.bb = fb.fil[0]['ba'][0] self.aa = fb.fil[0]['ba'][1] self.f_S = fb.fil[0]['f_S'] self.F_PB = fb.fil[0]['F_PB'] * self.f_S self.F_SB = fb.fil[0]['F_SB'] * self.f_S self.A_PB = fb.fil[0]['A_PB'] self.A_PB2 = fb.fil[0]['A_PB2'] self.A_SB = fb.fil[0]['A_SB'] self.A_SB2 = fb.fil[0]['A_SB2'] f_lim = fb.fil[0]['freqSpecsRange'] wholeF = fb.fil[0]['freqSpecsRangeType'] != 'half' if self.DEBUG: print("--- plotHf.draw() --- ") print("b, a = ", self.bb, self.aa) # calculate H_c(W) (complex) for W = 0 ... pi: [W, self.H_c] = sig.freqz(self.bb, self.aa, worN = fb.gD['N_FFT'], whole = wholeF) self.F = W / (2 * np.pi) * self.f_S if fb.fil[0]['freqSpecsRangeType'] == 'sym': self.H_c = np.fft.fftshift(self.H_c) self.F = self.F - self.f_S/2. if self.linphase: # remove the linear phase self.H_c = self.H_c * np.exp(1j * W * fb.fil[0]["N"]/2.) if self.cmbShowH.currentIndex() == 0: # show magnitude of H H = abs(self.H_c) H_str = r'$|H(\mathrm{e}^{\mathrm{j} \Omega})|$' elif self.cmbShowH.currentIndex() == 1: # show real part of H H = self.H_c.real H_str = r'$\Re \{H(\mathrm{e}^{\mathrm{j} \Omega})\}$' else: # show imag. part of H H = self.H_c.imag H_str = r'$\Im \{H(\mathrm{e}^{\mathrm{j} \Omega})\}$' # clear the axes and (re)draw the plot # if self.ax.get_navigate(): self.ax.clear() #================ Main Plotting Routine ========================= if self.unitA == 'dB': A_lim = [-self.A_SB -10, self.A_PB +1] self.H_plt = 20*np.log10(abs(H)) H_str += ' in dB ' + r'$\rightarrow$' elif self.unitA == 'V': # 'lin' A_lim = [10**((-self.A_SB-10)/20), 10**((self.A_PB+1)/20)] self.H_plt = H H_str +=' in V ' + r'$\rightarrow $' self.ax.axhline(linewidth=1, color='k') # horizontal line at 0 else: # unit is W A_lim = [10**((-self.A_SB-10)/10), 10**((self.A_PB+0.5)/10)] self.H_plt = H * H.conj() H_str += ' in W ' + r'$\rightarrow $' plt_lim = f_lim + A_lim #----------------------------------------------------------- self.ax.plot(self.F, self.H_plt, lw = fb.gD['rc']['lw'], label = 'H(f)') #----------------------------------------------------------- self.ax_bounds = [self.ax.get_ybound()[0], self.ax.get_ybound()[1]]#, self.ax.get] self.ax.axis(plt_lim) if self.specs: self.plotSpecLimits(specAxes = self.ax) self.ax.set_title(r'Magnitude Frequency Response') self.ax.set_xlabel(fb.fil[0]['plt_fLabel']) self.ax.set_ylabel(H_str) self.mplwidget.redraw() def draw_phase(self): self.phase = self.chkPhase.isChecked() if self.phase: self.ax_p = self.ax.twinx() # second axes system with same x-axis for phase phi_str = r'$\angle H(\mathrm{e}^{\mathrm{j} \Omega})$' if fb.fil[0]['plt_phiUnit'] == 'rad': phi_str += ' in rad ' + r'$\rightarrow $' scale = 1. elif fb.fil[0]['plt_phiUnit'] == 'rad/pi': phi_str += ' in rad' + r'$ / \pi \;\rightarrow $' scale = 1./ np.pi else: phi_str += ' in deg ' + r'$\rightarrow $' scale = 180./np.pi #----------------------------------------------------------- self.ax_p.plot(self.F,np.unwrap(np.angle(self.H_c))*scale, 'b--', lw = fb.gD['rc']['lw'], label = "Phase") #----------------------------------------------------------- self.ax_p.set_ylabel(phi_str, color='blue') nbins = len(self.ax.get_yticks()) # number of ticks on main y-axis # http://stackoverflow.com/questions/28692608/align-grid-lines-on-two-plots # http://stackoverflow.com/questions/3654619/matplotlib-multiple-y-axes-grid-lines-applied-to-both # http://stackoverflow.com/questions/20243683/matplotlib-align-twinx-tick-marks # manual setting: #self.ax_p.set_yticks( np.linspace(self.ax_p.get_ylim()[0],self.ax_p.get_ylim()[1],nbins) ) #ax1.set_yticks(np.linspace(ax1.get_ybound()[0], ax1.get_ybound()[1], 5)) #ax2.set_yticks(np.linspace(ax2.get_ybound()[0], ax2.get_ybound()[1], 5)) #http://stackoverflow.com/questions/3654619/matplotlib-multiple-y-axes-grid-lines-applied-to-both # use helper functions from matplotlib.ticker: # MaxNLocator: set no more than nbins + 1 ticks #self.ax_p.yaxis.set_major_locator( matplotlib.ticker.MaxNLocator(nbins = nbins) ) # further options: integer = False, # prune = [‘lower’ | ‘upper’ | ‘both’ | None] Remove edge ticks # AutoLocator: #self.ax_p.yaxis.set_major_locator( matplotlib.ticker.AutoLocator() ) # LinearLocator: #self.ax_p.yaxis.set_major_locator( matplotlib.ticker.LinearLocator(numticks = nbins -1 ) ) # self.ax_p.locator_params(axis = 'y', nbins = nbins) # # self.ax_p.set_yticks(np.linspace(self.ax_p.get_ybound()[0], # self.ax_p.get_ybound()[1], # len(self.ax.get_yticks())-1)) #N = source_ax.xaxis.get_major_ticks() #target_ax.xaxis.set_major_locator(LinearLocator(N)) else: try: self.mplwidget.fig.delaxes(self.ax_p) except (KeyError, AttributeError): pass self.draw() def draw_inset(self): """ Construct / destruct second axes for an inset second plot """ # TODO: try ax1 = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6 # TODO: choose size & position of inset, maybe dependent on filter type # or specs (i.e. where is passband etc.) if self.DEBUG: print(self.cmbInset.currentIndex(), self.mplwidget.fig.axes) # list of axes in Figure for ax in self.mplwidget.fig.axes: print(ax) print("cmbInset, inset_idx:",self.cmbInset.currentIndex(), self.inset_idx) if self.cmbInset.currentIndex() > 0: if self.inset_idx == 0: # Inset was turned off before, create a new one # Add an axes at position rect [left, bottom, width, height]: self.ax_i = self.mplwidget.fig.add_axes([0.65, 0.61, .3, .3]) self.ax_i.clear() # clear old plot and specs # draw an opaque background with the extent of the inset plot: # self.ax_i.patch.set_facecolor('green') # without label area # self.mplwidget.fig.patch.set_facecolor('green') # whole figure extent = self.mplwidget.full_extent(self.ax_i, pad = 0.0) # Transform this back to figure coordinates - otherwise, it # won't behave correctly when the size of the plot is changed: extent = extent.transformed(self.mplwidget.fig.transFigure.inverted()) rect = Rectangle((extent.xmin, extent.ymin), extent.width, extent.height, facecolor=(1.0,1.0,1.0), edgecolor='none', transform=self.mplwidget.fig.transFigure, zorder=-1) self.ax_i.patches.append(rect) self.ax_i.set_xlim(fb.fil[0]['freqSpecsRange']) self.ax_i.plot(self.F, self.H_plt, lw = fb.gD['rc']['lw']) if self.cmbInset.currentIndex() == 1: # edit / navigate inset self.ax_i.set_navigate(True) self.ax.set_navigate(False) if self.specs: self.plotSpecLimits(specAxes = self.ax_i) else: # edit / navigate main plot self.ax_i.set_navigate(False) self.ax.set_navigate(True) else: # inset has been turned off, delete it self.ax.set_navigate(True) try: #remove ax_i from the figure self.mplwidget.fig.delaxes(self.ax_i) except AttributeError: pass self.inset_idx = self.cmbInset.currentIndex() # update index self.draw()
class Plot3D(QtGui.QMainWindow): """ Class for various 3D-plots: - lin / log line plot of H(f) - lin / log surf plot of H(z) - optional display of poles / zeros """ def __init__(self, parent = None, DEBUG = False): # default parent = None -> top Window super(Plot3D, self).__init__(parent) # initialize QWidget base class # QtGui.QMainWindow.__init__(self) # alternative syntax self.DEBUG = DEBUG self.zmin = 0 self.zmax = 4 self.zmin_dB = -80 self.lblLog = QtGui.QLabel("Log.") self.chkLog = QtGui.QCheckBox(self) self.chkLog.setObjectName("chkLog") self.chkLog.setToolTip("Logarithmic scale") self.chkLog.setChecked(False) self.lblBottom = QtGui.QLabel("Bottom:") self.ledBottom = QtGui.QLineEdit(self) self.ledBottom.setObjectName("ledBottom") self.ledBottom.setText(str(self.zmin)) self.ledBottom.setToolTip("Minimum display value.") self.lblTop = QtGui.QLabel("Top:") self.ledTop = QtGui.QLineEdit(self) self.ledTop.setObjectName("ledTop") self.ledTop.setText(str(self.zmax)) self.ledTop.setToolTip("Maximum display value.") # self.ledTop.setSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum) self.lblUC = QtGui.QLabel(self) self.lblUC.setText("UC") self.chkUC = QtGui.QCheckBox(self) self.chkUC.setObjectName("chkUC") self.chkUC.setToolTip("Plot unit circle") self.chkUC.setChecked(True) self.lblPZ = QtGui.QLabel(self) self.lblPZ.setText("P/Z") self.chkPZ = QtGui.QCheckBox(self) self.chkPZ.setObjectName("chkPZ") self.chkPZ.setToolTip("Plot poles and zeros") self.chkPZ.setChecked(True) self.lblHf = QtGui.QLabel(self) self.lblHf.setText("H(f)") self.chkHf = QtGui.QCheckBox(self) self.chkHf.setObjectName("chkHf") self.chkHf.setToolTip("Plot H(f) along the unit circle") self.chkHf.setChecked(True) modes = ['None', 'Mesh', 'Surf', 'Contour'] self.cmbMode3D = QtGui.QComboBox(self) self.cmbMode3D.addItems(modes) self.cmbMode3D.setObjectName("cmbShow3D") self.cmbMode3D.setToolTip("Select 3D-plot mode.") self.cmbMode3D.setCurrentIndex(0) self.cmbMode3D.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.lblColBar = QtGui.QLabel(self) self.lblColBar.setText("Colorbar") self.chkColBar = QtGui.QCheckBox(self) self.chkColBar.setObjectName("chkColBar") self.chkColBar.setToolTip("Show colorbar") self.chkColBar.setChecked(False) self.diaAlpha = QtGui.QDial(self) self.diaAlpha.setRange(0.,10.) self.diaAlpha.setValue(5) self.diaAlpha.setTracking(False) # produce less events when turning self.diaAlpha.setFixedHeight(30) self.diaAlpha.setFixedWidth(30) self.diaAlpha.setWrapping(False) self.diaAlpha.setToolTip("Set transparency for surf and contour plot.") self.diaHatch = QtGui.QDial(self) self.diaHatch.setRange(0.,9.) self.diaHatch.setValue(5) self.diaHatch.setTracking(False) # produce less events when turning self.diaHatch.setFixedHeight(30) self.diaHatch.setFixedWidth(30) self.diaHatch.setWrapping(False) self.diaHatch.setToolTip("Set hatching for H(jw).") self.lblContour2D = QtGui.QLabel(self) self.lblContour2D.setText("Contour2D") self.chkContour2D = QtGui.QCheckBox(self) self.chkContour2D.setObjectName("chkContour2D") self.chkContour2D.setToolTip("Plot 2D-contours for real and imaginary part") self.chkContour2D.setChecked(False) self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) self.layHChkBoxes.addWidget(self.lblLog) self.layHChkBoxes.addWidget(self.chkLog) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblBottom) self.layHChkBoxes.addWidget(self.ledBottom) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblTop) self.layHChkBoxes.addWidget(self.ledTop) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblUC) self.layHChkBoxes.addWidget(self.chkUC) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblPZ) self.layHChkBoxes.addWidget(self.chkPZ) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblHf) self.layHChkBoxes.addWidget(self.chkHf) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.cmbMode3D) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblColBar) self.layHChkBoxes.addWidget(self.chkColBar) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.diaAlpha) self.layHChkBoxes.addWidget(self.diaHatch) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblContour2D) self.layHChkBoxes.addWidget(self.chkContour2D) self.layHChkBoxes.addStretch(1) self.mplwidget = MplWidget() # self.mplwidget.setParent(self) self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # self.mplwidget.setFocus() # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) self.initAxes() self.initCoord() self.draw() # calculate and draw phi(f) # #============================================= # # Signals & Slots # #============================================= self.chkLog.clicked.connect(self.logClicked) self.ledBottom.editingFinished.connect(self.logClicked) self.ledTop.editingFinished.connect(self.logClicked) self.chkUC.clicked.connect(self.draw) self.chkHf.clicked.connect(self.draw) self.chkPZ.clicked.connect(self.draw) self.cmbMode3D.currentIndexChanged.connect(self.draw) self.chkColBar.clicked.connect(self.draw) self.diaAlpha.valueChanged.connect(self.draw) self.diaHatch.valueChanged.connect(self.draw) self.chkContour2D.clicked.connect(self.draw) def initCoord(self): """ Initialize coordinates for the unit circle """ # TODO: move creation of x,y-grid here as well self.phi_EK = np.linspace(0, 2*pi, 400, endpoint = True) self.xy_UC = np.exp(1j * self.phi_EK) # x,y coordinates of unity circle def initAxes(self): """Initialize and clear the axes see http://stackoverflow.com/questions/4575588/matplotlib-3d-plot-with-pyqt4-in-qtabwidget-mplwidget """ self.mplwidget.fig.clf() # needed to get rid of colormap self.ax3d = self.mplwidget.fig.add_subplot(111, projection = '3d') def logClicked(self): """ Change scale and settings to log / lin """ self.log = self.chkLog.isChecked() if self.sender().objectName() == 'chkLog': # origin of signal that triggered the slot if self.log: self.ledBottom.setText(str(self.zmin_dB)) self.zmax_dB = np.round(20 * log10(self.zmax),2) self.ledTop.setText(str(self.zmax_dB)) else: self.ledBottom.setText(str(self.zmin)) self.zmax = np.round(10**(self.zmax_dB / 20),2) self.ledTop.setText(str(self.zmax)) else: if self.log: self.zmin_dB = float(self.ledBottom.text()) self.zmax_dB = float(self.ledTop.text()) else: self.zmin = float(self.ledBottom.text()) self.zmax = float(self.ledTop.text()) self.draw() def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_3d() def draw_3d(self): """ Draw various 3D plots """ self.initAxes() # needed to get rid of colormap bb = fb.fil[0]['ba'][0] aa = fb.fil[0]['ba'][1] zz = np.array(fb.fil[0]['zpk'][0]) pp = np.array(fb.fil[0]['zpk'][1]) wholeF = fb.fil[0]['freqSpecsRangeType'] != 'half' f_S = fb.fil[0]['f_S'] N_FFT = fb.gD['N_FFT'] alpha = self.diaAlpha.value()/10. #----------------------------------------------------------------------------- # Define 3D-Plotting Options #----------------------------------------------------------------------------- OPT_3D_POLAR_SPEC = True # Plot circular range in 3D-Plot OPT_3D_FORCE_ZMAX = True # Enforce absolute limit for 3D-Plot OPT_3D_MSTRIDE = 1 # Schrittweite für MESH und CONT3D OPT_3D_ALPHA = alpha#0.5 # Transparency for surface plot cmap = cm.jet # Colormaps: 'hsv', 'jet_r', 'bone', 'prism' 'gray', 'prism', 'coolwarm' # *_r colormaps reverse the color order # steps = 100 # number of steps for x, y, r, phi rmin = 0; rmax = 1.2 # polar range definition # xmin = -1.5; xmax = 1.5 # cartesian range definition ymin = -1.5; ymax = 1.5 # zmax_rel = 5 # Max. displayed z - value relative to max|H(f)| # Calculate limits etc. for 3D-Plots dr = rmax / steps * 2 dphi = pi / steps # grid size for polar range dx = (xmax - xmin) / steps dy = (ymax - ymin) / steps # grid size cartesian range if OPT_3D_POLAR_SPEC == True: # polar grid [r, phi] = np.meshgrid(np.arange(rmin, rmax, dr), np.linspace(0, 2 * pi, steps, endpoint = True)) x = r * cos(phi) y = r * sin(phi) else: # cartesian grid [x, y] = np.meshgrid(np.arange(xmin,xmax,dx), np.arange(ymin,ymax,dy)) z = x + 1j*y # create coordinate grid for complex plane [w, H] = sig.freqz(bb, aa, N_FFT, wholeF) # calculate H(w) along the # upper half of unity circle # w runs from 0 ... pi, length = N_FFT f = w / (2 * pi) * f_S # translate w to absolute frequencies H_abs = abs(H) H_max = max(H_abs) H_max_dB = 20*log10(H_max) F_max = f[np.argmax(H_abs)] H_min = min(H_abs) H_min_dB = 20*log10(H_min) F_min = f[np.argmin(H_abs)] plevel_rel = 1.05 # height of plotted pole position relative to zmax zlevel_rel = 0.1 # height of plotted zero position relative to zmax # calculate H(jw)| along the unity circle and |H(z)|, each clipped # between bottom and top if self.chkLog.isChecked(): bottom = np.floor(max(self.zmin_dB, H_min_dB) / 10) * 10 top = self.zmax_dB plevel_top = top + (top - bottom) * (plevel_rel - 1) plevel_btm = top zlevel = bottom - (top - bottom) * (zlevel_rel) H_UC = pyfda_lib.H_mag(bb, aa, self.xy_UC, top, H_min = bottom, log = True) Hmag = pyfda_lib.H_mag(bb, aa, z, top, H_min = bottom, log = True) else: bottom = max(self.zmin, H_min) top = self.zmax # top = zmax_rel * H_max # calculate display top from max. of H(f) H_UC = pyfda_lib.H_mag(bb, aa, self.xy_UC, top, H_min = bottom) Hmag = pyfda_lib.H_mag(bb, aa, z, top, H_min = bottom) zlevel = zlevel_rel * top # height of displayed zero position if self.cmbMode3D.currentText() == 'None': # "Poleposition" for H(f) plot only plevel_top = H_max * 0.3 # plevel = H_max * 0.1 / zlevel = 0.1 plevel_btm = bottom else: plevel_top = plevel_rel * top # height of displayed pole position plevel_btm = top #=============================================================== ## plot unit circle #=============================================================== if self.chkUC.isChecked(): # Plot unit circle and marker at (1,0): self.ax3d.plot(self.xy_UC.real, self.xy_UC.imag, ones(len(self.xy_UC)) * bottom, lw = 2, color = 'k') self.ax3d.plot([0.97, 1.03],[0,0], [bottom, bottom], lw = 2, color = 'k') #=============================================================== ## plot ||H(f)| along unit circle as 3D-lineplot #=============================================================== if self.chkHf.isChecked(): self.ax3d.plot(self.xy_UC.real, self.xy_UC.imag, H_UC, lw = fb.gD['rc']['lw']) self.ax3d.plot(self.xy_UC.real, self.xy_UC.imag, H_UC, 'w--', lw = fb.gD['rc']['lw']) NL = 10 - self.diaHatch.value() # plot line every NL points on the UC if NL < 10: for k in range(len(self.xy_UC[::NL])): self.ax3d.plot([self.xy_UC.real[::NL][k], self.xy_UC.real[::NL][k]], [self.xy_UC.imag[::NL][k], self.xy_UC.imag[::NL][k]], [np.ones(len(self.xy_UC[::NL]))[k]*bottom, H_UC[::NL][k]], linewidth=1, color=(0.5, 0.5, 0.5)) #=============================================================== ## plot Poles and Zeros #=============================================================== if self.chkPZ.isChecked(): PN_SIZE = 8 # size of P/N symbols # Plot zero markers at |H(z_i)| = zlevel with "stems": self.ax3d.plot(zz.real, zz.imag, ones(len(zz)) * zlevel, 'o', markersize = PN_SIZE, markeredgecolor='blue', markeredgewidth=2.0, markerfacecolor = 'none') for k in range(len(zz)): # plot zero "stems" self.ax3d.plot([zz[k].real, zz[k].real], [zz[k].imag, zz[k].imag], [bottom, zlevel], linewidth=1, color='b') # Plot the poles at |H(z_p)| = plevel with "stems": self.ax3d.plot(np.real(pp), np.imag(pp), plevel_top, 'x', markersize = PN_SIZE, markeredgewidth=2.0, markeredgecolor='red') for k in range(len(pp)): # plot pole "stems" self.ax3d.plot([pp[k].real, pp[k].real], [pp[k].imag, pp[k].imag], [plevel_btm, plevel_top], linewidth=1, color='r') #=============================================================== ## 3D-Plots of |H(z)| clipped between |H(z)| = top #=============================================================== # ## Mesh plot if self.cmbMode3D.currentText() == 'Mesh': # fig_mlab = mlab.figure(fgcolor=(0., 0., 0.), bgcolor=(1, 1, 1)) # self.ax3d.set_zlim(0,2) self.ax3d.plot_wireframe(x, y, Hmag, rstride=5, cstride=OPT_3D_MSTRIDE, linewidth = 1, color = 'gray') #--------------------------------------------------------------- ## 3D-surface plot; elif self.cmbMode3D.currentText() == 'Surf': s = self.ax3d.plot_surface(x, y, Hmag, alpha = OPT_3D_ALPHA, rstride=1, cstride=1, cmap = cmap, linewidth=0, antialiased=False, shade = True) # facecolors= cmap ?? s.set_edgecolor('gray') #--------------------------------------------------------------- ## 3D-Contour plot elif self.cmbMode3D.currentText() == 'Contour': s = self.ax3d.contourf3D(x, y, Hmag, 20, alpha = alpha, rstride=OPT_3D_MSTRIDE, cstride=OPT_3D_MSTRIDE, cmap = cmap) #--------------------------------------------------------------- ## 2D-Contour plot # TODO: 2D contour plots do not plot correctly together with 3D plots in # current matplotlib 1.4.3 -> disable them for now # TODO: zdir = x / y delivers unexpected results -> rather plot max(H) # along the other axis? # TODO: colormap is created depending on the zdir = 'z' contour plot # -> set limits of (all) other plots manually? if self.chkContour2D.isChecked(): # self.ax3d.contourf(x, y, Hmag, 20, zdir='x', offset=xmin, # cmap=cmap, alpha = alpha)#, vmin = bottom)#, vmax = top, vmin = bottom) # self.ax3d.contourf(x, y, Hmag, 20, zdir='y', offset=ymax, # cmap=cmap, alpha = alpha)#, vmin = bottom)#, vmax = top, vmin = bottom) s = self.ax3d.contourf(x, y, Hmag, 20, zdir='z', offset = bottom - (top - bottom) * 0.05, cmap=cmap, alpha = alpha) if self.cmbMode3D.currentText() in {'Contour', 'Surf'}\ or self.chkContour2D.isChecked(): if self.chkColBar.isChecked(): self.colb = self.mplwidget.fig.colorbar(s, ax = self.ax3d, shrink=0.8, aspect=20, pad = 0.02, fraction = 0.08) self.ax3d.set_xlim3d(xmin, xmax) self.ax3d.set_ylim3d(ymin, ymax) self.ax3d.set_zlim3d(bottom, top) self.ax3d.set_xlabel('Re')#(fb.fil[0]['plt_fLabel']) self.ax3d.set_ylabel('Im') #(r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega}) / T_S \; \rightarrow $') # self.ax3d.set_zlabel(r'$|H(z)|\; \rightarrow $') self.ax3d.set_title(r'3D-Plot of $|H(\mathrm{e}^{\mathrm{j} \Omega})|$ and $|H(z)|$') self.mplwidget.redraw()
class PlotImpz(QtGui.QMainWindow): def __init__(self, parent=None, DEBUG=False): # default parent = None -> top Window super(PlotImpz, self).__init__(parent) # initialize QWidget base class # QtGui.QMainWindow.__init__(self) # alternative syntax self.DEBUG = DEBUG self.ACTIVE_3D = False self.lblLog = QtGui.QLabel(self) self.lblLog.setText("Log.") self.chkLog = QtGui.QCheckBox(self) self.chkLog.setObjectName("chkLog") self.chkLog.setToolTip("Show logarithmic impulse / step response.") self.chkLog.setChecked(False) self.lblLogBottom = QtGui.QLabel("Log. bottom:") self.ledLogBottom = QtGui.QLineEdit(self) self.ledLogBottom.setText("-80") self.ledLogBottom.setToolTip("Minimum display value for log. scale.") self.lblNPoints = QtGui.QLabel("<i>N</i> =") self.ledNPoints = QtGui.QLineEdit(self) self.ledNPoints.setText("0") self.ledNPoints.setToolTip( "Number of points to calculate and display.\n" "N = 0 chooses automatically.") self.lblStep = QtGui.QLabel("Step Response") self.chkStep = QtGui.QCheckBox() self.chkStep.setChecked(False) self.chkStep.setToolTip( "Show step response instead of impulse response.") self.lblLockZoom = QtGui.QLabel("Lock Zoom") self.chkLockZoom = QtGui.QCheckBox() self.chkLockZoom.setChecked(False) self.chkLockZoom.setToolTip("Lock zoom to current setting.") self.layHChkBoxes = QtGui.QHBoxLayout() self.layHChkBoxes.addStretch(10) self.layHChkBoxes.addWidget(self.lblLog) self.layHChkBoxes.addWidget(self.chkLog) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblLogBottom) self.layHChkBoxes.addWidget(self.ledLogBottom) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblStep) self.layHChkBoxes.addWidget(self.chkStep) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblLockZoom) self.layHChkBoxes.addWidget(self.chkLockZoom) self.layHChkBoxes.addStretch(1) self.layHChkBoxes.addWidget(self.lblNPoints) self.layHChkBoxes.addWidget(self.ledNPoints) self.layHChkBoxes.addStretch(10) self.mplwidget = MplWidget() # self.mplwidget.setParent(self) self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes) # self.mplwidget.layVMainMpl1.addWidget(self.mplwidget) # self.mplwidget.setFocus() # make this the central widget, taking all available space: self.setCentralWidget(self.mplwidget) # self.setLayout(self.layHChkBoxes) self.draw() # calculate and draw |H(f)| # #============================================= # # Signals & Slots # #============================================= self.chkLog.clicked.connect(self.draw) self.chkStep.clicked.connect(self.draw) self.ledNPoints.editingFinished.connect(self.draw) self.ledLogBottom.editingFinished.connect(self.draw) def initAxes(self): # clear the axes and (re)draw the plot # try: self.mplwidget.fig.delaxes(self.ax_r) self.mplwidget.fig.delaxes(self.ax_i) except (KeyError, AttributeError, UnboundLocalError): pass if self.cmplx: self.ax_r = self.mplwidget.fig.add_subplot(211) self.ax_r.clear() self.ax_i = self.mplwidget.fig.add_subplot(212) self.ax_i.clear() else: self.ax_r = self.mplwidget.fig.add_subplot(111) self.ax_r.clear() if self.ACTIVE_3D: self.ax3d = Axes3D(fig) def draw(self): if self.mplwidget.mplToolbar.enable_update: self.draw_impz() def draw_impz(self): """ (Re-)calculate h[n] and draw the figure """ log = self.chkLog.isChecked() step = self.chkStep.isChecked() self.lblLogBottom.setEnabled(log) self.ledLogBottom.setEnabled(log) # if np.ndim(fb.fil[0]['coeffs']) == 1: # FIR self.bb = fb.fil[0]['ba'][0] self.aa = fb.fil[0]['ba'][1] self.f_S = fb.fil[0]['f_S'] self.F_PB = fb.fil[0]['F_PB'] * self.f_S self.F_SB = fb.fil[0]['F_SB'] * self.f_S self.A_PB = fb.fil[0]['A_PB'] self.A_PB2 = fb.fil[0]['A_PB2'] self.A_SB = fb.fil[0]['A_SB'] self.A_SB2 = fb.fil[0]['A_SB2'] if self.DEBUG: print("--- plotHf.draw() --- ") print("b, a = ", self.bb, self.aa) # calculate h[n] [h, t] = pyfda_lib.impz(self.bb, self.aa, self.f_S, step=step, N=int(self.ledNPoints.text())) if step: title_str = r'Step Response' H_str = r'$h_{\epsilon}[n]$' else: title_str = r'Impulse Response' H_str = r'$h[n]$' self.cmplx = np.any(np.iscomplex(h)) if self.cmplx: h_i = h.imag h = h.real H_i_str = r'$\Im\{$' + H_str + '$\}$' H_str = r'$\Re\{$' + H_str + '$\}$' if log: bottom = float(self.ledLogBottom.text()) H_str = r'$\log$ ' + H_str + ' in dB' h = np.maximum(20 * np.log10(abs(h)), bottom) if self.cmplx: h_i = np.maximum(20 * np.log10(abs(h_i)), bottom) H_i_str = r'$\log$ ' + H_i_str + ' in dB' else: bottom = 0 self.initAxes() #================ Main Plotting Routine ========================= [ml, sl, bl] = self.ax_r.stem(t, h, bottom=bottom, markerfmt='bo', linefmt='r') self.ax_r.set_xlim([min(t), max(t)]) self.ax_r.set_title(title_str) if self.cmplx: [ml_i, sl_i, bl_i] = self.ax_i.stem(t, h_i, bottom=bottom, markerfmt='rd', linefmt='b') self.ax_i.set_xlabel(fb.fil[0]['plt_tLabel']) self.ax_r.set_ylabel(H_str + r'$\rightarrow $') self.ax_i.set_ylabel(H_i_str + r'$\rightarrow $') else: self.ax_r.set_xlabel(fb.fil[0]['plt_tLabel']) self.ax_r.set_ylabel(H_str + r'$\rightarrow $') if self.ACTIVE_3D: # not implemented yet # plotting the stems for i in range(len(t)): self.ax3d.plot([t[i], t[i]], [h[i], h[i]], [0, h_i[i]], '-', linewidth=2, color='b', alpha=.5) # plotting a circle on the top of each stem self.ax3d.plot(t, h, h_i, 'o', markersize=8, markerfacecolor='none', color='b', label='ib') self.ax3d.set_xlabel('x') self.ax3d.set_ylabel('y') self.ax3d.set_zlabel('z') # fig.setp(ml, 'markerfacecolor', 'r', 'markersize', 8) # ax.setp(sl, lw = fb.gD['rc']['lw']) # print(self.mplwidget.plt_lim) # ax.axis(self.mplwidget.plt_lim) # if self.mplwidget.plt_lim == [] or not self.chkLockZoom.isChecked(): # # self.mplwidget.plt_lim = t_lim + y_lim # self.mplwidget.x_lim = t_lim self.mplwidget.redraw()