Exemplo n.º 1
0
    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)
        self.verbose = False  # suppress warnings

        # =============================================================================
        # #### not needed at the moment ###
        #         self.chkWarnings = QCheckBox("Enable Warnings", self)
        #         self.chkWarnings.setChecked(False)
        #         self.chkWarnings.setToolTip("Print warnings about singular group delay")
        #
        #         layHControls = QHBoxLayout()
        #         layHControls.addStretch(10)
        #         layHControls.addWidget(self.chkWarnings)
        #
        #         # This widget encompasses all control subwidgets:
        #         self.frmControls = QFrame(self)
        #         self.frmControls.setObjectName("frmControls")
        #         self.frmControls.setLayout(layHControls)
        #
        # =============================================================================
        self.mplwidget = MplWidget(self)
        #        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()
        self.draw()  # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)
Exemplo n.º 2
0
    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)


        self.chkWarnings = QCheckBox()
        self.chkWarnings.setText("Enable Warnings")
        self.chkWarnings.setChecked(False)
        self.chkWarnings.setToolTip("Print warnings about singular group delay")

        self.layHChkBoxes = QHBoxLayout()
        self.layHChkBoxes.addStretch(10)
        self.layHChkBoxes.addWidget(self.chkWarnings)

        self.mplwidget = MplWidget(self)
        
        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)
        
        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
#        self.setCentralWidget(self.mplwidget)

        
        self._init_axes()

        self.draw() # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkWarnings.clicked.connect(self.draw)
Exemplo n.º 3
0
    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)

        self.chkWarnings = QCheckBox("Enable Warnings", self)
        self.chkWarnings.setChecked(False)
        self.chkWarnings.setToolTip(
            "Print warnings about singular group delay")

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        layHControls.addWidget(self.chkWarnings)

        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.ax = self.mplwidget.fig.add_subplot(111)

        self.draw()  # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkWarnings.clicked.connect(self.draw)
Exemplo n.º 4
0
    def __init__(self, parent): 
        super(PlotPZ, self).__init__(parent)

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
#        self.frmControls = QFrame(self)
#        self.frmControls.setLayout(layHControls)


        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addLayout(layHControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
 #       self.setCentralWidget(self.mplwidget)
        
        self._init_axes()

        self.draw() # calculate and draw poles and zeros
Exemplo n.º 5
0
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 poles and zeros

#        #=============================================
#        # Signals & Slots
#        #=============================================
#        self.btnWhatever.clicked.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] = 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()
Exemplo n.º 6
0
    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)
        self.verbose = False # suppress warnings

# =============================================================================
# #### not needed at the moment ###
#         self.chkWarnings = QCheckBox("Enable Warnings", self)
#         self.chkWarnings.setChecked(False)
#         self.chkWarnings.setToolTip("Print warnings about singular group delay")
# 
#         layHControls = QHBoxLayout()
#         layHControls.addStretch(10)
#         layHControls.addWidget(self.chkWarnings)
#         
#         # This widget encompasses all control subwidgets:
#         self.frmControls = QFrame(self)
#         self.frmControls.setObjectName("frmControls")
#         self.frmControls.setLayout(layHControls)
# 
# =============================================================================
        self.mplwidget = MplWidget(self)
#        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()        
        self.draw() # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)
Exemplo n.º 7
0
    def __init__(self, parent): 
        super(PlotPZ, self).__init__(parent)

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
#        self.frmControls = QFrame(self)
#        self.frmControls.setLayout(layHControls)


        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addLayout(layHControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
 #       self.setCentralWidget(self.mplwidget)
        
        self.init_axes()

        self.draw() # calculate and draw poles and zeros

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
    def __init__(self, parent):
        super(PlotPhi, self).__init__(parent)

        self.cmbUnitsPhi = 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(QComboBox.AdjustToContents)

        self.chkWrap = QCheckBox("Wrapped Phase", self)
        self.chkWrap.setChecked(False)
        self.chkWrap.setToolTip("Plot phase wrapped to +/- pi")
        
        layHControls = QHBoxLayout()
#        layHControls.addStretch(10)
        layHControls.addWidget(self.cmbUnitsPhi)
        layHControls.addWidget(self.chkWrap)
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)


        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        self.draw() # initial drawing

#        #=============================================
#        # Signals & Slots
#        #=============================================
        self.chkWrap.clicked.connect(self.draw)
        self.cmbUnitsPhi.currentIndexChanged.connect(self.draw)
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)        
Exemplo n.º 10
0
    def __init__(self, parent):
        super(PlotPhi, self).__init__(parent)

        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)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)

        self.setLayout(self.mplwidget.layVMainMpl)

        #        self.mplwidget.setFocus()
        # make this the central widget, taking all available space:
        #        self.setCentralWidget(self.mplwidget)

        self._init_axes()

        self.draw()  # initial drawing

        #        #=============================================
        #        # Signals & Slots
        #        #=============================================
        self.btnWrap.clicked.connect(self.draw)
        self.cmbUnitsPhi.currentIndexChanged.connect(self.draw)
Exemplo n.º 11
0
    def __init__(self, parent):
        super(PlotPZ, self).__init__(parent)

        self.layHChkBoxes = QtGui.QHBoxLayout()
        self.layHChkBoxes.addStretch(10)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)

        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
        #       self.setCentralWidget(self.mplwidget)

        self._init_axes()

        self.draw()  # calculate and draw poles and zeros
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    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 poles and zeros
Exemplo n.º 14
0
class PlotTauG(QWidget):

    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)


        self.chkWarnings = QCheckBox()
        self.chkWarnings.setText("Enable Warnings")
        self.chkWarnings.setChecked(False)
        self.chkWarnings.setToolTip("Print warnings about singular group delay")

        self.layHChkBoxes = QHBoxLayout()
        self.layHChkBoxes.addStretch(10)
        self.layHChkBoxes.addWidget(self.chkWarnings)

        self.mplwidget = MplWidget(self)
        
        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)
        
        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
#        self.setCentralWidget(self.mplwidget)

        
        self._init_axes()

        self.draw() # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkWarnings.clicked.connect(self.draw)


#------------------------------------------------------------------------------
    def _init_axes(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 update_view(self):
        """
        Update frequency range etc. without recalculating group delay.
        """
        self.draw()

#------------------------------------------------------------------------------
    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']

        [w, tau_g] = grpdelay(bb,aa, rc.params['N_FFT'], whole = wholeF, 
            verbose = self.chkWarnings.isChecked())

        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.
            
        if fb.fil[0]['freq_specs_unit'] in {'f_S', 'f_Ny'}:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega}) / T_S \; \rightarrow $'
        else:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega})$'\
                + ' in ' + fb.fil[0]['plt_tUnit'] + r' $ \rightarrow $'
            tau_g = tau_g / fb.fil[0]['f_S']


        #---------------------------------------------------------
        line_tau_g, = self.ax.plot(F, tau_g, label = "Group Delay")
        #---------------------------------------------------------
                   

        self.ax.set_xlabel(fb.fil[0]['plt_fLabel'])
        self.ax.set_ylabel(tau_str)
        # widen y-limits to suppress numerical inaccuracies when tau_g = constant
        self.ax.set_ylim([max(min(tau_g)-0.5,0), max(tau_g) + 0.5])
        self.ax.set_xlim(fb.fil[0]['freqSpecsRange'])

        self.mplwidget.redraw()
Exemplo n.º 15
0
    def _init_UI(self):
        self.lblLog = QLabel(self)
        self.lblLog.setText("Log:")
        self.chkLog = QCheckBox(self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("Show logarithmic impulse / step response.")
        self.chkLog.setChecked(False)

        self.lblLogBottom = QLabel("Bottom = ")
        self.ledLogBottom = QLineEdit(self)
        self.ledLogBottom.setText("-80")
        self.ledLogBottom.setToolTip("Minimum display value for log. scale.")
        self.lbldB = QLabel("dB")
        
        self.lblPltStim = QLabel(self)
        self.lblPltStim.setText("Stimulus:  Show")
        self.chkPltStim = QCheckBox(self)
        self.chkPltStim.setChecked(False)
        
        self.lblStimulus = QLabel("Type = ")
        self.cmbStimulus = QComboBox(self)
        self.cmbStimulus.addItems(["Pulse","Step","StepErr", "Sine", "Rect", "Saw"])
        self.cmbStimulus.setToolTip("Select stimulus type.")
        
        self.lblFreq = QLabel("<i>f</i>&nbsp; =")

        self.ledFreq = QLineEdit(self)
        self.ledFreq.setText(str(self.stim_freq))
        self.ledFreq.setToolTip("Stimulus frequency.")
        
        self.lblFreqUnit = QLabel("f_S")

        self.lblNPoints = QLabel("<i>N</i>&nbsp; =")

        self.ledNPoints = QLineEdit(self)
        self.ledNPoints.setText("0")
        self.ledNPoints.setToolTip("Number of points to calculate and display.\n"
                                   "N = 0 chooses automatically.")

        self.layHChkBoxes = QHBoxLayout()
        self.layHChkBoxes.addStretch(10)
        
        self.layHChkBoxes.addWidget(self.lblNPoints)
        self.layHChkBoxes.addWidget(self.ledNPoints)
        self.layHChkBoxes.addStretch(2)
        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.addWidget(self.lbldB)
        self.layHChkBoxes.addStretch(2)
        self.layHChkBoxes.addWidget(self.lblPltStim)
        self.layHChkBoxes.addWidget(self.chkPltStim)
        self.layHChkBoxes.addStretch(1)
        self.layHChkBoxes.addWidget(self.lblStimulus)
        self.layHChkBoxes.addWidget(self.cmbStimulus)
        self.layHChkBoxes.addStretch(2)
        self.layHChkBoxes.addWidget(self.lblFreq)
        self.layHChkBoxes.addWidget(self.ledFreq)
        self.layHChkBoxes.addWidget(self.lblFreqUnit)

        self.layHChkBoxes.addStretch(10)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)

        self.setLayout(self.mplwidget.layVMainMpl)

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self.draw)
        self.ledNPoints.editingFinished.connect(self.draw)
        self.ledLogBottom.editingFinished.connect(self.draw)
        self.chkPltStim.clicked.connect(self.draw)
        self.cmbStimulus.currentIndexChanged.connect(self.draw)
        self.ledFreq.installEventFilter(self) 
#        self.ledFreq.editingFinished.connect(self.draw)

        self.draw() # initial calculation and drawing
Exemplo n.º 16
0
    def __init__(self, parent):
        super(PlotHf, self).__init__(parent)

        modes = ['| H |', 're{H}', 'im{H}']
        self.cmbShowH = 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 = QLabel("in")

        units = ['dB', 'V', 'W', 'Auto']
        self.cmbUnitsA = 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(QComboBox.AdjustToContents)
        self.cmbUnitsA.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.lblLinphase = QLabel("Acausal system")
        self.chkLinphase = 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 = QLabel("Inset")

        self.cmbInset = 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 = QLabel("Show Specs")
        self.chkSpecs = QCheckBox()
        self.chkSpecs.setChecked(False)
        self.chkSpecs.setToolTip("Display filter specs as hatched regions")

        self.lblPhase = QLabel("Phase")
        self.chkPhase = QCheckBox()
        self.chkPhase.setToolTip("Overlay phase")

        self.layHChkBoxes = 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)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)

        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        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)
Exemplo n.º 17
0
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 = rc.params['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)
        #---------------------------------------------------------

        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()
Exemplo n.º 18
0
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':rcParams['figure.edgecolor'], 'lw':0.0}
        line_params = {'linewidth':1.0, 'color':'blue', 'linestyle':'--'}
        ax = specAxes

        # extract from filterTree the parameters that are actually used
#        myParams = fb.fil_tree[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 = rc.params['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, 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--', 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=rcParams['figure.facecolor'], 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)

            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()
Exemplo n.º 19
0
class PlotPZ(QWidget):

    def __init__(self, parent): 
        super(PlotPZ, self).__init__(parent)

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
#        self.frmControls = QFrame(self)
#        self.frmControls.setLayout(layHControls)


        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addLayout(layHControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
 #       self.setCentralWidget(self.mplwidget)
        
        self._init_axes()

        self.draw() # calculate and draw poles and zeros

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
#        self.btnWhatever.clicked.connect(self.draw)

#------------------------------------------------------------------------------
    def _init_axes(self):
        """Initialize and clear the axes
        """
#        self.ax = self.mplwidget.ax
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()
        
#------------------------------------------------------------------------------
    def update_specs(self):
        """
        Draw the figure with new limits, scale etcs without recalculating H(f)
        -- not yet implemented, just use draw() for the moment
        """
        self.draw()

#------------------------------------------------------------------------------
    def draw(self):
        if self.mplwidget.mplToolbar.enabled:
            self.draw_pz()
            
#------------------------------------------------------------------------------
    def draw_pz(self):
        """
        (re)draw P/Z plot
        """
        p_marker = params['P_Marker']
        z_marker = params['Z_Marker']
        
        zpk = fb.fil[0]['zpk']

        self.ax.clear()

        [z, p, k] = self.zplane(z = zpk[0], p = zpk[1], k = zpk[2], plt_ax = self.ax, verbose = False, 
            mps = p_marker[0], mpc = p_marker[1], mzs = z_marker[0], mzc = z_marker[1])

        self.ax.set_title(r'Pole / Zero Plot')
        self.ax.set_xlabel('Real axis')
        self.ax.set_ylabel('Imaginary axis')

        self.redraw()
        
#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
        
        
#------------------------------------------------------------------------------
    def zplane(self, b=None, a=1, z=None, p=None, k =1,  pn_eps=1e-3, analog=False, plt_ax = None,
              verbose=False, style='square', anaCircleRad=0, lw=2,
              mps = 10, mzs = 10, mpc = 'r', mzc = 'b', plabel = '', zlabel = ''):
        """
        Plot the poles and zeros in the complex z-plane either from the
        coefficients (`b,`a) of a discrete transfer function `H`(`z`) (zpk = False)
        or directly from the zeros and poles (z,p) (zpk = True).
    
        When only b is given, an FIR filter with all poles at the origin is assumed.
    
        Parameters
        ----------
        b :  array_like
             Numerator coefficients (transversal part of filter)
             When b is not None, poles and zeros are determined from the coefficients
             b and a
    
        a :  array_like (optional, default = 1 for FIR-filter)
             Denominator coefficients (recursive part of filter)
             
        z :  array_like, default = None
             Zeros
             When b is None, poles and zeros are taken directly from z and p
    
        p :  array_like, default = None
             Poles
    
        analog : boolean (default: False)
            When True, create a P/Z plot suitable for the s-plane, i.e. suppress
            the unit circle (unless anaCircleRad > 0) and scale the plot for
            a good display of all poles and zeros.
    
        pn_eps : float (default : 1e-2)
             Tolerance for separating close poles or zeros
             
        plt_ax : handle to axes for plotting (default: None)
            When no axes is specified, the current axes is determined via plt.gca()
    
        pltLib :  string (default: 'matplotlib')
             Library for plotting the P/Z plane. Currently, only matplotlib is
             implemented. When pltLib = 'none' or when matplotlib is not
             available, only pass the poles / zeros and their multiplicity
    
        verbose : boolean (default: False)
            When verbose == True, print poles / zeros and their multiplicity.
    
        style : string (default: 'square')
            Style of the plot, for style == 'square' make scale of x- and y-
            axis equal.
    
        mps : integer  (default: 10)
            Size for pole marker
            
        mzs : integer (default: 10)
            Size for zero marker
            
        mpc : char (default: 'r')
            Pole marker colour
            
        mzc : char (default: 'b')
            Zero marker colour
            
        lw : integer (default:  2)
            Linewidth for unit circle
    
        plabel, zlabel : string (default: '')
            This string is passed to the plot command for poles and zeros and
            can be displayed by legend()
    
    
        Returns
        -------
        z, p, k : ndarray
    
    
        Notes
        -----
    
        """
        # TODO:
        # - polar option
        # - add keywords for size, color etc. of markers and circle -> **kwargs
        # - add option for multi-dimensional arrays and zpk data
    
        # make sure that all inputs are arrays
        b = np.atleast_1d(b) 
        a = np.atleast_1d(a)
        z = np.atleast_1d(z) # make sure that p, z  are arrays
        p = np.atleast_1d(p)
    
        if b.any(): # coefficients were specified
            if len(b) < 2 and len(a) < 2:
                logger.error('No proper filter coefficients: both b and a are scalars!')
                return z, p, k
            
            # The coefficients are less than 1, normalize the coefficients
            if np.max(b) > 1:
                kn = np.max(b)
                b = b / float(kn) 
            else:
                kn = 1.
    
            if np.max(a) > 1:
                kd = np.max(a)
                a = a / abs(kd)
            else:
                kd = 1.
    
            # Calculate the poles, zeros and scaling factor
            p = np.roots(a)
            z = np.roots(b)
            k = kn/kd
        elif not (len(p) or len(z)): # P/Z were specified
                logger.error('Either b,a or z,p must be specified!')
                return z, p, k
  
        # find multiple poles and zeros and their multiplicities
        if len(p) < 2: # single pole, [None] or [0]
            if not p or p == 0: # only zeros, create equal number of poles at origin
                p = np.array(0,ndmin=1) # 
                num_p = np.atleast_1d(len(z))
            else:
                num_p = [1.] # single pole != 0
        else:
            #p, num_p = sig.signaltools.unique_roots(p, tol = pn_eps, rtype='avg')
            p, num_p = unique_roots(p, tol = pn_eps, rtype='avg')
    #        p = np.array(p); num_p = np.ones(len(p))
        if len(z) > 0:
            z, num_z = unique_roots(z, tol = pn_eps, rtype='avg')
    #        z = np.array(z); num_z = np.ones(len(z))
            #z, num_z = sig.signaltools.unique_roots(z, tol = pn_eps, rtype='avg')
        else:
            num_z = []
    
    
        ax = plt_ax#.subplot(111)
        if analog == False:
            # create the unit circle for the z-plane
            uc = patches.Circle((0,0), radius=1, fill=False,
                                color='grey', ls='solid', zorder=1)
            ax.add_patch(uc)
            if style == 'square':
                r = 1.1
                ax.axis([-r, r, -r, r], 'equal')
                ax.axis('equal')
        #    ax.spines['left'].set_position('center')
        #    ax.spines['bottom'].set_position('center')
        #    ax.spines['right'].set_visible(True)
        #    ax.spines['top'].set_visible(True)
    
        else: # s-plane
            if anaCircleRad > 0:
                # plot a circle with radius = anaCircleRad
                uc = patches.Circle((0,0), radius=anaCircleRad, fill=False,
                                    color='grey', ls='solid', zorder=1)
                ax.add_patch(uc)
            # plot real and imaginary axis
            ax.axhline(lw=2, color = 'k', zorder=1)
            ax.axvline(lw=2, color = 'k', zorder=1)
    
        # Plot the zeros
        ax.scatter(z.real, z.imag, s=mzs*mzs, zorder=2, marker = 'o',
                   facecolor = 'none', edgecolor = mzc, lw = lw, label=zlabel)
    #        t1 = plt.plot(z.real, z.imag, 'go', ms=10, label=label)
    #        plt.setp( t1, markersize=mzs, markeredgewidth=2.0,
    #                  markeredgecolor=mzc, markerfacecolor='none')
        # Plot the poles
        ax.scatter(p.real, p.imag, s=mps*mps, zorder=2, marker='x',
                   color=mpc, lw=lw, label=plabel)
    
         # Print multiplicity of poles / zeros
        for i in range(len(z)):
            if verbose == True: print('z', i, z[i], num_z[i])
            if num_z[i] > 1:
                ax.text(np.real(z[i]), np.imag(z[i]),'  (' + str(num_z[i]) +')',va = 'bottom')
    
        for i in range(len(p)):
            if verbose == True: print('p', i, p[i], num_p[i])
            if num_p[i] > 1:
                ax.text(np.real(p[i]), np.imag(p[i]), '  (' + str(num_p[i]) +')',va = 'bottom')
    
            # increase distance between ticks and labels
            # to give some room for poles and zeros
        for tick in ax.get_xaxis().get_major_ticks():
            tick.set_pad(12.)
            tick.label1 = tick._get_text1()
        for tick in ax.get_yaxis().get_major_ticks():
            tick.set_pad(12.)
            tick.label1 = tick._get_text1()
    
        xl = ax.get_xlim(); Dx = max(abs(xl[1]-xl[0]), 0.05)
        yl = ax.get_ylim(); Dy = max(abs(yl[1]-yl[0]), 0.05)
        ax.set_xlim((xl[0]-Dx*0.05, max(xl[1]+Dx*0.05,0)))
        ax.set_ylim((yl[0]-Dy*0.05, yl[1] + Dy*0.05))
    #    print(ax.get_xlim(),ax.get_ylim())
    
    
        return z, p, k
Exemplo n.º 20
0
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] = 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=0.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, ...)
        #      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()
Exemplo n.º 21
0
class PlotHf(QWidget):

# 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
# TODO: ax.clear() should not be neccessary for each replot?
# TODO: Canvas should be grey when disabled

    def __init__(self, parent): 
        super(PlotHf, self).__init__(parent)

        modes = ['| H |', 're{H}', 'im{H}']
        self.cmbShowH = 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 = QLabel("in", self)

        units = ['dB', 'V', 'W', 'Auto']
        self.cmbUnitsA = QComboBox(self)
        self.cmbUnitsA.addItems(units)
        self.cmbUnitsA.setObjectName("cmbUnitsA")
        self.cmbUnitsA.setToolTip("<span>Set unit for y-axis:\n"
        "dB is attenuation (positive values), V and W are gain (less than 1).</span>")
        self.cmbUnitsA.setCurrentIndex(0)

        self.cmbShowH.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbUnitsA.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.chkLinphase = QCheckBox("Zero phase", self)
        self.chkLinphase.setToolTip("<span>Subtract linear phase according to filter order.\n"
           "Attention: this makes no sense for a non-linear phase system!</span>")

        self.lblInset = QLabel("Inset", self)
        self.cmbInset = 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.chkSpecs = QCheckBox("Show Specs", self)
        self.chkSpecs.setChecked(False)
        self.chkSpecs.setToolTip("Display filter specs as hatched regions")

        self.chkPhase = QCheckBox("Phase", self)
        self.chkPhase.setToolTip("Overlay phase")


        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        layHControls.addWidget(self.cmbShowH)
        layHControls.addWidget(self.lblIn)
        layHControls.addWidget(self.cmbUnitsA)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkLinphase)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblInset)
        layHControls.addWidget(self.cmbInset)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkSpecs)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkPhase)
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        # This is the plot pane widget, encompassing the other widgets        
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        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)
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)        
        
#------------------------------------------------------------------------------
    def init_axes(self):
        """
        Initialize and clear the axes
        """
#        self.ax = self.mplwidget.ax
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()
        self.ax.get_xaxis().tick_bottom() # remove axis ticks on top
        self.ax.get_yaxis().tick_left() # remove axis ticks right

#------------------------------------------------------------------------------
    def plot_spec_limits(self, ax):
        """
        Plot the specifications limits (F_SB, A_SB, ...) as hatched areas with borders.
        """
        hatch = params['mpl_hatch']
        hatch_borders = params['mpl_hatch_border']

        def dB(lin):
            return 20 * np.log10(lin)

        def _plot_specs():
            # upper limits:
            ax.plot(F_lim_upl, A_lim_upl, F_lim_upc, A_lim_upc, F_lim_upr, A_lim_upr, **hatch_borders)
            if A_lim_upl:
                ax.fill_between(F_lim_upl, max(A_lim_upl), A_lim_upl, **hatch)
            if A_lim_upc:
                ax.fill_between(F_lim_upc, max(A_lim_upc), A_lim_upc, **hatch)
            if A_lim_upr:
                ax.fill_between(F_lim_upr, max(A_lim_upr), A_lim_upr, **hatch)
            # lower limits:
            ax.plot(F_lim_lol, A_lim_lol, F_lim_loc, A_lim_loc, F_lim_lor, A_lim_lor, **hatch_borders)
            if A_lim_lol:
                ax.fill_between(F_lim_lol, min(A_lim_lol), A_lim_lol, **hatch)
            if A_lim_loc:
                ax.fill_between(F_lim_loc, min(A_lim_loc), A_lim_loc, **hatch)
            if A_lim_lor:
                ax.fill_between(F_lim_lor, min(A_lim_lor), A_lim_lor, **hatch)

        if self.unitA == 'V':
            exp = 1.
        elif self.unitA == 'W':
            exp = 2.

        if self.unitA == 'dB':
            if fb.fil[0]['ft'] == "FIR":
                A_PB_max  = dB(1 + self.A_PB)
                A_PB2_max = dB(1 + self.A_PB2)
            else: # IIR dB
                A_PB_max = A_PB2_max = 0

            A_PB_min  = dB(1 - self.A_PB)
            A_PB2_min = dB(1 - self.A_PB2)
            A_PB_minx = min(A_PB_min, A_PB2_min) - 5
            A_PB_maxx = max(A_PB_max, A_PB2_max) + 5

            A_SB  = dB(self.A_SB)
            A_SB2 = dB(self.A_SB2)
            A_SB_maxx = max(A_SB, A_SB2) + 10
        else: # 'V' or 'W'
            if fb.fil[0]['ft'] == "FIR":
                A_PB_max  = (1 + self.A_PB)**exp
                A_PB2_max = (1 + self.A_PB2)**exp
            else: # IIR lin
                A_PB_max = A_PB2_max = 1

            A_PB_min  = (1 - self.A_PB)**exp
            A_PB2_min = (1 - self.A_PB2)**exp
            A_PB_minx = min(A_PB_min, A_PB2_min) / 1.05
            A_PB_maxx = max(A_PB_max, A_PB2_max) * 1.05

            A_SB  = self.A_SB ** exp
            A_SB2 = self.A_SB2 ** exp
            A_SB_maxx = A_PB_min / 10.

        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

        F_lim_upl = F_lim_lol = [] # left side limits, lower and upper
        A_lim_upl = A_lim_lol = []

        F_lim_upc = F_lim_loc = [] # center limits, lower and upper
        A_lim_upc = A_lim_loc = []

        F_lim_upr = F_lim_lor = [] # right side limits, lower and upper
        A_lim_upr = A_lim_lor = []

        if fb.fil[0]['rt'] == 'LP':
            F_lim_upl = [0,        F_PB,     F_PB]
            A_lim_upl = [A_PB_max, A_PB_max, A_PB_maxx]
            F_lim_lol = F_lim_upl
            A_lim_lol = [A_PB_min, A_PB_min, A_PB_minx]

            F_lim_upr = [F_SB,     F_SB, F_max]
            A_lim_upr = [A_SB_maxx, A_SB, A_SB]

        if fb.fil[0]['rt'] == 'HP':
            F_lim_upl = [0,    F_SB, F_SB]
            A_lim_upl = [A_SB, A_SB, A_SB_maxx]

            F_lim_upr = [F_PB,      F_PB,     F_max]
            A_lim_upr = [A_PB_maxx, A_PB_max, A_PB_max]
            F_lim_lor = F_lim_upr
            A_lim_lor = [A_PB_minx, A_PB_min, A_PB_min]

        if fb.fil[0]['rt'] == 'BS':
            F_lim_upl = [0,        F_PB,     F_PB]
            A_lim_upl = [A_PB_max, A_PB_max, A_PB_maxx]
            F_lim_lol = F_lim_upl
            A_lim_lol = [A_PB_min, A_PB_min, A_PB_minx]

            F_lim_upc = [F_SB, F_SB, F_SB2, F_SB2]
            A_lim_upc = [A_SB_maxx, A_SB, A_SB,  A_SB_maxx]

            F_lim_upr = [F_PB2, F_PB2, F_max]
            A_lim_upr = [A_PB_maxx, A_PB2_max, A_PB2_max]
            F_lim_lor = F_lim_upr
            A_lim_lor = [A_PB_minx, A_PB2_min, A_PB2_min]

        if fb.fil[0]['rt'] == 'BP':
            F_lim_upl = [0,    F_SB, F_SB]
            A_lim_upl = [A_SB, A_SB, A_SB_maxx]

            F_lim_upc = [F_PB,      F_PB,     F_PB2,    F_PB2]
            A_lim_upc = [A_PB_maxx, A_PB_max, A_PB_max, A_PB_maxx]
            F_lim_loc = F_lim_upc
            A_lim_loc = [A_PB_minx, A_PB_min, A_PB_min, A_PB_minx]

            F_lim_upr = [F_SB2,    F_SB2, F_max]
            A_lim_upr = [A_SB_maxx, A_SB2, A_SB2]

        if fb.fil[0]['rt'] == 'HIL':
            F_lim_upc = [F_PB,      F_PB,     F_PB2,    F_PB2]
            A_lim_upc = [A_PB_maxx, A_PB_max, A_PB_max, A_PB_maxx]

            F_lim_loc = F_lim_upc
            A_lim_loc = [A_PB_minx, A_PB_min, A_PB_min, A_PB_minx]

        F_lim_upr = np.array(F_lim_upr)
        F_lim_lor = np.array(F_lim_lor)
        F_lim_upl = np.array(F_lim_upl)
        F_lim_lol = np.array(F_lim_lol)
        F_lim_upc = np.array(F_lim_upc)
        F_lim_loc = np.array(F_lim_loc)

        _plot_specs() # plot specs in the range 0 ... f_S/2

        if fb.fil[0]['freqSpecsRangeType'] != 'half':
            # add plot limits for other half of the spectrum
            if fb.fil[0]['freqSpecsRangeType'] == 'sym': # frequency axis +/- f_S/2
                F_lim_upl = -F_lim_upl
                F_lim_lol = -F_lim_lol
                F_lim_upc = -F_lim_upc
                F_lim_loc = -F_lim_loc
                F_lim_upr = -F_lim_upr
                F_lim_lor = -F_lim_lor
            else: # -> 'whole'
                F_lim_upl = self.f_S - F_lim_upl
                F_lim_lol = self.f_S - F_lim_lol
                F_lim_upc = self.f_S - F_lim_upc
                F_lim_loc = self.f_S - F_lim_loc
                F_lim_upr = self.f_S - F_lim_upr
                F_lim_lor = self.f_S - F_lim_lor

            _plot_specs()

#------------------------------------------------------------------------------
    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.)

# 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.get_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=rcParams['figure.facecolor'], 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)

            if self.cmbInset.currentIndex() == 1: # edit / navigate inset
                self.ax_i.set_navigate(True)
                self.ax.set_navigate(False)
                if self.specs:
                    self.plot_spec_limits(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()


#------------------------------------------------------------------------------
    def draw_phase(self, ax):
        """
        Draw phase on second y-axis in the axes system passed as the argument
        """
        try:
            self.mplwidget.fig.delaxes(self.ax_p)
        except (KeyError, AttributeError):
            pass

        if self.chkPhase.isChecked():
            self.ax_p = 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
                
            # replace nan and inf by finite values, otherwise np.unwrap yields
            # an array full of nans
            phi = np.angle(np.nan_to_num(self.H_c)) 
        #-----------------------------------------------------------
            self.ax_p.plot(self.F,np.unwrap(phi)*scale,
                               'g-.', label = "Phase")
        #-----------------------------------------------------------
            self.ax_p.set_ylabel(phi_str)
            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 calc_hf(self):
        """
        (Re-)Calculate the complex frequency response H(f)
        """

        # calculate H_cmplx(W) (complex) for W = 0 ... 2 pi:
        self.W, self.H_cmplx = calc_Hcomplex(fb.fil[0], params['N_FFT'], True)

#------------------------------------------------------------------------------
    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.init_axes()
            self.draw()

#------------------------------------------------------------------------------
    def draw(self):
        """
        Re-calculate |H(f)| and draw the figure if enabled
        """
        if self.mplwidget.mplToolbar.enabled:
            self.calc_hf()
            self.update_view()

#------------------------------------------------------------------------------
    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """
        # Get corners for spec display from the parameters of the target specs subwidget       
        try:
            param_list = fb.fil_tree[fb.fil[0]['rt']][fb.fil[0]['ft']]\
                                    [fb.fil[0]['fc']][fb.fil[0]['fo']]['tspecs'][1]['amp']
        except KeyError:
            param_list = []

        SB = [l for l in param_list if 'A_SB' in l]
        PB = [l for l in param_list if 'A_PB' in l]
        
        if SB:
            A_min = min([fb.fil[0][l] for l in SB])
        else:
            A_min = 5e-4

        if PB:
            A_max = max([fb.fil[0][l] for l in PB])
        else:
            A_max = 1

        if np.all(self.W) is None: # H(f) has not been calculated yet
            self.calc_hf()

        if self.cmbUnitsA.currentText() == 'Auto':
            self.unitA = fb.fil[0]['amp_specs_unit']
        else:
            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.specs = self.chkSpecs.isChecked()
        self.linphase = self.chkLinphase.isChecked()

        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']

        #========= select frequency range to be displayed =====================
        #=== shift, scale and select: W -> F, H_cplx -> H_c
        self.F = self.W / (2 * np.pi) * self.f_S

        if fb.fil[0]['freqSpecsRangeType'] == 'sym':
            # shift H and F by f_S/2
            self.H_c = np.fft.fftshift(self.H_cmplx)
            self.F -= self.f_S/2.
        elif fb.fil[0]['freqSpecsRangeType'] == 'half':
            # only use the first half of H and F
            self.H_c = self.H_cmplx[0:params['N_FFT']//2]
            self.F = self.F[0:params['N_FFT']//2]
        else: # fb.fil[0]['freqSpecsRangeType'] == 'whole'
            # use H and F as calculated
            self.H_c = self.H_cmplx
            
        # now calculate mag / real / imaginary part of H_c:
        if self.linphase: # remove the linear phase
            self.H_c = self.H_c * np.exp(1j * self.W[0:len(self.F)] * 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})\}$'

        #================ Main Plotting Routine =========================
        #===  clear the axes and (re)draw the plot (if selectable)
        if self.ax.get_navigate():

            if self.unitA == 'dB':
                A_lim = [20*np.log10(A_min) -10, 20*np.log10(1+A_max) +1]
                self.H_plt = 20*np.log10(abs(H))
                H_str += ' in dB ' + r'$\rightarrow$'
            elif self.unitA == 'V': #  'lin'
                A_lim = [0, (1.05 + A_max)]
                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 = [0, (1.03 + A_max)**2.]
                self.H_plt = H * H.conj()
                H_str += ' in W ' + r'$\rightarrow $'

            #-----------------------------------------------------------
            self.ax.clear()
            self.ax.plot(self.F, self.H_plt, label = 'H(f)')
            self.draw_phase(self.ax)
            #-----------------------------------------------------------
            
            #============= Set Limits and draw specs =========================
            if self.specs: 
                self.plot_spec_limits(self.ax)

            #     self.ax_bounds = [self.ax.get_ybound()[0], self.ax.get_ybound()[1]]#, self.ax.get]
            self.ax.set_xlim(f_lim)
            self.ax.set_ylim(A_lim)

            self.ax.set_title(r'Magnitude Frequency Response')
            self.ax.set_xlabel(fb.fil[0]['plt_fLabel'])
            self.ax.set_ylabel(H_str)

        self.redraw()
        
#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
Exemplo n.º 22
0
class PlotTauG(QWidget):
    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)

        self.chkWarnings = QCheckBox("Enable Warnings", self)
        self.chkWarnings.setChecked(False)
        self.chkWarnings.setToolTip(
            "Print warnings about singular group delay")

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        layHControls.addWidget(self.chkWarnings)

        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.ax = self.mplwidget.fig.add_subplot(111)

        self.draw()  # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkWarnings.clicked.connect(self.draw)

#------------------------------------------------------------------------------

    def draw(self):
        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.draw_taug()

#------------------------------------------------------------------------------

    def update_view(self):
        """
        Update frequency range etc. without recalculating group delay.
        """
        self.draw()

#------------------------------------------------------------------------------

    def draw_taug(self):
        """
        Draw group delay
        """
        self.ax.clear()
        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']

        [w, tau_g] = grpdelay(bb,
                              aa,
                              params['N_FFT'],
                              whole=wholeF,
                              verbose=self.chkWarnings.isChecked())

        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.

        if fb.fil[0]['freq_specs_unit'] in {'f_S', 'f_Ny'}:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega}) / T_S \; \rightarrow $'
        else:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega})$'\
                + ' in ' + fb.fil[0]['plt_tUnit'] + r' $ \rightarrow $'
            tau_g = tau_g / fb.fil[0]['f_S']

        #---------------------------------------------------------
        line_tau_g, = self.ax.plot(F, tau_g, label="Group Delay")
        #---------------------------------------------------------

        self.ax.set_title(r'Group Delay $ \tau_g$')
        self.ax.set_xlabel(fb.fil[0]['plt_fLabel'])
        self.ax.set_ylabel(tau_str)
        # widen y-limits to suppress numerical inaccuracies when tau_g = constant
        self.ax.set_ylim([max(min(tau_g) - 0.5, 0), max(tau_g) + 0.5])
        self.ax.set_xlim(fb.fil[0]['freqSpecsRange'])

        self.redraw()

#------------------------------------------------------------------------------

    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
Exemplo n.º 23
0
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 = rc.params['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 = H_mag(bb, aa, self.xy_UC, top, H_min=bottom, log=True)
            Hmag = 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 = H_mag(bb, aa, self.xy_UC, top, H_min=bottom)
            Hmag = 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)
            # draw once more as dashed white line to improve visibility
            self.ax3d.plot(self.xy_UC.real, self.xy_UC.imag, H_UC, 'w--')


            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()
Exemplo n.º 24
0
class PlotPhi(QtGui.QWidget):
    def __init__(self, parent):
        super(PlotPhi, self).__init__(parent)

        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)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)

        self.setLayout(self.mplwidget.layVMainMpl)

        #        self.mplwidget.setFocus()
        # make this the central widget, taking all available space:
        #        self.setCentralWidget(self.mplwidget)

        self._init_axes()

        self.draw()  # initial drawing

        #        #=============================================
        #        # Signals & Slots
        #        #=============================================
        self.btnWrap.clicked.connect(self.draw)
        self.cmbUnitsPhi.currentIndexChanged.connect(self.draw)

    def _init_axes(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 update_view(self):
        """
        place holder; should update only the limits without recalculating
        the phase
        """
        self.draw()

    def draw(self):
        """
        main entry point for drawing the phase
        """
        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]

        wholeF = fb.fil[0]['freqSpecsRangeType'] != 'half'
        f_S = fb.fil[0]['f_S']

        [W, H] = sig.freqz(self.bb,
                           self.aa,
                           worN=rc.params['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})$ in '
        if self.unitPhi == 'rad':
            y_str += 'rad ' + r'$\rightarrow $'
            scale = 1.
        elif self.unitPhi == 'rad/pi':
            y_str += 'rad' + r'$ / \pi \;\rightarrow $'
            scale = 1. / np.pi
        else:
            y_str += 'deg ' + r'$\rightarrow $'
            scale = 180. / np.pi
        fb.fil[0]['plt_phiLabel'] = y_str
        fb.fil[0]['plt_phiUnit'] = self.unitPhi

        # replace nan and inf by finite values, otherwise np.unwrap yields
        # an array full of nans
        H = np.nan_to_num(H)
        if self.btnWrap.isChecked():
            phi_plt = np.angle(H) * scale
        else:
            phi_plt = np.unwrap(np.angle(H)) * scale

        self.ax.clear()  # need to clear, doesn't overwrite
        #---------------------------------------------------------
        line_phi, = self.ax.plot(F, phi_plt)
        #---------------------------------------------------------

        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()
Exemplo n.º 25
0
    def _init_UI(self):
        self.chkLog = QCheckBox(self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("<span>Logarithmic scale for y-axis.</span>")
        self.chkLog.setChecked(False)
        self.lblLog = QLabel("Log. y-axis", self)

        self.lblLogBottom = QLabel("Bottom = ", self)
        self.ledLogBottom = QLineEdit(self)
        self.ledLogBottom.setText(str(self.bottom))
        self.ledLogBottom.setToolTip("<span>Minimum display value for log. scale.</span>")
        self.lbldB = QLabel("dB")
        
        self.lblPltStim = QLabel(self)
        self.lblPltStim.setText("Stimulus:")
        self.chkPltStim = QCheckBox("Show", self)
        self.chkPltStim.setChecked(False)
        self.chkPltStim.setToolTip("Show stimulus signal.")
        
        self.lblStimulus = QLabel("Type = ", self)
        self.cmbStimulus = QComboBox(self)
        self.cmbStimulus.addItems(["Pulse","Step","StepErr", "Cos", "Sine", "Rect", "Saw", "RandN", "RandU"])
        self.cmbStimulus.setToolTip("Select stimulus type.")

        self.lblAmp = QLabel("<i>A</i>&nbsp; =", self)
        self.ledAmp = QLineEdit(self)
        self.ledAmp.setText(str(self.A))
        self.ledAmp.setToolTip("Stimulus amplitude.")
        self.ledAmp.setObjectName("stimAmp")

        self.lblFreq = QLabel("<i>f</i>&nbsp; =", self)
        self.ledFreq = QLineEdit(self)
        self.ledFreq.setText(str(self.stim_freq))
        self.ledFreq.setToolTip("Stimulus frequency.")
        self.ledFreq.setObjectName("stimFreq")

        self.lblFreqUnit = QLabel("f_S", self)

        self.lblNPoints = QLabel("<i>N</i>&nbsp; =", self)

        self.ledNPoints = QLineEdit(self)
        self.ledNPoints.setText("0")
        self.ledNPoints.setToolTip("<span>Number of points to calculate and display. "
                                   "N = 0 selects automatically.</span>")

        layHControls = QHBoxLayout()
        
        layHControls.addWidget(self.lblNPoints)
        layHControls.addWidget(self.ledNPoints)
        layHControls.addStretch(2)
        layHControls.addWidget(self.chkLog)
        layHControls.addWidget(self.lblLog)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblLogBottom)
        layHControls.addWidget(self.ledLogBottom)
        layHControls.addWidget(self.lbldB)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblPltStim)
        layHControls.addWidget(self.chkPltStim)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblStimulus)
        layHControls.addWidget(self.cmbStimulus)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblAmp)
        layHControls.addWidget(self.ledAmp)
        layHControls.addWidget(self.lblFreq)
        layHControls.addWidget(self.ledFreq)
        layHControls.addWidget(self.lblFreqUnit)

        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self.draw)
        self.ledNPoints.editingFinished.connect(self.draw)
        self.ledLogBottom.editingFinished.connect(self.draw)
        self.chkPltStim.clicked.connect(self.draw)
#        self.cmbStimulus.currentIndexChanged.connect(self.draw)
        self.cmbStimulus.activated.connect(self.draw)
        self.ledAmp.editingFinished.connect(self.draw)
        self.ledFreq.installEventFilter(self) 
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)

        self.draw() # initial calculation and drawing
Exemplo n.º 26
0
class PlotImpz(QWidget):

    def __init__(self, parent):
        super(PlotImpz, self).__init__(parent)

        self.ACTIVE_3D = False
        # initial settings for line edit widgets
        self.stim_freq = 0.02
        self.A = 1.0
        self.bottom = -80
        self._init_UI()

    def _init_UI(self):
        self.chkLog = QCheckBox(self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("<span>Logarithmic scale for y-axis.</span>")
        self.chkLog.setChecked(False)
        self.lblLog = QLabel("Log. y-axis", self)

        self.lblLogBottom = QLabel("Bottom = ", self)
        self.ledLogBottom = QLineEdit(self)
        self.ledLogBottom.setText(str(self.bottom))
        self.ledLogBottom.setToolTip("<span>Minimum display value for log. scale.</span>")
        self.lbldB = QLabel("dB")
        
        self.lblPltStim = QLabel(self)
        self.lblPltStim.setText("Stimulus:")
        self.chkPltStim = QCheckBox("Show", self)
        self.chkPltStim.setChecked(False)
        self.chkPltStim.setToolTip("Show stimulus signal.")
        
        self.lblStimulus = QLabel("Type = ", self)
        self.cmbStimulus = QComboBox(self)
        self.cmbStimulus.addItems(["Pulse","Step","StepErr", "Cos", "Sine", "Rect", "Saw", "RandN", "RandU"])
        self.cmbStimulus.setToolTip("Select stimulus type.")

        self.lblAmp = QLabel("<i>A</i>&nbsp; =", self)
        self.ledAmp = QLineEdit(self)
        self.ledAmp.setText(str(self.A))
        self.ledAmp.setToolTip("Stimulus amplitude.")
        self.ledAmp.setObjectName("stimAmp")

        self.lblFreq = QLabel("<i>f</i>&nbsp; =", self)
        self.ledFreq = QLineEdit(self)
        self.ledFreq.setText(str(self.stim_freq))
        self.ledFreq.setToolTip("Stimulus frequency.")
        self.ledFreq.setObjectName("stimFreq")

        self.lblFreqUnit = QLabel("f_S", self)

        self.lblNPoints = QLabel("<i>N</i>&nbsp; =", self)

        self.ledNPoints = QLineEdit(self)
        self.ledNPoints.setText("0")
        self.ledNPoints.setToolTip("<span>Number of points to calculate and display. "
                                   "N = 0 selects automatically.</span>")

        layHControls = QHBoxLayout()
        
        layHControls.addWidget(self.lblNPoints)
        layHControls.addWidget(self.ledNPoints)
        layHControls.addStretch(2)
        layHControls.addWidget(self.chkLog)
        layHControls.addWidget(self.lblLog)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblLogBottom)
        layHControls.addWidget(self.ledLogBottom)
        layHControls.addWidget(self.lbldB)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblPltStim)
        layHControls.addWidget(self.chkPltStim)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblStimulus)
        layHControls.addWidget(self.cmbStimulus)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblAmp)
        layHControls.addWidget(self.ledAmp)
        layHControls.addWidget(self.lblFreq)
        layHControls.addWidget(self.ledFreq)
        layHControls.addWidget(self.lblFreqUnit)

        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self.draw)
        self.ledNPoints.editingFinished.connect(self.draw)
        self.ledLogBottom.editingFinished.connect(self.draw)
        self.chkPltStim.clicked.connect(self.draw)
#        self.cmbStimulus.currentIndexChanged.connect(self.draw)
        self.cmbStimulus.activated.connect(self.draw)
        self.ledAmp.editingFinished.connect(self.draw)
        self.ledFreq.installEventFilter(self) 
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)

        self.draw() # initial calculation and drawing

#------------------------------------------------------------------------------
    def eventFilter(self, source, event):
        """
        Filter all events generated by the QLineEdit widgets. Source and type
        of all events generated by monitored objects are passed to this eventFilter,
        evaluated and passed on to the next hierarchy level.

        - When a QLineEdit widget gains input focus (QEvent.FocusIn`), display
          the stored value from filter dict with full precision
        - When a key is pressed inside the text field, set the `spec_edited` flag
          to True.
        - When a QLineEdit widget loses input focus (QEvent.FocusOut`), store
          current value normalized to f_S with full precision (only if
          `spec_edited`== True) and display the stored value in selected format
        """

        def _store_entry(source):
            if self.spec_edited:
                self.stim_freq = safe_eval(source.text(), self.stim_freq * fb.fil[0]['f_S'],
                                            return_type='float') / fb.fil[0]['f_S']
                self.spec_edited = False # reset flag
                self.draw()
                
        if isinstance(source, QLineEdit): # could be extended for other widgets
            if event.type() == QEvent.FocusIn:
                self.spec_edited = False
                self.load_dict()
            elif event.type() == QEvent.KeyPress:
                self.spec_edited = True # entry has been changed
                key = event.key()
                if key in {Qt.Key_Return, Qt.Key_Enter}:
                    _store_entry(source)
                elif key == Qt.Key_Escape: # revert changes
                    self.spec_edited = False                    
                    source.setText(str(params['FMT'].format(self.stim_freq * fb.fil[0]['f_S'])))
                
            elif event.type() == QEvent.FocusOut:
                _store_entry(source)
                source.setText(str(params['FMT'].format(self.stim_freq * fb.fil[0]['f_S'])))
        # Call base class method to continue normal event processing:
        return super(PlotImpz, self).eventFilter(source, event)

#-------------------------------------------------------------        
    def load_dict(self):
        """
        Reload textfields from filter dictionary 
        Transform the displayed frequency spec input fields according to the units
        setting (i.e. f_S). Spec entries are always stored normalized w.r.t. f_S 
        in the dictionary; when f_S or the unit are changed, only the displayed values
        of the frequency entries are updated, not the dictionary!

        load_dict() is called during init and when the frequency unit or the
        sampling frequency have been changed.

        It should be called when sigSpecsChanged or sigFilterDesigned is emitted
        at another place, indicating that a reload is required.
        """

        # recalculate displayed freq spec values for (maybe) changed f_S
        logger.debug("exec load_dict")
        if not self.ledFreq.hasFocus():
            # widget has no focus, round the display
            self.ledFreq.setText(
                str(params['FMT'].format(self.stim_freq * fb.fil[0]['f_S'])))
        else:
            # widget has focus, show full precision
            self.ledFreq.setText(str(self.stim_freq * fb.fil[0]['f_S']))

#------------------------------------------------------------------------------
    def init_axes(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_r.get_xaxis().tick_bottom() # remove axis ticks on top
            self.ax_r.get_yaxis().tick_left() # remove axis ticks right

            self.ax_i = self.mplwidget.fig.add_subplot(212, sharex = self.ax_r)
            self.ax_i.clear()
            self.ax_i.get_xaxis().tick_bottom() # remove axis ticks on top
            self.ax_i.get_yaxis().tick_left() # remove axis ticks right

        else:
            self.ax_r = self.mplwidget.fig.add_subplot(111)
            self.ax_r.clear()
            self.ax_r.get_xaxis().tick_bottom() # remove axis ticks on top
            self.ax_r.get_yaxis().tick_left() # remove axis ticks right


        self.mplwidget.fig.subplots_adjust(hspace = 0.5)  

        if self.ACTIVE_3D: # not implemented / tested yet
            self.ax3d = self.mplwidget.fig.add_subplot(111, projection='3d')

#------------------------------------------------------------------------------
    def update_view(self):
        """
        place holder; should update only the limits without recalculating
        the impulse respons
        """
        self.draw()

#------------------------------------------------------------------------------
    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            # self.init_axes() # called by self.draw
            self.draw()
#------------------------------------------------------------------------------
    def draw(self):
        if self.mplwidget.mplToolbar.enabled:
            self.draw_impz()

#------------------------------------------------------------------------------
    def draw_impz(self):
        """
        (Re-)calculate h[n] and draw the figure
        """
        log = self.chkLog.isChecked()
        stim = str(self.cmbStimulus.currentText())
        periodic_sig = stim in {"Cos", "Sine","Rect", "Saw"}
        self.lblLogBottom.setVisible(log)
        self.ledLogBottom.setVisible(log)
        self.lbldB.setVisible(log)
        
        self.lblFreq.setVisible(periodic_sig)
        self.ledFreq.setVisible(periodic_sig)
        self.lblFreqUnit.setVisible(periodic_sig)

        self.lblFreqUnit.setText(rt_label(fb.fil[0]['freq_specs_unit']))
        self.load_dict()
        
        self.bb = np.asarray(fb.fil[0]['ba'][0])
        self.aa = np.asarray(fb.fil[0]['ba'][1])
        if min(len(self.aa), len(self.bb)) < 2:
            logger.error('No proper filter coefficients: len(a), len(b) < 2 !')
            return

        sos = np.asarray(fb.fil[0]['sos'])
        antiCausal = 'zpkA' in fb.fil[0]
        causal     = not (antiCausal)

        self.f_S  = fb.fil[0]['f_S']
        
        N_entry = safe_eval(self.ledNPoints.text(), 0, return_type='int', sign='pos')
        N = self.calc_n_points(N_entry)
        if N_entry != 0: # automatic calculation
            self.ledNPoints.setText(str(N))

        self.A = safe_eval(self.ledAmp.text(), self.A, return_type='float')
        self.ledAmp.setText(str(self.A))

        t = np.linspace(0, N/self.f_S, N, endpoint=False)

        title_str = r'Impulse Response' # default
        H_str = r'$h[n]$' # default

        # calculate h[n]
        if stim == "Pulse":
            x = np.zeros(N)
            x[0] = self.A # create dirac impulse as input signal
        elif stim == "Step":
            x = self.A * np.ones(N) # create step function
            title_str = r'Step Response'
            H_str = r'$h_{\epsilon}[n]$'
        elif stim == "StepErr":
            x = self.A * np.ones(N) # create step function
            title_str = r'Settling Error'
            H_str = r'$h_{\epsilon, \infty} - h_{\epsilon}[n]$'
            
        elif stim in {"Cos"}:
            x = self.A * np.cos(2 * np.pi * t * float(self.ledFreq.text()))
            if stim == "Cos":
                title_str = r'Transient Response to Cosine Signal'
                H_str = r'$y_{\cos}[n]$'

        elif stim in {"Sine", "Rect"}:
            x = self.A * np.sin(2 * np.pi * t * float(self.ledFreq.text()))
            if stim == "Sine":
                title_str = r'Transient Response to Sine Signal'
                H_str = r'$y_{\sin}[n]$'
            else:
                x = self.A * np.sign(x)
                title_str = r'Transient Response to Rect. Signal'
                H_str = r'$y_{rect}[n]$'

        elif stim == "Saw":
            x = self.A * sig.sawtooth(t * (float(self.ledFreq.text())* 2*np.pi))
            title_str = r'Transient Response to Sawtooth Signal'
            H_str = r'$y_{saw}[n]$'

        elif stim == "RandN":
            x = self.A * np.random.randn(N)
            title_str = r'Transient Response to Gaussian Noise'
            H_str = r'$y_{gauss}[n]$'

        elif stim == "RandU":
            x = self.A * (np.random.rand(N)-0.5)
            title_str = r'Transient Response to Uniform Noise'
            H_str = r'$y_{uni}[n]$'

        else:
            logger.error('Unknown stimulus "{0}"'.format(stim))
            return

        if len(sos) > 0 and (causal): # has second order sections and is causal
            h = sig.sosfilt(sos, x)
        elif (antiCausal):
            h = sig.filtfilt(self.bb, self.aa, x, -1, None)
        else: # no second order sections or antiCausals for current filter
            h = sig.lfilter(self.bb, self.aa, x)

        dc = sig.freqz(self.bb, self.aa, [0])

        if stim == "StepErr":
            h = h - abs(dc[1]) # subtract DC value from response

        h = np.real_if_close(h, tol = 1e3)  # tol specified in multiples of machine eps
        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:
            self.bottom = safe_eval(self.ledLogBottom.text(), self.bottom, return_type='float')
            self.ledLogBottom.setText(str(self.bottom))
            H_str = r'$|$ ' + H_str + '$|$ in dB'
            h = np.maximum(20 * np.log10(abs(h)), self.bottom)
            if self.cmplx:
                h_i = np.maximum(20 * np.log10(abs(h_i)), self.bottom)
                H_i_str = r'$\log$ ' + H_i_str + ' in dB'
        else:
            self.bottom = 0

        self.init_axes()

        #================ Main Plotting Routine =========================
        [ml, sl, bl] = self.ax_r.stem(t, h, bottom=self.bottom, markerfmt='o', label = '$h[n]$')
        stem_fmt = params['mpl_stimuli']
        if self.chkPltStim.isChecked():
            [ms, ss, bs] = self.ax_r.stem(t, x, bottom=self.bottom, label = 'Stim.', **stem_fmt)
            ms.set_mfc(stem_fmt['mfc'])
            ms.set_mec(stem_fmt['mec'])
            ms.set_ms(stem_fmt['ms'])
            ms.set_alpha(stem_fmt['alpha'])
            for stem in ss:
                stem.set_linewidth(stem_fmt['lw'])
                stem.set_color(stem_fmt['mec'])
                stem.set_alpha(stem_fmt['alpha'])
            bs.set_visible(False) # invisible bottomline
        expand_lim(self.ax_r, 0.02)
        self.ax_r.set_title(title_str)

        if self.cmplx:
            [ml_i, sl_i, bl_i] = self.ax_i.stem(t, h_i, bottom=self.bottom,
                                                markerfmt='d', label = '$h_i[n]$')
            self.ax_i.set_xlabel(fb.fil[0]['plt_tLabel'])
            # self.ax_r.get_xaxis().set_ticklabels([]) # removes both xticklabels
            # plt.setp(ax_r.get_xticklabels(), visible=False) 
            # is shorter but imports matplotlib, set property directly instead:
            [label.set_visible(False) for label in self.ax_r.get_xticklabels()]
            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 / tested 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, alpha=.5)

            # plotting a circle on the top of each stem
            self.ax3d.plot(t, h, h_i, 'o', markersize=8,
                           markerfacecolor='none', label='$h[n]$')

            self.ax3d.set_xlabel('x')
            self.ax3d.set_ylabel('y')
            self.ax3d.set_zlabel('z')

        self.redraw()
        
#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()

#------------------------------------------------------------------------------        
    def calc_n_points(self, N_user = 0):
        """
        Calculate number of points to be displayed, depending on type of filter 
        (FIR, IIR) and user input. If the user selects 0 points, the number is
        calculated automatically.
        
        An improvement would be to calculate the dominant pole and the corresponding
        settling time.
        """

        if N_user == 0: # set number of data points automatically
            if fb.fil[0]['ft'] == 'IIR':
                N = 100 # TODO: IIR: more intelligent algorithm needed (based on transients)
            else:
                N = min(len(self.bb),  100) # FIR: N = number of coefficients (max. 100)
        else:
            N = N_user

        return N
Exemplo n.º 27
0
class PlotHf(QWidget):

    # 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):
        super(PlotHf, self).__init__(parent)

        modes = ['| H |', 're{H}', 'im{H}']
        self.cmbShowH = 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 = QLabel("in")

        units = ['dB', 'V', 'W', 'Auto']
        self.cmbUnitsA = 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(QComboBox.AdjustToContents)
        self.cmbUnitsA.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.lblLinphase = QLabel("Acausal system")
        self.chkLinphase = 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 = QLabel("Inset")

        self.cmbInset = 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 = QLabel("Show Specs")
        self.chkSpecs = QCheckBox()
        self.chkSpecs.setChecked(False)
        self.chkSpecs.setToolTip("Display filter specs as hatched regions")

        self.lblPhase = QLabel("Phase")
        self.chkPhase = QCheckBox()
        self.chkPhase.setToolTip("Overlay phase")

        self.layHChkBoxes = 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)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layHChkBoxes)

        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        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)

#------------------------------------------------------------------------------

    def init_axes(self):
        """
        Initialize and clear the axes
        """
        #        self.ax = self.mplwidget.ax
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()

#------------------------------------------------------------------------------

    def plot_spec_limits(self, ax):
        """
        Plot the specifications limits (F_SB, A_SB, ...) as lines and as
        hatched areas.
        """
        def dB(lin):
            return 20 * np.log10(lin)

        def _plot_specs():
            # upper limits:
            ax.plot(F_lim_upl, A_lim_upl, F_lim_upc, A_lim_upc, F_lim_upr,
                    A_lim_upr, **line_params)
            if A_lim_upl:
                ax.fill_between(F_lim_upl, max(A_lim_upl), A_lim_upl,
                                **fill_params)
            if A_lim_upc:
                ax.fill_between(F_lim_upc, max(A_lim_upc), A_lim_upc,
                                **fill_params)
            if A_lim_upr:
                ax.fill_between(F_lim_upr, max(A_lim_upr), A_lim_upr,
                                **fill_params)
            # lower limits:
            ax.plot(F_lim_lol, A_lim_lol, F_lim_loc, A_lim_loc, F_lim_lor,
                    A_lim_lor, **line_params)
            if A_lim_lol:
                ax.fill_between(F_lim_lol, min(A_lim_lol), A_lim_lol,
                                **fill_params)
            if A_lim_loc:
                ax.fill_between(F_lim_loc, min(A_lim_loc), A_lim_loc,
                                **fill_params)
            if A_lim_lor:
                ax.fill_between(F_lim_lor, min(A_lim_lor), A_lim_lor,
                                **fill_params)

#        fc = (0.8,0.8,0.8) # color for shaded areas

        fill_params = {
            'facecolor': 'none',
            'hatch': '/',
            'edgecolor': rcParams['figure.edgecolor'],
            'lw': 0.0
        }
        line_params = {'linewidth': 1.0, 'color': 'blue', 'linestyle': '--'}

        if self.unitA == 'V':
            exp = 1.
        elif self.unitA == 'W':
            exp = 2.

        if self.unitA == 'dB':
            if fb.fil[0]['ft'] == "FIR":
                A_PB_max = dB(1 + self.A_PB)
                A_PB2_max = dB(1 + self.A_PB2)
            else:  # IIR dB
                A_PB_max = A_PB2_max = 0

            A_PB_min = dB(1 - self.A_PB)
            A_PB2_min = dB(1 - self.A_PB2)
            A_PB_minx = min(A_PB_min, A_PB2_min) - 5
            A_PB_maxx = max(A_PB_max, A_PB2_max) + 5

            A_SB = dB(self.A_SB)
            A_SB2 = dB(self.A_SB2)
            A_SB_maxx = max(A_SB, A_SB2) + 10
        else:  # 'V' or 'W'
            if fb.fil[0]['ft'] == "FIR":
                A_PB_max = (1 + self.A_PB)**exp
                A_PB2_max = (1 + self.A_PB2)**exp
            else:  # IIR lin
                A_PB_max = A_PB2_max = 1

            A_PB_min = (1 - self.A_PB)**exp
            A_PB2_min = (1 - self.A_PB)**exp
            A_PB_minx = A_PB_min / 1.05
            A_PB_maxx = max(A_PB_max, A_PB2_max) * 1.05

            A_SB = self.A_SB**exp
            A_SB2 = self.A_SB2**exp
            A_SB_maxx = A_PB_min / 10.

        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

        F_lim_upl = F_lim_lol = []  # left side limits, lower and upper
        A_lim_upl = A_lim_lol = []

        F_lim_upc = F_lim_loc = []  # center limits, lower and upper
        A_lim_upc = A_lim_loc = []

        F_lim_upr = F_lim_lor = []  # right side limits, lower and upper
        A_lim_upr = A_lim_lor = []

        if fb.fil[0]['rt'] == 'LP':
            F_lim_upl = [0, F_PB, F_PB]
            A_lim_upl = [A_PB_max, A_PB_max, A_PB_maxx]
            F_lim_lol = F_lim_upl
            A_lim_lol = [A_PB_min, A_PB_min, A_PB_minx]

            F_lim_upr = [F_SB, F_SB, F_max]
            A_lim_upr = [A_SB_maxx, A_SB, A_SB]

        if fb.fil[0]['rt'] == 'HP':
            F_lim_upl = [0, F_SB, F_SB]
            A_lim_upl = [A_SB, A_SB, A_SB_maxx]

            F_lim_upr = [F_PB, F_PB, F_max]
            A_lim_upr = [A_PB_maxx, A_PB_max, A_PB_max]
            F_lim_lor = F_lim_upr
            A_lim_lor = [A_PB_minx, A_PB_min, A_PB_min]

        if fb.fil[0]['rt'] == 'BS':
            F_lim_upl = [0, F_PB, F_PB]
            A_lim_upl = [A_PB_max, A_PB_max, A_PB_maxx]
            F_lim_lol = F_lim_upl
            A_lim_lol = [A_PB_min, A_PB_min, A_PB_minx]

            F_lim_upc = [F_SB, F_SB, F_SB2, F_SB2]
            A_lim_upc = [A_SB_maxx, A_SB, A_SB, A_SB_maxx]

            F_lim_upr = [F_PB2, F_PB2, F_max]
            A_lim_upr = [A_PB_maxx, A_PB2_max, A_PB2_max]
            F_lim_lor = F_lim_upr
            A_lim_lor = [A_PB_minx, A_PB2_min, A_PB2_min]

        if fb.fil[0]['rt'] in {"BP", "HIL"}:
            F_lim_upl = [0, F_SB, F_SB]
            A_lim_upl = [A_SB, A_SB, A_SB_maxx]

            F_lim_upc = [F_PB, F_PB, F_PB2, F_PB2]
            A_lim_upc = [A_PB_maxx, A_PB_max, A_PB_max, A_PB_maxx]
            F_lim_loc = F_lim_upc
            A_lim_loc = [A_PB_minx, A_PB_min, A_PB_min, A_PB_minx]

            F_lim_upr = [F_SB2, F_SB2, F_max]
            A_lim_upr = [A_SB_maxx, A_SB2, A_SB2]

        F_lim_upr = np.array(F_lim_upr)
        F_lim_lor = np.array(F_lim_lor)
        F_lim_upl = np.array(F_lim_upl)
        F_lim_lol = np.array(F_lim_lol)
        F_lim_upc = np.array(F_lim_upc)
        F_lim_loc = np.array(F_lim_loc)

        _plot_specs()  # plot specs in the range 0 ... f_S/2

        if fb.fil[0]['freqSpecsRangeType'] != 'half':
            # add plot limits for other half of the spectrum
            if fb.fil[0][
                    'freqSpecsRangeType'] == 'sym':  # frequency axis +/- f_S/2
                F_lim_upl = -F_lim_upl
                F_lim_lol = -F_lim_lol
                F_lim_upc = -F_lim_upc
                F_lim_loc = -F_lim_loc
                F_lim_upr = -F_lim_upr
                F_lim_lor = -F_lim_lor
            else:  # -> 'whole'
                F_lim_upl = self.f_S - F_lim_upl
                F_lim_lol = self.f_S - F_lim_lol
                F_lim_upc = self.f_S - F_lim_upc
                F_lim_loc = self.f_S - F_lim_loc
                F_lim_upr = self.f_S - F_lim_upr
                F_lim_lor = self.f_S - F_lim_lor

            _plot_specs()

#------------------------------------------------------------------------------

    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.)

        # 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.get_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=rcParams['figure.facecolor'],
                                 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)

            if self.cmbInset.currentIndex() == 1:  # edit / navigate inset
                self.ax_i.set_navigate(True)
                self.ax.set_navigate(False)
                if self.specs:
                    self.plot_spec_limits(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()

#------------------------------------------------------------------------------

    def draw_phase(self, ax):
        """
        Draw phase on second y-axis in the axes system passed as the argument
        """
        try:
            self.mplwidget.fig.delaxes(self.ax_p)
        except (KeyError, AttributeError):
            pass

        if self.chkPhase.isChecked():
            self.ax_p = 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

            # replace nan and inf by finite values, otherwise np.unwrap yields
            # an array full of nans
            phi = np.angle(np.nan_to_num(self.H_c))
            #-----------------------------------------------------------
            self.ax_p.plot(self.F,
                           np.unwrap(phi) * scale,
                           'b--',
                           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 calc_hf(self):
        """
        (Re-)Calculate the complex frequency response H(f)
        """

        #        whole = fb.fil[0]['freqSpecsRangeType'] != 'half'

        # calculate H_cplx(W) (complex) for W = 0 ... 2 pi:
        [self.W,
         self.H_cplx] = sig.freqz(fb.fil[0]['ba'][0],
                                  fb.fil[0]['ba'][1],
                                  worN=rc.params['N_FFT'],
                                  whole=True)  # bb, aa, N_FFT, 0 ... 2 pi

#------------------------------------------------------------------------------

    def draw(self):
        """
        Re-calculate |H(f)| and draw the figure
        """
        if self.mplwidget.mplToolbar.enable_update:
            self.calc_hf()
            self.update_view()

#------------------------------------------------------------------------------

    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """
        if np.all(self.W) is None:  # H(f) has not been calculated yet
            self.calc_hf()

        if self.cmbUnitsA.currentText() == 'Auto':
            self.unitA = fb.fil[0]['amp_specs_unit']
        else:
            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.linphase = self.chkLinphase.isChecked()

        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']

        # shift, scale and select frequency range to be displayed:
        # W -> F, H_cplx -> H_c
        self.H_c = self.H_cplx
        self.F = self.W / (2 * np.pi) * self.f_S

        if fb.fil[0]['freqSpecsRangeType'] == 'sym':
            self.H_c = np.fft.fftshift(self.H_cplx)
            self.F -= self.f_S / 2.
        elif fb.fil[0]['freqSpecsRangeType'] == 'half':
            self.H_c = self.H_cplx[0:rc.params['N_FFT'] // 2]
            self.F = self.F[0:rc.params['N_FFT'] // 2]

        # now calculate mag / real / imaginary part of H_c:
        if self.linphase:  # remove the linear phase
            self.H_c = self.H_c * np.exp(
                1j * self.W[0:len(self.F)] * 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 = [
                    20 * np.log10(self.A_SB) - 10,
                    20 * np.log10(1 + 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 = [0, (self.A_PB + 1)]
                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 = [0, (1 + self.A_PB)**2.]
                self.H_plt = H * H.conj()
                H_str += ' in W ' + r'$\rightarrow $'

            #-----------------------------------------------------------
            self.ax.plot(self.F, self.H_plt, label='H(f)')
            self.draw_phase(self.ax)
            #-----------------------------------------------------------
            #     self.ax_bounds = [self.ax.get_ybound()[0], self.ax.get_ybound()[1]]#, self.ax.get]
            self.ax.set_xlim(f_lim)
            self.ax.set_ylim(A_lim)

            if self.specs: self.plot_spec_limits(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()
Exemplo n.º 28
0
class PlotTauG(QWidget):
    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)
        self.verbose = False  # suppress warnings

        # =============================================================================
        # #### not needed at the moment ###
        #         self.chkWarnings = QCheckBox("Enable Warnings", self)
        #         self.chkWarnings.setChecked(False)
        #         self.chkWarnings.setToolTip("Print warnings about singular group delay")
        #
        #         layHControls = QHBoxLayout()
        #         layHControls.addStretch(10)
        #         layHControls.addWidget(self.chkWarnings)
        #
        #         # This widget encompasses all control subwidgets:
        #         self.frmControls = QFrame(self)
        #         self.frmControls.setObjectName("frmControls")
        #         self.frmControls.setLayout(layHControls)
        #
        # =============================================================================
        self.mplwidget = MplWidget(self)
        #        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()
        self.draw()  # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)

#------------------------------------------------------------------------------

    def init_axes(self):
        """
        Initialize and clear the axes
        """
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()
        self.ax.get_xaxis().tick_bottom()  # remove axis ticks on top
        self.ax.get_yaxis().tick_left()  # remove axis ticks right

#------------------------------------------------------------------------------

    def calc_tau_g(self):
        """
        (Re-)Calculate the complex frequency response H(f)
        """
        bb = fb.fil[0]['ba'][0]
        aa = fb.fil[0]['ba'][1]

        # calculate H_cmplx(W) (complex) for W = 0 ... 2 pi:
        self.W, self.tau_g = grpdelay(
            bb, aa, params['N_FFT'], whole=True,
            verbose=self.verbose)  # self.chkWarnings.isChecked())

        # Zero phase filters have no group delay (Causal+AntiCausal)
        if 'baA' in fb.fil[0]:
            self.tau_g = np.zeros(self.tau_g.size)

#------------------------------------------------------------------------------

    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
        #        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.init_axes()
            self.draw()

#------------------------------------------------------------------------------

    def draw(self):
        if self.mplwidget.mplToolbar.enabled:
            self.calc_tau_g()
            self.update_view()

#------------------------------------------------------------------------------

    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """
        #========= select frequency range to be displayed =====================
        #=== shift, scale and select: W -> F, H_cplx -> H_c
        f_S2 = fb.fil[0]['f_S'] / 2.
        F = self.W * f_S2 / np.pi

        if fb.fil[0]['freqSpecsRangeType'] == 'sym':
            # shift tau_g and F by f_S/2
            tau_g = np.fft.fftshift(self.tau_g)
            F -= f_S2
        elif fb.fil[0]['freqSpecsRangeType'] == 'half':
            # only use the first half of H and F
            tau_g = self.tau_g[0:params['N_FFT'] // 2]
            F = F[0:params['N_FFT'] // 2]
        else:  # fb.fil[0]['freqSpecsRangeType'] == 'whole'
            # use H and F as calculated
            tau_g = self.tau_g

        #================ Main Plotting Routine =========================
        #===  clear the axes and (re)draw the plot

        if fb.fil[0]['freq_specs_unit'] in {'f_S', 'f_Ny'}:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega}) / T_S \; \rightarrow $'
        else:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega})$'\
                + ' in ' + fb.fil[0]['plt_tUnit'] + r' $ \rightarrow $'
            tau_g = tau_g / fb.fil[0]['f_S']

        #---------------------------------------------------------
        self.ax.clear()  # need to clear, doesn't overwrite
        line_tau_g, = self.ax.plot(F, tau_g, label="Group Delay")
        #---------------------------------------------------------

        self.ax.set_title(r'Group Delay $ \tau_g$')
        self.ax.set_xlabel(fb.fil[0]['plt_fLabel'])
        self.ax.set_ylabel(tau_str)
        # widen y-limits to suppress numerical inaccuracies when tau_g = constant
        self.ax.set_ylim([max(min(tau_g) - 0.5, 0), max(tau_g) + 0.5])
        self.ax.set_xlim(fb.fil[0]['freqSpecsRange'])

        self.redraw()

#------------------------------------------------------------------------------

    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
Exemplo n.º 29
0
    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)
Exemplo n.º 30
0
class Plot3D(QtGui.QWidget):
    """
    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):
        super(Plot3D, self).__init__(parent)
        self.zmin = 0
        self.zmax = 4
        self.zmin_dB = -80
        self.cmap_default = 'RdYlBu_r'
        self._init_UI()

    def _init_UI(self):
        self.chkLog = QtGui.QCheckBox(self)
        self.chkLog.setText("Log.")
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("Logarithmic scale")
        self.chkLog.setChecked(False)

        self.chkPolar = QtGui.QCheckBox(self)
        self.chkPolar.setText("Polar")
        self.chkPolar.setObjectName("chkPolar")
        self.chkPolar.setToolTip("Polar coordinates")
        self.chkPolar.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.chkUC = QtGui.QCheckBox(self)
        self.chkUC.setText("UC")
        self.chkUC.setObjectName("chkUC")
        self.chkUC.setToolTip("Plot unit circle")
        self.chkUC.setChecked(True)

        self.chkPZ = QtGui.QCheckBox(self)
        self.chkPZ.setText("P/Z")
        self.chkPZ.setObjectName("chkPZ")
        self.chkPZ.setToolTip("Plot poles and zeros")
        self.chkPZ.setChecked(True)

        self.chkHf = QtGui.QCheckBox(self)
        self.chkHf.setText("H(f)")
        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.chkColormap_r = QtGui.QCheckBox(self)
        self.chkColormap_r.setText("reverse")
        self.chkColormap_r.setToolTip("reverse colormap")
        self.chkColormap_r.setChecked(True)

        self.cmbColormap = QtGui.QComboBox(self)
        self._init_cmb_colormap()
        self.cmbColormap.setToolTip("Select colormap")

        self.chkColBar = QtGui.QCheckBox(self)
        self.chkColBar.setText("Colorbar")
        self.chkColBar.setObjectName("chkColBar")
        self.chkColBar.setToolTip("Show colorbar")
        self.chkColBar.setChecked(False)

        self.chkLighting = QtGui.QCheckBox(self)
        self.chkLighting.setText("Lighting")
        self.chkLighting.setObjectName("chkLighting")
        self.chkLighting.setToolTip("Enable light source")
        self.chkLighting.setChecked(False)

        self.lblAlpha = QtGui.QLabel("Alpha")
        self.diaAlpha = QtGui.QDial(self)
        self.diaAlpha.setRange(0., 10.)
        self.diaAlpha.setValue(10)
        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 3D-contour plot.")

        self.lblHatch = QtGui.QLabel("Stride")
        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.chkContour2D = QtGui.QCheckBox(self)
        self.chkContour2D.setText("Contour2D")
        self.chkContour2D.setObjectName("chkContour2D")
        self.chkContour2D.setToolTip("Plot 2D-contours at z =0")
        self.chkContour2D.setChecked(False)

        #----------------------------------------------------------------------
        # LAYOUT for UI widgets
        #----------------------------------------------------------------------
        spc = QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Expanding,
                                QtGui.QSizePolicy.Minimum)
        self.layGSelect = QtGui.QGridLayout()
        self.layGSelect.setObjectName('plotSpecSelect')
        self.layGSelect.addWidget(self.chkLog, 0, 0)
        self.layGSelect.addWidget(self.chkPolar, 1, 0)
        self.layGSelect.addWidget(self.lblTop, 0, 2)
        self.layGSelect.addWidget(self.lblBottom, 1, 2)
        self.layGSelect.addWidget(self.ledTop, 0, 4)
        self.layGSelect.addWidget(self.ledBottom, 1, 4)
        self.layGSelect.addItem(spc, 0, 5)

        self.layGSelect.addWidget(self.chkUC, 0, 6)
        self.layGSelect.addWidget(self.chkHf, 1, 6)
        self.layGSelect.addWidget(self.chkPZ, 0, 8)
        self.layGSelect.addWidget(self.cmbColormap, 0, 10, 1, 1)
        self.layGSelect.addWidget(self.chkColormap_r, 1, 10)
        self.layGSelect.addWidget(self.cmbMode3D, 0, 12)
        self.layGSelect.addWidget(self.chkContour2D, 1, 12)
        self.layGSelect.addWidget(self.chkLighting, 0, 14)
        self.layGSelect.addWidget(self.chkColBar, 1, 14)

        self.layGSelect.addWidget(self.diaAlpha, 0, 16)
        self.layGSelect.addWidget(self.lblAlpha, 0, 15)
        self.layGSelect.addWidget(self.diaHatch, 1, 16)
        self.layGSelect.addWidget(self.lblHatch, 1, 15)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layGSelect)

        self.setLayout(self.mplwidget.layVMainMpl)

        #        self.mplwidget.setFocus()
        # make this the central widget, taking all available space:
        #        self.setCentralWidget(self.mplwidget)

        self._init_grid()  # initialize grid and do initial plot

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self._log_clicked)
        self.ledBottom.editingFinished.connect(self._log_clicked)
        self.ledTop.editingFinished.connect(self._log_clicked)

        self.chkPolar.clicked.connect(self._init_grid)
        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.cmbColormap.currentIndexChanged.connect(self.draw)
        self.chkColormap_r.clicked.connect(self._init_cmb_colormap)

        self.chkLighting.clicked.connect(self.draw)
        self.diaAlpha.valueChanged.connect(self.draw)
        self.diaHatch.valueChanged.connect(self.draw)
        self.chkContour2D.clicked.connect(self.draw)

        logger.debug("UI initialized")

#------------------------------------------------------------------------------

    def _init_cmb_colormap(self):
        """ (Re-)Load combobox with available colormaps"""
        if self.chkColormap_r.isChecked():
            cmap_list = [m for m in cm.datad if m.endswith("_r")]
        else:
            cmap_list = [m for m in cm.datad if not m.endswith("_r")]
        # *_r colormaps reverse the color order
        cmap_list.sort()
        self.cmbColormap.blockSignals(True)  # don't send signal "indexChanged"
        self.cmbColormap.clear()
        self.cmbColormap.addItems(cmap_list)
        self.cmbColormap.blockSignals(False)

        idx = self.cmbColormap.findText(self.cmap_default)
        if idx == -1:
            idx = 0
        self.cmbColormap.setCurrentIndex(idx)

#------------------------------------------------------------------------------

    def _init_grid(self):
        """ Initialize (x,y,z) coordinate grid + (re)draw plot."""
        phi_UC = np.linspace(0, 2 * pi, 400,
                             endpoint=True)  # angles for unit circle
        self.xy_UC = np.exp(1j * phi_UC)  # x,y coordinates of unity circle

        steps = 100  # number of steps for x, y, r, phi
        #
        self.xmin = -1.5
        self.xmax = 1.5  # cartesian range limits
        self.ymin = -1.5
        self.ymax = 1.5

        rmin = 0
        rmax = self.xmin  # polar range limits

        # Calculate grids for 3D-Plots
        dr = rmax / steps * 2  # grid size for polar range
        dx = (self.xmax - self.xmin) / steps
        dy = (self.ymax - self.ymin) / steps  # grid size cartesian range

        if self.chkPolar.isChecked():  # # Plot circular range in 3D-Plot
            [r,
             phi] = np.meshgrid(np.arange(rmin, rmax, dr),
                                np.linspace(0, 2 * pi, steps, endpoint=True))
            self.x = r * cos(phi)
            self.y = r * sin(phi)
        else:  # cartesian grid
            [self.x, self.y] = np.meshgrid(np.arange(self.xmin, self.xmax, dx),
                                           np.arange(self.ymin, self.ymax, dy))

        self.z = self.x + 1j * self.y  # create coordinate grid for complex plane

        self.draw()  # initial plot

#------------------------------------------------------------------------------

    def _init_axes(self):
        """
        Initialize and clear the axes to get rid of colorbar
        The azimuth / elevation / distance settings of the camera are restored
        after clearing the axes. See
        http://stackoverflow.com/questions/4575588/matplotlib-3d-plot-with-pyqt4-in-qtabwidget-mplwidget
        """

        self._save_axes()

        self.mplwidget.fig.clf()  # needed to get rid of colorbar
        self.ax3d = self.mplwidget.fig.add_subplot(111, projection='3d')

        self._restore_axes()

#------------------------------------------------------------------------------

    def _save_axes(self):
        """
        Store x/y/z - limits and camera position
        """

        try:
            self.azim = self.ax3d.azim
            self.elev = self.ax3d.elev
            self.dist = self.ax3d.dist
            self.xlim = self.ax3d.get_xlim3d()
            self.ylim = self.ax3d.get_ylim3d()
            self.zlim = self.ax3d.get_zlim3d()

        except AttributeError:  # not yet initialized, set standard values
            self.azim = -65
            self.elev = 30
            self.dist = 10
            self.xlim = (self.xmin, self.xmax)
            self.ylim = (self.ymin, self.ymax)
            self.zlim = (self.zmin, self.zmax)

#------------------------------------------------------------------------------

    def _restore_axes(self):
        """
        Restore x/y/z - limits and camera position
        """
        if self.mplwidget.mplToolbar.lock_zoom:
            self.ax3d.set_xlim3d(self.xlim)
            self.ax3d.set_ylim3d(self.ylim)
            self.ax3d.set_zlim3d(self.zlim)
        self.ax3d.azim = self.azim
        self.ax3d.elev = self.elev
        self.ax3d.dist = self.dist

#------------------------------------------------------------------------------

    def _log_clicked(self):
        """
        Change scale and settings to log / lin when log setting is changed
        Update min / max settings when lineEdits have been edited        
        """
        self.log = self.chkLog.isChecked()
        if self.sender().objectName(
        ) == 'chkLog':  # clicking chkLog 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:  # finishing a lineEdit field triggered the slot
            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):
        """
        Main drawing entry point: Check whether updating is enabled in the
        toolbar and then perform the actual plot
        """
        if self.mplwidget.mplToolbar.enable_update:
            self.draw_3d()

#------------------------------------------------------------------------------

    def draw_3d(self):
        """
        Draw various 3D plots
        """
        self._init_axes()

        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'  # not used
        f_S = fb.fil[0]['f_S']
        N_FFT = rc.params['N_FFT']

        alpha = self.diaAlpha.value() / 10.
        cmap = cm.get_cmap(str(self.cmbColormap.currentText()))
        # Number of Lines /step size for H(f) stride, mesh, contour3d:

        stride = 10 - self.diaHatch.value()
        NL = 3 * self.diaHatch.value() + 5

        #cNorm  = colors.Normalize(vmin=0, vmax=values[-1])
        #scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)

        #-----------------------------------------------------------------------------
        # Calculate H(w) along the upper half of unity circle
        #-----------------------------------------------------------------------------

        [w, H] = sig.freqz(bb, aa, worN=N_FFT, whole=True)
        H = np.nan_to_num(H)  # replace nans and inf by finite numbers

        H_abs = abs(H)
        H_max = max(H_abs)
        H_min = min(H_abs)
        #f = w / (2 * pi) * f_S                  # translate w to absolute frequencies
        #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

        if self.chkLog.isChecked():  # logarithmic scale
            bottom = np.floor(max(self.zmin_dB, 20 * log10(H_min)) / 10) * 10
            top = self.zmax_dB
            top_bottom = top - bottom

            zlevel = bottom - top_bottom * zlevel_rel

            if self.cmbMode3D.currentText(
            ) == 'None':  # "Poleposition" for H(f) plot only
                plevel_top = 2 * bottom - zlevel  # height of displayed pole position
                plevel_btm = bottom
            else:
                plevel_top = top + top_bottom * (plevel_rel - 1)
                plevel_btm = top

        else:  # linear scale
            bottom = max(self.zmin, H_min)  # min. display value
            top = self.zmax  # max. display value
            top_bottom = top - bottom
            #   top = zmax_rel * H_max # calculate display top from max. of H(f)

            zlevel = bottom + top_bottom * zlevel_rel  # height of displayed zero position

            if self.cmbMode3D.currentText(
            ) == 'None':  # "Poleposition" for H(f) plot only
                #H_max = np.clip(max(H_abs), 0, self.zmax)
                # make height of displayed poles same to zeros
                plevel_top = bottom + top_bottom * zlevel_rel
                plevel_btm = bottom
            else:
                plevel_top = plevel_rel * top
                plevel_btm = top

        # calculate H(jw)| along the unity circle and |H(z)|, each clipped
        # between bottom and top
        H_UC = H_mag(bb,
                     aa,
                     self.xy_UC,
                     top,
                     H_min=bottom,
                     log=self.chkLog.isChecked())
        Hmag = H_mag(bb,
                     aa,
                     self.z,
                     top,
                     H_min=bottom,
                     log=self.chkLog.isChecked())

        #===============================================================
        ## plot Unit Circle (UC)
        #===============================================================
        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, alpha=0.5)
            # draw once more as dashed white line to improve visibility
            self.ax3d.plot(self.xy_UC.real, self.xy_UC.imag, H_UC, 'w--')

            if stride < 10:  # plot thin vertical line every stride points on the UC
                for k in range(len(self.xy_UC[::stride])):
                    self.ax3d.plot([
                        self.xy_UC.real[::stride][k],
                        self.xy_UC.real[::stride][k]
                    ], [
                        self.xy_UC.imag[::stride][k],
                        self.xy_UC.imag[::stride][k]
                    ], [
                        np.ones(len(self.xy_UC[::stride]))[k] * bottom,
                        H_UC[::stride][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
        #===============================================================

        m_cb = cm.ScalarMappable(
            cmap=cmap)  # normalized proxy object that is mappable
        m_cb.set_array(Hmag)  # for colorbar

        #---------------------------------------------------------------
        ## 3D-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(self.x,
                                     self.y,
                                     Hmag,
                                     rstride=5,
                                     cstride=stride,
                                     linewidth=1,
                                     color='gray')

        #---------------------------------------------------------------
        ## 3D-surface plot
        #---------------------------------------------------------------
        # http://stackoverflow.com/questions/28232879/phong-shading-for-shiny-python-3d-surface-plots
        elif self.cmbMode3D.currentText() == 'Surf':
            if MLAB:
                ## Mayavi
                surf = mlab.surf(self.x,
                                 self.y,
                                 H_mag,
                                 colormap='RdYlBu',
                                 warp_scale='auto')
                # Change the visualization parameters.
                surf.actor.property.interpolation = 'phong'
                surf.actor.property.specular = 0.1
                surf.actor.property.specular_power = 5
                #                s = mlab.contour_surf(self.x, self.y, Hmag, contour_z=0)
                mlab.show()

            else:
                if self.chkLighting.isChecked():
                    ls = LightSource(azdeg=0,
                                     altdeg=65)  # Create light source object
                    rgb = ls.shade(
                        Hmag, cmap=cmap)  # Shade data, creating an rgb array
                    cmap_surf = None
                else:
                    rgb = None
                    cmap_surf = cmap

    #            s = self.ax3d.plot_surface(self.x, self.y, Hmag,
    #                    alpha=OPT_3D_ALPHA, rstride=1, cstride=1, cmap=cmap,
    #                    linewidth=0, antialiased=False, shade=True, facecolors = rgb)
    #            s.set_edgecolor('gray')
                s = self.ax3d.plot_surface(self.x,
                                           self.y,
                                           Hmag,
                                           alpha=alpha,
                                           rstride=1,
                                           cstride=1,
                                           linewidth=0,
                                           antialiased=False,
                                           facecolors=rgb,
                                           cmap=cmap_surf,
                                           shade=True)
                s.set_edgecolor(None)
        #---------------------------------------------------------------
        ## 3D-Contour plot
        #---------------------------------------------------------------
        elif self.cmbMode3D.currentText() == 'Contour':
            s = self.ax3d.contourf3D(self.x,
                                     self.y,
                                     Hmag,
                                     NL,
                                     alpha=alpha,
                                     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(self.x,
                                   self.y,
                                   Hmag,
                                   NL,
                                   zdir='z',
                                   offset=bottom - (top - bottom) * 0.05,
                                   cmap=cmap,
                                   alpha=alpha)

        # plot colorbar for suitable plot modes
        if self.chkColBar.isChecked() and (self.chkContour2D.isChecked()
                                           or str(self.cmbMode3D.currentText())
                                           in {'Contour', 'Surf'}):
            self.colb = self.mplwidget.fig.colorbar(m_cb,
                                                    ax=self.ax3d,
                                                    shrink=0.8,
                                                    aspect=20,
                                                    pad=0.02,
                                                    fraction=0.08)

        #----------------------------------------------------------------------
        ## Set view limits and labels
        #----------------------------------------------------------------------
        if not self.mplwidget.mplToolbar.lock_zoom:
            self.ax3d.set_xlim3d(self.xmin, self.xmax)
            self.ax3d.set_ylim3d(self.ymin, self.ymax)
            self.ax3d.set_zlim3d(bottom, top)
        else:
            self._restore_axes()

        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()
Exemplo n.º 31
0
    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)
Exemplo n.º 32
0
class PlotImpz(QWidget):
    def __init__(self, parent):
        super(PlotImpz, self).__init__(parent)

        self.ACTIVE_3D = False
        self.stim_freq = 0.02
        self._init_UI()

    def _init_UI(self):
        self.chkLog = QCheckBox(self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("<span>Logarithmic scale for y-axis.</span>")
        self.chkLog.setChecked(False)
        self.lblLog = QLabel("Log. y-axis", self)

        self.lblLogBottom = QLabel("Bottom = ", self)
        self.ledLogBottom = QLineEdit(self)
        self.ledLogBottom.setText("-80")
        self.ledLogBottom.setToolTip("Minimum display value for log. scale.")
        self.lbldB = QLabel("dB")

        self.lblPltStim = QLabel(self)
        self.lblPltStim.setText("Stimulus:")
        self.chkPltStim = QCheckBox("Show", self)
        self.chkPltStim.setChecked(False)
        self.chkPltStim.setToolTip("Show stimulus signal.")

        self.lblStimulus = QLabel("Type = ", self)
        self.cmbStimulus = QComboBox(self)
        self.cmbStimulus.addItems([
            "Pulse", "Step", "StepErr", "Sine", "Rect", "Saw", "RandN", "RandU"
        ])
        self.cmbStimulus.setToolTip("Select stimulus type.")

        self.lblFreq = QLabel("<i>f</i>&nbsp; =", self)

        self.ledFreq = QLineEdit(self)
        self.ledFreq.setText(str(self.stim_freq))
        self.ledFreq.setToolTip("Stimulus frequency.")

        self.lblFreqUnit = QLabel("f_S", self)

        self.lblNPoints = QLabel("<i>N</i>&nbsp; =", self)

        self.ledNPoints = QLineEdit(self)
        self.ledNPoints.setText("0")
        self.ledNPoints.setToolTip(
            "Number of points to calculate and display.\n"
            "N = 0 selects automatically.")

        layHControls = QHBoxLayout()

        layHControls.addWidget(self.lblNPoints)
        layHControls.addWidget(self.ledNPoints)
        layHControls.addStretch(2)
        layHControls.addWidget(self.chkLog)
        layHControls.addWidget(self.lblLog)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblLogBottom)
        layHControls.addWidget(self.ledLogBottom)
        layHControls.addWidget(self.lbldB)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblPltStim)
        layHControls.addWidget(self.chkPltStim)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblStimulus)
        layHControls.addWidget(self.cmbStimulus)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblFreq)
        layHControls.addWidget(self.ledFreq)
        layHControls.addWidget(self.lblFreqUnit)

        layHControls.addStretch(10)

        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self.draw)
        self.ledNPoints.editingFinished.connect(self.draw)
        self.ledLogBottom.editingFinished.connect(self.draw)
        self.chkPltStim.clicked.connect(self.draw)
        #        self.cmbStimulus.currentIndexChanged.connect(self.draw)
        self.cmbStimulus.activated.connect(self.draw)
        self.ledFreq.installEventFilter(self)

        self.draw()  # initial calculation and drawing

#------------------------------------------------------------------------------

    def eventFilter(self, source, event):
        """
        Filter all events generated by the QLineEdit widgets. Source and type
        of all events generated by monitored objects are passed to this eventFilter,
        evaluated and passed on to the next hierarchy level.

        - When a QLineEdit widget gains input focus (QEvent.FocusIn`), display
          the stored value from filter dict with full precision
        - When a key is pressed inside the text field, set the `spec_edited` flag
          to True.
        - When a QLineEdit widget loses input focus (QEvent.FocusOut`), store
          current value normalized to f_S with full precision (only if
          `spec_edited`== True) and display the stored value in selected format
        """
        def _store_entry(source):
            if self.spec_edited:
                self.stim_freq = safe_eval(source.text()) / fb.fil[0]['f_S']
                self.spec_edited = False  # reset flag
                self.draw()

        if isinstance(source,
                      QLineEdit):  # could be extended for other widgets
            if event.type() == QEvent.FocusIn:
                self.spec_edited = False
                self.load_dict()
            elif event.type() == QEvent.KeyPress:
                self.spec_edited = True  # entry has been changed
                key = event.key()
                if key in {Qt.Key_Return, Qt.Key_Enter}:
                    _store_entry(source)
                elif key == Qt.Key_Escape:  # revert changes
                    self.spec_edited = False
                    source.setText(
                        str(params['FMT'].format(self.stim_freq *
                                                 fb.fil[0]['f_S'])))

            elif event.type() == QEvent.FocusOut:
                _store_entry(source)
                source.setText(
                    str(params['FMT'].format(self.stim_freq *
                                             fb.fil[0]['f_S'])))
        # Call base class method to continue normal event processing:
        return super(PlotImpz, self).eventFilter(source, event)

#-------------------------------------------------------------

    def load_dict(self):
        """
        Reload textfields from filter dictionary 
        Transform the displayed frequency spec input fields according to the units
        setting (i.e. f_S). Spec entries are always stored normalized w.r.t. f_S 
        in the dictionary; when f_S or the unit are changed, only the displayed values
        of the frequency entries are updated, not the dictionary!

        load_dict() is called during init and when the frequency unit or the
        sampling frequency have been changed.

        It should be called when sigSpecsChanged or sigFilterDesigned is emitted
        at another place, indicating that a reload is required.
        """

        # recalculate displayed freq spec values for (maybe) changed f_S
        logger.debug("exec load_dict")
        if not self.ledFreq.hasFocus():
            # widget has no focus, round the display
            self.ledFreq.setText(
                str(params['FMT'].format(self.stim_freq * fb.fil[0]['f_S'])))
        else:
            # widget has focus, show full precision
            self.ledFreq.setText(str(self.stim_freq * fb.fil[0]['f_S']))

#------------------------------------------------------------------------------

    def _init_axes(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, sharex=self.ax_r)
            self.ax_i.clear()
        else:
            self.ax_r = self.mplwidget.fig.add_subplot(111)
            self.ax_r.clear()

        self.mplwidget.fig.subplots_adjust(hspace=0.5)

        if self.ACTIVE_3D:  # not implemented / tested yet
            self.ax3d = self.mplwidget.fig.add_subplot(111, projection='3d')

#------------------------------------------------------------------------------

    def update_view(self):
        """
        place holder; should update only the limits without recalculating
        the impulse respons
        """
        self.draw()

#------------------------------------------------------------------------------

    def draw(self):
        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.draw_impz()

#------------------------------------------------------------------------------

    def draw_impz(self):
        """
        (Re-)calculate h[n] and draw the figure
        """
        log = self.chkLog.isChecked()
        stim = str(self.cmbStimulus.currentText())
        periodic_sig = stim in {"Sine", "Rect", "Saw"}
        self.lblLogBottom.setVisible(log)
        self.ledLogBottom.setVisible(log)
        self.lbldB.setVisible(log)

        self.lblFreq.setVisible(periodic_sig)
        self.ledFreq.setVisible(periodic_sig)
        self.lblFreqUnit.setVisible(periodic_sig)

        self.lblFreqUnit.setText(rt_label(fb.fil[0]['freq_specs_unit']))
        self.load_dict()

        self.bb = np.asarray(fb.fil[0]['ba'][0])
        self.aa = np.asarray(fb.fil[0]['ba'][1])
        if min(len(self.aa), len(self.bb)) < 2:
            logger.error('No proper filter coefficients: len(a), len(b) < 2 !')
            return

        sos = np.asarray(fb.fil[0]['sos'])

        self.f_S = fb.fil[0]['f_S']

        N = self.calc_n_points(abs(int(self.ledNPoints.text())))

        t = np.linspace(0, N / self.f_S, N, endpoint=False)
        # calculate h[n]
        if stim == "Pulse":
            x = np.zeros(N)
            x[0] = 1.0  # create dirac impulse as input signal
            title_str = r'Impulse Response'
            H_str = r'$h[n]$'
        elif stim == "Step":
            x = np.ones(N)  # create step function
            title_str = r'Step Response'
            H_str = r'$h_{\epsilon}[n]$'
        elif stim == "StepErr":
            x = np.ones(N)  # create step function
            title_str = r'Settling Error'
            H_str = r'$h_{\epsilon, \infty} - h_{\epsilon}[n]$'

        elif stim in {"Sine", "Rect"}:
            x = np.sin(2 * np.pi * t * float(self.ledFreq.text()))
            if stim == "Sine":
                title_str = r'Transient Response to Sine Signal'
                H_str = r'$y_{\sin}[n]$'
            else:
                x = np.sign(x)
                title_str = r'Transient Response to Rect. Signal'
                H_str = r'$y_{rect}[n]$'

        elif stim == "Saw":
            x = sig.sawtooth(t * (float(self.ledFreq.text()) * 2 * np.pi))
            title_str = r'Transient Response to Sawtooth Signal'
            H_str = r'$y_{saw}[n]$'

        elif stim == "RandN":
            x = np.random.randn(N)
            title_str = r'Transient Response to Gaussian Noise'
            H_str = r'$y_{gauss}[n]$'

        elif stim == "RandU":
            x = np.random.rand(N) - 0.5
            title_str = r'Transient Response to Uniform Noise'
            H_str = r'$y_{uni}[n]$'

        else:
            logger.error('Unknown stimulus "{0}"'.format(stim))
            return

        if len(sos) > 0:  # has second order sections
            h = sig.sosfilt(sos, x)
            dc = sig.freqz(self.bb, self.aa, [0])
        else:  # no second order sections for current filter
            h = sig.lfilter(self.bb, self.aa, x)
            dc = sig.freqz(self.bb, self.aa, [0])

        if stim == "StepErr":
            h = h - abs(dc[1])  # subtract DC value from response

        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'$|$ ' + 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._init_axes()

        #================ Main Plotting Routine =========================
        [ml, sl, bl] = self.ax_r.stem(t,
                                      h,
                                      bottom=bottom,
                                      markerfmt='o',
                                      label='$h[n]$')
        stem_fmt = params['mpl_stimuli']
        if self.chkPltStim.isChecked():
            [ms, ss, bs] = self.ax_r.stem(t,
                                          x,
                                          bottom=bottom,
                                          label='Stim.',
                                          **stem_fmt)
            ms.set_mfc(stem_fmt['mfc'])
            ms.set_mec(stem_fmt['mec'])
            ms.set_ms(stem_fmt['ms'])
            ms.set_alpha(stem_fmt['alpha'])
            for stem in ss:
                stem.set_linewidth(stem_fmt['lw'])
                stem.set_color(stem_fmt['mec'])
                stem.set_alpha(stem_fmt['alpha'])
            bs.set_visible(False)  # invisible bottomline
        expand_lim(self.ax_r, 0.02)
        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='d',
                                                label='$h_i[n]$')
            self.ax_i.set_xlabel(fb.fil[0]['plt_tLabel'])
            # self.ax_r.get_xaxis().set_ticklabels([]) # removes both xticklabels
            # plt.setp(ax_r.get_xticklabels(), visible=False)
            # is shorter but imports matplotlib, set property directly instead:
            [label.set_visible(False) for label in self.ax_r.get_xticklabels()]
            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 / tested 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,
                               alpha=.5)

            # plotting a circle on the top of each stem
            self.ax3d.plot(t,
                           h,
                           h_i,
                           'o',
                           markersize=8,
                           markerfacecolor='none',
                           label='$h[n]$')

            self.ax3d.set_xlabel('x')
            self.ax3d.set_ylabel('y')
            self.ax3d.set_zlabel('z')

        self.redraw()

#------------------------------------------------------------------------------

    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()

#------------------------------------------------------------------------------

    def calc_n_points(self, N_user=0):
        """
        Calculate number of points to be displayed, depending on type of filter 
        (FIR, IIR) and user input. If the user selects 0 points, the number is
        calculated automatically.
        
        An improvement would be to calculate the dominant pole and the corresponding
        settling time.
        """

        if N_user == 0:  # set number of data points automatically
            if fb.fil[0]['ft'] == 'IIR':
                N = 100  # TODO: IIR: more intelligent algorithm needed
            else:
                N = min(len(self.bb),
                        100)  # FIR: N = number of coefficients (max. 100)
        else:
            N = N_user

        return N
Exemplo n.º 33
0
    def __init__(self, parent): 
        super(PlotHf, self).__init__(parent)

        modes = ['| H |', 're{H}', 'im{H}']
        self.cmbShowH = 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 = QLabel("in", self)

        units = ['dB', 'V', 'W', 'Auto']
        self.cmbUnitsA = QComboBox(self)
        self.cmbUnitsA.addItems(units)
        self.cmbUnitsA.setObjectName("cmbUnitsA")
        self.cmbUnitsA.setToolTip("<span>Set unit for y-axis:\n"
        "dB is attenuation (positive values), V and W are gain (less than 1).</span>")
        self.cmbUnitsA.setCurrentIndex(0)

        self.cmbShowH.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbUnitsA.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.chkLinphase = QCheckBox("Zero phase", self)
        self.chkLinphase.setToolTip("<span>Subtract linear phase according to filter order.\n"
           "Attention: this makes no sense for a non-linear phase system!</span>")

        self.lblInset = QLabel("Inset", self)
        self.cmbInset = 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.chkSpecs = QCheckBox("Show Specs", self)
        self.chkSpecs.setChecked(False)
        self.chkSpecs.setToolTip("Display filter specs as hatched regions")

        self.chkPhase = QCheckBox("Phase", self)
        self.chkPhase.setToolTip("Overlay phase")


        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        layHControls.addWidget(self.cmbShowH)
        layHControls.addWidget(self.lblIn)
        layHControls.addWidget(self.cmbUnitsA)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkLinphase)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblInset)
        layHControls.addWidget(self.cmbInset)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkSpecs)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkPhase)
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        # This is the plot pane widget, encompassing the other widgets        
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        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)
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)        
Exemplo n.º 34
0
class PlotPhi(QWidget):

    def __init__(self, parent):
        super(PlotPhi, self).__init__(parent)

        self.cmbUnitsPhi = 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(QComboBox.AdjustToContents)

        self.chkWrap = QCheckBox("Wrapped Phase", self)
        self.chkWrap.setChecked(False)
        self.chkWrap.setToolTip("Plot phase wrapped to +/- pi")
        
        layHControls = QHBoxLayout()
#        layHControls.addStretch(10)
        layHControls.addWidget(self.cmbUnitsPhi)
        layHControls.addWidget(self.chkWrap)
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)


        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        self.draw() # initial drawing

#        #=============================================
#        # Signals & Slots
#        #=============================================
        self.chkWrap.clicked.connect(self.draw)
        self.cmbUnitsPhi.currentIndexChanged.connect(self.draw)
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)        

#------------------------------------------------------------------------------
    def init_axes(self):
        """
        Initialize and clear the axes
        """
#        self.ax = self.mplwidget.ax
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()
        self.ax.get_xaxis().tick_bottom() # remove axis ticks on top
        self.ax.get_yaxis().tick_left() # remove axis ticks right

#------------------------------------------------------------------------------
    def calc_hf(self):
        """
        (Re-)Calculate the complex frequency response H(f)
        """
        # calculate H_cplx(W) (complex) for W = 0 ... 2 pi:
        self.W, self.H_cmplx = calc_Hcomplex(fb.fil[0], params['N_FFT'], wholeF=True)
        # replace nan and inf by finite values, otherwise np.unwrap yields
        # an array full of nans
        self.H_cmplx = np.nan_to_num(self.H_cmplx) 

#------------------------------------------------------------------------------
    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.init_axes()
            self.draw()

#------------------------------------------------------------------------------
    def draw(self):
        """
        Main entry point: 
        Re-calculate |H(f)| and draw the figure if enabled
        """
        if self.mplwidget.mplToolbar.enabled:
            self.calc_hf()
            self.update_view()

#------------------------------------------------------------------------------
    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """

        self.unitPhi = self.cmbUnitsPhi.currentText()

        f_S2 = fb.fil[0]['f_S'] / 2.

        #========= select frequency range to be displayed =====================
        #=== shift, scale and select: W -> F, H_cplx -> H_c
        F = self.W * f_S2 / np.pi

        if fb.fil[0]['freqSpecsRangeType'] == 'sym':
            # shift H and F by f_S/2
            H = np.fft.fftshift(self.H_cmplx)
            F -= f_S2
        elif fb.fil[0]['freqSpecsRangeType'] == 'half':
            # only use the first half of H and F
            H = self.H_cmplx[0:params['N_FFT']//2]
            F = F[0:params['N_FFT']//2]
        else: # fb.fil[0]['freqSpecsRangeType'] == 'whole'
            # use H and F as calculated
            H = self.H_cmplx

        y_str = r'$\angle H(\mathrm{e}^{\mathrm{j} \Omega})$ in '
        if self.unitPhi == 'rad':
            y_str += 'rad ' + r'$\rightarrow $'
            scale = 1.
        elif self.unitPhi == 'rad/pi':
            y_str += 'rad' + r'$ / \pi \;\rightarrow $'
            scale = 1./ np.pi
        else:
            y_str += 'deg ' + r'$\rightarrow $'
            scale = 180./np.pi
        fb.fil[0]['plt_phiLabel'] = y_str
        fb.fil[0]['plt_phiUnit'] = self.unitPhi
        
        if self.chkWrap.isChecked():
            phi_plt = np.angle(H) * scale
        else:
            phi_plt = np.unwrap(np.angle(H)) * scale

        #---------------------------------------------------------
        self.ax.clear() # need to clear, doesn't overwrite 
        line_phi, = self.ax.plot(F, phi_plt)
        #---------------------------------------------------------

        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.redraw()
        
#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
Exemplo n.º 35
0
    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)
Exemplo n.º 36
0
class PlotPZ(QWidget):

    def __init__(self, parent): 
        super(PlotPZ, self).__init__(parent)

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        
        # This widget encompasses all control subwidgets:
#        self.frmControls = QFrame(self)
#        self.frmControls.setLayout(layHControls)


        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addLayout(layHControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        # make this the central widget, taking all available space:
 #       self.setCentralWidget(self.mplwidget)
        
        self.init_axes()

        self.draw() # calculate and draw poles and zeros

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)

#------------------------------------------------------------------------------
    def init_axes(self):
        """Initialize and clear the axes
        """
#        self.ax = self.mplwidget.ax
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()
        self.ax.get_xaxis().tick_bottom() # remove axis ticks on top
        self.ax.get_yaxis().tick_left() # remove axis ticks right

#------------------------------------------------------------------------------
    def update_specs(self):
        """
        Draw the figure with new limits, scale etcs without recalculating H(f)
        -- not yet implemented, just use draw() for the moment
        """
        self.draw()

#------------------------------------------------------------------------------
    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
        # self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled) # no control widgets
        if self.mplwidget.mplToolbar.enabled:
            self.init_axes()
            self.draw()

#------------------------------------------------------------------------------
    def draw(self):
        if self.mplwidget.mplToolbar.enabled:
            self.draw_pz()
            
#------------------------------------------------------------------------------
    def draw_pz(self):
        """
        (re)draw P/Z plot
        """
        p_marker = params['P_Marker']
        z_marker = params['Z_Marker']
        
        zpk = fb.fil[0]['zpk']

        # add antiCausals if they exist (must take reciprocal to plot)
        if 'rpk' in fb.fil[0]:
            zA = fb.fil[0]['zpk'][0]
            zA = np.conj(1./zA)
            pA = fb.fil[0]['zpk'][1]
            pA = np.conj(1./pA)
            zC = np.append(zpk[0],zA)
            pC = np.append(zpk[1],pA)
            zpk[0] = zC
            zpk[1] = pC

        self.ax.clear()

        [z,p,k] = self.zplane(z = zpk[0], p = zpk[1], k = zpk[2], plt_ax = self.ax, verbose = False,
            mps = p_marker[0], mpc = p_marker[1], mzs = z_marker[0], mzc = z_marker[1])

        self.ax.set_title(r'Pole / Zero Plot')
        self.ax.set_xlabel('Real axis')
        self.ax.set_ylabel('Imaginary axis')

        self.redraw()
        
#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
        
        
#------------------------------------------------------------------------------
    def zplane(self, b=None, a=1, z=None, p=None, k =1,  pn_eps=1e-3, analog=False, plt_ax = None,
              verbose=False, style='square', anaCircleRad=0, lw=2,
              mps = 10, mzs = 10, mpc = 'r', mzc = 'b', plabel = '', zlabel = ''):
        """
        Plot the poles and zeros in the complex z-plane either from the
        coefficients (`b,`a) of a discrete transfer function `H`(`z`) (zpk = False)
        or directly from the zeros and poles (z,p) (zpk = True).
    
        When only b is given, an FIR filter with all poles at the origin is assumed.
    
        Parameters
        ----------
        b :  array_like
             Numerator coefficients (transversal part of filter)
             When b is not None, poles and zeros are determined from the coefficients
             b and a
    
        a :  array_like (optional, default = 1 for FIR-filter)
             Denominator coefficients (recursive part of filter)
             
        z :  array_like, default = None
             Zeros
             When b is None, poles and zeros are taken directly from z and p
    
        p :  array_like, default = None
             Poles
    
        analog : boolean (default: False)
            When True, create a P/Z plot suitable for the s-plane, i.e. suppress
            the unit circle (unless anaCircleRad > 0) and scale the plot for
            a good display of all poles and zeros.
    
        pn_eps : float (default : 1e-2)
             Tolerance for separating close poles or zeros
             
        plt_ax : handle to axes for plotting (default: None)
            When no axes is specified, the current axes is determined via plt.gca()
    
        pltLib :  string (default: 'matplotlib')
             Library for plotting the P/Z plane. Currently, only matplotlib is
             implemented. When pltLib = 'none' or when matplotlib is not
             available, only pass the poles / zeros and their multiplicity
    
        verbose : boolean (default: False)
            When verbose == True, print poles / zeros and their multiplicity.
    
        style : string (default: 'square')
            Style of the plot, for style == 'square' make scale of x- and y-
            axis equal.
    
        mps : integer  (default: 10)
            Size for pole marker
            
        mzs : integer (default: 10)
            Size for zero marker
            
        mpc : char (default: 'r')
            Pole marker colour
            
        mzc : char (default: 'b')
            Zero marker colour
            
        lw : integer (default:  2)
            Linewidth for unit circle
    
        plabel, zlabel : string (default: '')
            This string is passed to the plot command for poles and zeros and
            can be displayed by legend()
    
    
        Returns
        -------
        z, p, k : ndarray
    
    
        Notes
        -----
    
        """
        # TODO:
        # - polar option
        # - add keywords for size, color etc. of markers and circle -> **kwargs
        # - add option for multi-dimensional arrays and zpk data
    
        # make sure that all inputs are arrays
        b = np.atleast_1d(b) 
        a = np.atleast_1d(a)
        z = np.atleast_1d(z) # make sure that p, z  are arrays
        p = np.atleast_1d(p)
    
        if b.any(): # coefficients were specified
            if len(b) < 2 and len(a) < 2:
                logger.error('No proper filter coefficients: both b and a are scalars!')
                return z, p, k
            
            # The coefficients are less than 1, normalize the coefficients
            if np.max(b) > 1:
                kn = np.max(b)
                b = b / float(kn) 
            else:
                kn = 1.
    
            if np.max(a) > 1:
                kd = np.max(a)
                a = a / abs(kd)
            else:
                kd = 1.
    
            # Calculate the poles, zeros and scaling factor
            p = np.roots(a)
            z = np.roots(b)
            k = kn/kd
        elif not (len(p) or len(z)): # P/Z were specified
            logger.error('Either b,a or z,p must be specified!')
            return z, p, k
  
        # find multiple poles and zeros and their multiplicities
        if len(p) < 2: # single pole, [None] or [0]
            if not p or p == 0: # only zeros, create equal number of poles at origin
                p = np.array(0,ndmin=1) # 
                num_p = np.atleast_1d(len(z))
            else:
                num_p = [1.] # single pole != 0
        else:
            #p, num_p = sig.signaltools.unique_roots(p, tol = pn_eps, rtype='avg')
            p, num_p = unique_roots(p, tol = pn_eps, rtype='avg')
    #        p = np.array(p); num_p = np.ones(len(p))
        if len(z) > 0:
            z, num_z = unique_roots(z, tol = pn_eps, rtype='avg')
    #        z = np.array(z); num_z = np.ones(len(z))
            #z, num_z = sig.signaltools.unique_roots(z, tol = pn_eps, rtype='avg')
        else:
            num_z = []
    
    
        ax = plt_ax#.subplot(111)
        if analog == False:
            # create the unit circle for the z-plane
            uc = patches.Circle((0,0), radius=1, fill=False,
                                color='grey', ls='solid', zorder=1)
            ax.add_patch(uc)
            if style == 'square':
                r = 1.1
                ax.axis([-r, r, -r, r], 'equal')
                ax.axis('equal')
        #    ax.spines['left'].set_position('center')
        #    ax.spines['bottom'].set_position('center')
        #    ax.spines['right'].set_visible(True)
        #    ax.spines['top'].set_visible(True)
    
        else: # s-plane
            if anaCircleRad > 0:
                # plot a circle with radius = anaCircleRad
                uc = patches.Circle((0,0), radius=anaCircleRad, fill=False,
                                    color='grey', ls='solid', zorder=1)
                ax.add_patch(uc)
            # plot real and imaginary axis
            ax.axhline(lw=2, color = 'k', zorder=1)
            ax.axvline(lw=2, color = 'k', zorder=1)
    
        # Plot the zeros
        ax.scatter(z.real, z.imag, s=mzs*mzs, zorder=2, marker = 'o',
                   facecolor = 'none', edgecolor = mzc, lw = lw, label=zlabel)
    #        t1 = plt.plot(z.real, z.imag, 'go', ms=10, label=label)
    #        plt.setp( t1, markersize=mzs, markeredgewidth=2.0,
    #                  markeredgecolor=mzc, markerfacecolor='none')
        # Plot the poles
        ax.scatter(p.real, p.imag, s=mps*mps, zorder=2, marker='x',
                   color=mpc, lw=lw, label=plabel)
    
         # Print multiplicity of poles / zeros
        for i in range(len(z)):
            if verbose == True: print('z', i, z[i], num_z[i])
            if num_z[i] > 1:
                ax.text(np.real(z[i]), np.imag(z[i]),'  (' + str(num_z[i]) +')',va = 'bottom')
    
        for i in range(len(p)):
            if verbose == True: print('p', i, p[i], num_p[i])
            if num_p[i] > 1:
                ax.text(np.real(p[i]), np.imag(p[i]), '  (' + str(num_p[i]) +')',va = 'bottom')
    
            # increase distance between ticks and labels
            # to give some room for poles and zeros
        for tick in ax.get_xaxis().get_major_ticks():
            tick.set_pad(12.)
            tick.label1 = tick._get_text1()
        for tick in ax.get_yaxis().get_major_ticks():
            tick.set_pad(12.)
            tick.label1 = tick._get_text1()
    
        xl = ax.get_xlim(); Dx = max(abs(xl[1]-xl[0]), 0.05)
        yl = ax.get_ylim(); Dy = max(abs(yl[1]-yl[0]), 0.05)
        ax.set_xlim((xl[0]-Dx*0.05, max(xl[1]+Dx*0.05,0)))
        ax.set_ylim((yl[0]-Dy*0.05, yl[1] + Dy*0.05))
    #    print(ax.get_xlim(),ax.get_ylim())
    
        return z, p, k
Exemplo n.º 37
0
class Plot3D(QWidget):
    """
    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):
        super(Plot3D, self).__init__(parent)
        self.zmin = 0
        self.zmax = 4
        self.zmin_dB = -80
        self.cmap_default = 'RdYlBu_r'
        self._construct_UI()

    def _construct_UI(self):
        self.chkLog = QCheckBox("Log.", self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("Logarithmic scale")
        self.chkLog.setChecked(False)

        self.chkPolar = QCheckBox("Polar", self)
        self.chkPolar.setObjectName("chkPolar")
        self.chkPolar.setToolTip("Polar coordinate range")
        self.chkPolar.setChecked(False)

        self.lblBottom = QLabel("Bottom =", self)
        self.ledBottom = QLineEdit(self)
        self.ledBottom.setObjectName("ledBottom")
        self.ledBottom.setText(str(self.zmin))
        self.ledBottom.setToolTip("Minimum display value.")

        self.lblTop = QLabel("Top:", self)
        self.ledTop = QLineEdit(self)
        self.ledTop.setObjectName("ledTop")
        self.ledTop.setText(str(self.zmax))
        self.ledTop.setToolTip("Maximum display value.")

        self.chkUC = QCheckBox("UC", self)
        self.chkUC.setObjectName("chkUC")
        self.chkUC.setToolTip("Plot unit circle")
        self.chkUC.setChecked(True)

        self.chkPZ = QCheckBox("P/Z", self)
        self.chkPZ.setObjectName("chkPZ")
        self.chkPZ.setToolTip("Plot poles and zeros")
        self.chkPZ.setChecked(True)

        self.chkHf = QCheckBox("H(f)", 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 = QComboBox(self)
        self.cmbMode3D.addItems(modes)
        self.cmbMode3D.setObjectName("cmbShow3D")
        self.cmbMode3D.setToolTip("Select 3D-plot mode.")
        self.cmbMode3D.setCurrentIndex(0)
        self.cmbMode3D.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.chkColormap_r = QCheckBox("reverse", self)
        self.chkColormap_r.setToolTip("reverse colormap")
        self.chkColormap_r.setChecked(True)

        self.cmbColormap = QComboBox(self)
        self._init_cmb_colormap()
        self.cmbColormap.setToolTip("Select colormap")

        self.chkColBar = QCheckBox("Colorbar", self)
        self.chkColBar.setObjectName("chkColBar")
        self.chkColBar.setToolTip("Show colorbar")
        self.chkColBar.setChecked(False)

        self.chkLighting = QCheckBox("Lighting", self)
        self.chkLighting.setObjectName("chkLighting")
        self.chkLighting.setToolTip("Enable light source")
        self.chkLighting.setChecked(False)

        self.lblAlpha = QLabel("Alpha", self)
        self.diaAlpha = QDial(self)
        self.diaAlpha.setRange(0., 10.)
        self.diaAlpha.setValue(10)
        self.diaAlpha.setTracking(False) # produce less events when turning
        self.diaAlpha.setFixedHeight(30)
        self.diaAlpha.setFixedWidth(30)
        self.diaAlpha.setWrapping(False)
        self.diaAlpha.setToolTip("<span>Set transparency for surf and contour plots.</span>")

        self.lblHatch = QLabel("Stride", self)
        self.diaHatch = 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 line density for various plots.")

        self.chkContour2D = QCheckBox("Contour2D", self)
        self.chkContour2D.setObjectName("chkContour2D")
        self.chkContour2D.setToolTip("Plot 2D-contours at z =0")
        self.chkContour2D.setChecked(False)

        #----------------------------------------------------------------------
        # LAYOUT for UI widgets
        #----------------------------------------------------------------------

        layGControls = QGridLayout()
        layGControls.addWidget(self.chkLog, 0, 0)
        layGControls.addWidget(self.chkPolar, 1, 0)
        layGControls.addWidget(self.lblTop, 0, 2)
        layGControls.addWidget(self.lblBottom, 1, 2)
        layGControls.addWidget(self.ledTop, 0, 4)
        layGControls.addWidget(self.ledBottom, 1, 4)
        layGControls.setColumnStretch(5,1)

        layGControls.addWidget(self.chkUC, 0, 6)
        layGControls.addWidget(self.chkHf, 1, 6)
        layGControls.addWidget(self.chkPZ, 0, 8)
        
        layGControls.addWidget(self.cmbMode3D, 0, 10)
        layGControls.addWidget(self.chkContour2D, 1, 10)        
        layGControls.addWidget(self.cmbColormap, 0,12,1,1)
        layGControls.addWidget(self.chkColormap_r, 1,12)

        layGControls.addWidget(self.chkLighting, 0, 14)
        layGControls.addWidget(self.chkColBar, 1, 14)

        layGControls.addWidget(self.lblAlpha, 0, 15)
        layGControls.addWidget(self.diaAlpha, 0, 16)

        layGControls.addWidget(self.lblHatch, 1, 15)
        layGControls.addWidget(self.diaHatch, 1, 16)

        # This widget encompasses all control subwidgets   
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layGControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        # This is the plot pane widget, encompassing the other widgets        
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)
        
        self._init_grid() # initialize grid and do initial plot

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self._log_clicked)
        self.ledBottom.editingFinished.connect(self._log_clicked)
        self.ledTop.editingFinished.connect(self._log_clicked)

        self.chkPolar.clicked.connect(self._init_grid)
        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.cmbColormap.currentIndexChanged.connect(self.draw)
        self.chkColormap_r.clicked.connect(self._init_cmb_colormap)

        self.chkLighting.clicked.connect(self.draw)
        self.diaAlpha.valueChanged.connect(self.draw)
        self.diaHatch.valueChanged.connect(self.draw)
        self.chkContour2D.clicked.connect(self.draw)

        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)
        self.mplwidget.mplToolbar.enable_update(state = False) # disable initially


#------------------------------------------------------------------------------
    def _init_cmb_colormap(self):
        """ (Re-)Load combobox with available colormaps"""
        if self.chkColormap_r.isChecked():
            cmap_list = [m for m in cm.datad if m.endswith("_r")]
        else:
            cmap_list = [m for m in cm.datad if not m.endswith("_r")]
        # *_r colormaps reverse the color order
        cmap_list.sort()
        self.cmbColormap.blockSignals(True) # don't send signal "indexChanged"
        self.cmbColormap.clear()
        self.cmbColormap.addItems(cmap_list)
        self.cmbColormap.blockSignals(False)
        
        idx = self.cmbColormap.findText(self.cmap_default)
        if idx == -1:
            idx = 0
        self.cmbColormap.setCurrentIndex(idx)


#------------------------------------------------------------------------------
    def _init_grid(self):
        """ Initialize (x,y,z) coordinate grid + (re)draw plot."""
        phi_UC = np.linspace(0, 2*pi, 400, endpoint=True) # angles for unit circle
        self.xy_UC = np.exp(1j * phi_UC) # x,y coordinates of unity circle

        steps = 100              # number of steps for x, y, r, phi
        #
        self.xmin = -1.5; self.xmax = 1.5  # cartesian range limits
        self.ymin = -1.5; self.ymax = 1.5
        
        rmin = 0;    rmax = self.xmin  # polar range limits

        # Calculate grids for 3D-Plots
        dr = rmax / steps * 2 # grid size for polar range
        dx = (self.xmax - self.xmin) / steps
        dy = (self.ymax - self.ymin) / steps # grid size cartesian range

        if self.chkPolar.isChecked(): # # Plot circular range in 3D-Plot
            [r, phi] = np.meshgrid(np.arange(rmin, rmax, dr),
                            np.linspace(0, 2 * pi, steps, endpoint=True))
            self.x = r * cos(phi)
            self.y = r * sin(phi)
        else: # cartesian grid
            [self.x, self.y] = np.meshgrid(np.arange(self.xmin, self.xmax, dx),
                                            np.arange(self.ymin, self.ymax, dy))

        self.z = self.x + 1j*self.y # create coordinate grid for complex plane

        self.draw() # initial plot

#------------------------------------------------------------------------------
    def init_axes(self):
        """
        Initialize and clear the axes to get rid of colorbar
        The azimuth / elevation / distance settings of the camera are restored
        after clearing the axes. See
        http://stackoverflow.com/questions/4575588/matplotlib-3d-plot-with-pyqt4-in-qtabwidget-mplwidget
        """
                
        self._save_axes()

        self.mplwidget.fig.clf() # needed to get rid of colorbar
        self.ax3d = self.mplwidget.fig.add_subplot(111, projection='3d')

        self._restore_axes()

#------------------------------------------------------------------------------
    def _save_axes(self):
        """
        Store x/y/z - limits and camera position
        """
                
        try:
            self.azim = self.ax3d.azim
            self.elev = self.ax3d.elev
            self.dist = self.ax3d.dist
            self.xlim = self.ax3d.get_xlim3d()
            self.ylim = self.ax3d.get_ylim3d()
            self.zlim = self.ax3d.get_zlim3d()
 
        except AttributeError: # not yet initialized, set standard values
            self.azim = -65
            self.elev = 30
            self.dist = 10
            self.xlim = (self.xmin, self.xmax)
            self.ylim = (self.ymin, self.ymax)
            self.zlim = (self.zmin, self.zmax)


#------------------------------------------------------------------------------
    def _restore_axes(self):
        """
        Restore x/y/z - limits and camera position
        """
        if self.mplwidget.mplToolbar.lock_zoom:
            self.ax3d.set_xlim3d(self.xlim)
            self.ax3d.set_ylim3d(self.ylim)
            self.ax3d.set_zlim3d(self.zlim)
        self.ax3d.azim = self.azim
        self.ax3d.elev = self.elev
        self.ax3d.dist = self.dist


#------------------------------------------------------------------------------
    def _log_clicked(self):
        """
        Change scale and settings to log / lin when log setting is changed
        Update min / max settings when lineEdits have been edited        
        """
        self.log = self.chkLog.isChecked()
        if self.sender().objectName() == 'chkLog': # clicking chkLog 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: # finishing a lineEdit field triggered the slot
            if self.log:
                self.zmin_dB = safe_eval(self.ledBottom.text(), self.zmin_dB, return_type='float')
                self.ledBottom.setText(str(self.zmin_dB))
                self.zmax_dB = safe_eval(self.ledTop.text(), self.zmax_dB, return_type='float')
                self.ledTop.setText(str(self.zmax_dB))
            else:
                self.zmin = safe_eval(self.ledBottom.text(), self.zmin, return_type='float')
                self.ledBottom.setText(str(self.zmin))
                self.zmax = safe_eval(self.ledTop.text(), self.zmax, return_type='float')
                self.ledTop.setText(str(self.zmax))

        self.draw()


#------------------------------------------------------------------------------
    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.init_axes()
            self.draw()

#------------------------------------------------------------------------------
    def draw(self):
        """
        Main drawing entry point: Check whether updating is enabled in the
        toolbar and then perform the actual plot
        """
        if self.mplwidget.mplToolbar.enabled:
            self.draw_3d()

#------------------------------------------------------------------------------
    def draw_3d(self):
        """
        Draw various 3D plots
        """
        self.init_axes()

        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' # not used
        f_S = fb.fil[0]['f_S']
        N_FFT = params['N_FFT']
        
        alpha = self.diaAlpha.value()/10.
        cmap = cm.get_cmap(str(self.cmbColormap.currentText()))
        # Number of Lines /step size for H(f) stride, mesh, contour3d:

        stride = 10 - self.diaHatch.value() 
        NL = 3 * self.diaHatch.value() + 5
        
        surf_enabled = qget_cmb_box(self.cmbMode3D, data=False) in {'Surf', 'Contour'}
        self.cmbColormap.setEnabled(surf_enabled)
        self.chkColormap_r.setEnabled(surf_enabled)
        self.chkLighting.setEnabled(surf_enabled)
        self.chkColBar.setEnabled(surf_enabled)
        self.diaAlpha.setEnabled(surf_enabled or self.chkContour2D.isChecked())

        #cNorm  = colors.Normalize(vmin=0, vmax=values[-1])
        #scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)

        #-----------------------------------------------------------------------------
        # Calculate H(w) along the upper half of unity circle
        #-----------------------------------------------------------------------------


        [w, H] = sig.freqz(bb, aa, worN=N_FFT, whole=True)
        H = np.nan_to_num(H) # replace nans and inf by finite numbers
       
        H_abs = abs(H)
        H_max = max(H_abs)
        H_min = min(H_abs)
        #f = w / (2 * pi) * f_S                  # translate w to absolute frequencies
        #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


        if self.chkLog.isChecked(): # logarithmic scale
            bottom = np.floor(max(self.zmin_dB, 20*log10(H_min)) / 10) * 10
            top = self.zmax_dB
            top_bottom = top - bottom
            
            zlevel = bottom - top_bottom * zlevel_rel

            if self.cmbMode3D.currentText() == 'None': # "Poleposition" for H(f) plot only
                plevel_top = 2 * bottom - zlevel # height of displayed pole position
                plevel_btm = bottom
            else:
                plevel_top = top + top_bottom * (plevel_rel - 1)
                plevel_btm = top

        else: # linear scale
            bottom = max(self.zmin, H_min)  # min. display value
            top = self.zmax                 # max. display value
            top_bottom = top - bottom
        #   top = zmax_rel * H_max # calculate display top from max. of H(f)

            zlevel = bottom + top_bottom * zlevel_rel # height of displayed zero position

            if self.cmbMode3D.currentText() == 'None': # "Poleposition" for H(f) plot only
                #H_max = np.clip(max(H_abs), 0, self.zmax)     
                # make height of displayed poles same to zeros
                plevel_top = bottom + top_bottom * zlevel_rel 
                plevel_btm = bottom
            else:
                plevel_top = plevel_rel * top
                plevel_btm = top
                
        # calculate H(jw)| along the unity circle and |H(z)|, each clipped
        # between bottom and top
        H_UC = H_mag(bb, aa, self.xy_UC, top, H_min=bottom, log=self.chkLog.isChecked())
        Hmag = H_mag(bb, aa, self.z, top, H_min=bottom, log=self.chkLog.isChecked())


        #===============================================================
        ## plot Unit Circle (UC)
        #===============================================================
        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, alpha = 0.5)
            # draw once more as dashed white line to improve visibility
            self.ax3d.plot(self.xy_UC.real, self.xy_UC.imag, H_UC, 'w--')

            if stride < 10:  # plot thin vertical line every stride points on the UC
                for k in range(len(self.xy_UC[::stride])):
                    self.ax3d.plot([self.xy_UC.real[::stride][k], self.xy_UC.real[::stride][k]],
                        [self.xy_UC.imag[::stride][k], self.xy_UC.imag[::stride][k]],
                        [np.ones(len(self.xy_UC[::stride]))[k]*bottom, H_UC[::stride][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
        #===============================================================

        m_cb = cm.ScalarMappable(cmap=cmap)  # normalized proxy object that is mappable
        m_cb.set_array(Hmag)                 # for colorbar

        #---------------------------------------------------------------
        ## 3D-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(self.x, self.y, Hmag, rstride=5,
                    cstride=stride, linewidth=1, color='gray')

        #---------------------------------------------------------------
        ## 3D-surface plot
        #---------------------------------------------------------------
        # http://stackoverflow.com/questions/28232879/phong-shading-for-shiny-python-3d-surface-plots
        elif self.cmbMode3D.currentText() == 'Surf':
            if MLAB:
                ## Mayavi
                surf = mlab.surf(self.x, self.y, H_mag, colormap='RdYlBu', warp_scale='auto')
                # Change the visualization parameters.
                surf.actor.property.interpolation = 'phong'
                surf.actor.property.specular = 0.1
                surf.actor.property.specular_power = 5
#                s = mlab.contour_surf(self.x, self.y, Hmag, contour_z=0)
                mlab.show()
                

            else:
                if self.chkLighting.isChecked():
                    ls = LightSource(azdeg=0, altdeg=65) # Create light source object
                    rgb = ls.shade(Hmag, cmap=cmap) # Shade data, creating an rgb array
                    cmap_surf = None
                else:
                    rgb = None
                    cmap_surf = cmap

    #            s = self.ax3d.plot_surface(self.x, self.y, Hmag,
    #                    alpha=OPT_3D_ALPHA, rstride=1, cstride=1, cmap=cmap,
    #                    linewidth=0, antialiased=False, shade=True, facecolors = rgb)
    #            s.set_edgecolor('gray')
                s = self.ax3d.plot_surface(self.x, self.y, Hmag,
                        alpha=alpha, rstride=1, cstride=1,
                        linewidth=0, antialiased=False, facecolors=rgb, cmap=cmap_surf, shade=True)
                s.set_edgecolor(None)
        #---------------------------------------------------------------
        ## 3D-Contour plot
        #---------------------------------------------------------------
        elif self.cmbMode3D.currentText() == 'Contour':
            s = self.ax3d.contourf3D(self.x, self.y, Hmag, NL, alpha=alpha, 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(self.x, self.y, Hmag, NL, zdir='z',
                               offset=bottom - (top - bottom) * 0.05,
                                cmap=cmap, alpha=alpha)
            
        # plot colorbar for suitable plot modes
        if self.chkColBar.isChecked() and (self.chkContour2D.isChecked() or
                str(self.cmbMode3D.currentText()) in {'Contour', 'Surf'}):
                            self.colb = self.mplwidget.fig.colorbar(m_cb,
                                ax=self.ax3d, shrink=0.8, aspect=20,
                                pad=0.02, fraction=0.08)

        #----------------------------------------------------------------------
        ## Set view limits and labels
        #----------------------------------------------------------------------
        if not self.mplwidget.mplToolbar.lock_zoom:
            self.ax3d.set_xlim3d(self.xmin, self.xmax)
            self.ax3d.set_ylim3d(self.ymin, self.ymax)
            self.ax3d.set_zlim3d(bottom, top)
        else:
            self._restore_axes()

        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.redraw()
        
#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
Exemplo n.º 38
0
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] = grpdelay(bb,aa, rc.params['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, 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()
Exemplo n.º 39
0
    def _construct_UI(self):
        self.chkLog = QCheckBox("Log.", self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("Logarithmic scale")
        self.chkLog.setChecked(False)

        self.chkPolar = QCheckBox("Polar", self)
        self.chkPolar.setObjectName("chkPolar")
        self.chkPolar.setToolTip("Polar coordinate range")
        self.chkPolar.setChecked(False)

        self.lblBottom = QLabel("Bottom =", self)
        self.ledBottom = QLineEdit(self)
        self.ledBottom.setObjectName("ledBottom")
        self.ledBottom.setText(str(self.zmin))
        self.ledBottom.setToolTip("Minimum display value.")

        self.lblTop = QLabel("Top:", self)
        self.ledTop = QLineEdit(self)
        self.ledTop.setObjectName("ledTop")
        self.ledTop.setText(str(self.zmax))
        self.ledTop.setToolTip("Maximum display value.")

        self.chkUC = QCheckBox("UC", self)
        self.chkUC.setObjectName("chkUC")
        self.chkUC.setToolTip("Plot unit circle")
        self.chkUC.setChecked(True)

        self.chkPZ = QCheckBox("P/Z", self)
        self.chkPZ.setObjectName("chkPZ")
        self.chkPZ.setToolTip("Plot poles and zeros")
        self.chkPZ.setChecked(True)

        self.chkHf = QCheckBox("H(f)", 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 = QComboBox(self)
        self.cmbMode3D.addItems(modes)
        self.cmbMode3D.setObjectName("cmbShow3D")
        self.cmbMode3D.setToolTip("Select 3D-plot mode.")
        self.cmbMode3D.setCurrentIndex(0)
        self.cmbMode3D.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.chkColormap_r = QCheckBox("reverse", self)
        self.chkColormap_r.setToolTip("reverse colormap")
        self.chkColormap_r.setChecked(True)

        self.cmbColormap = QComboBox(self)
        self._init_cmb_colormap()
        self.cmbColormap.setToolTip("Select colormap")

        self.chkColBar = QCheckBox("Colorbar", self)
        self.chkColBar.setObjectName("chkColBar")
        self.chkColBar.setToolTip("Show colorbar")
        self.chkColBar.setChecked(False)

        self.chkLighting = QCheckBox("Lighting", self)
        self.chkLighting.setObjectName("chkLighting")
        self.chkLighting.setToolTip("Enable light source")
        self.chkLighting.setChecked(False)

        self.lblAlpha = QLabel("Alpha", self)
        self.diaAlpha = QDial(self)
        self.diaAlpha.setRange(0., 10.)
        self.diaAlpha.setValue(10)
        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 3D-contour plot.")

        self.lblHatch = QLabel("Stride", self)
        self.diaHatch = 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.chkContour2D = QCheckBox("Contour2D", self)
        self.chkContour2D.setObjectName("chkContour2D")
        self.chkContour2D.setToolTip("Plot 2D-contours at z =0")
        self.chkContour2D.setChecked(False)

        #----------------------------------------------------------------------
        # LAYOUT for UI widgets
        #----------------------------------------------------------------------

        layGControls = QGridLayout()
        layGControls.addWidget(self.chkLog, 0, 0)
        layGControls.addWidget(self.chkPolar, 1, 0)
        layGControls.addWidget(self.lblTop, 0, 2)
        layGControls.addWidget(self.lblBottom, 1, 2)
        layGControls.addWidget(self.ledTop, 0, 4)
        layGControls.addWidget(self.ledBottom, 1, 4)
        layGControls.setColumnStretch(5, 1)

        layGControls.addWidget(self.chkUC, 0, 6)
        layGControls.addWidget(self.chkHf, 1, 6)
        layGControls.addWidget(self.chkPZ, 0, 8)

        layGControls.addWidget(self.cmbMode3D, 0, 10)
        layGControls.addWidget(self.chkContour2D, 1, 10)
        layGControls.addWidget(self.cmbColormap, 0, 12, 1, 1)
        layGControls.addWidget(self.chkColormap_r, 1, 12)

        layGControls.addWidget(self.chkLighting, 0, 14)
        layGControls.addWidget(self.chkColBar, 1, 14)

        layGControls.addWidget(self.lblAlpha, 0, 15)
        layGControls.addWidget(self.diaAlpha, 0, 16)

        layGControls.addWidget(self.lblHatch, 1, 15)
        layGControls.addWidget(self.diaHatch, 1, 16)

        # This widget encompasses all control subwidgets
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layGControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        # This is the plot pane widget, encompassing the other widgets
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self._init_grid()  # initialize grid and do initial plot

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self._log_clicked)
        self.ledBottom.editingFinished.connect(self._log_clicked)
        self.ledTop.editingFinished.connect(self._log_clicked)

        self.chkPolar.clicked.connect(self._init_grid)
        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.cmbColormap.currentIndexChanged.connect(self.draw)
        self.chkColormap_r.clicked.connect(self._init_cmb_colormap)

        self.chkLighting.clicked.connect(self.draw)
        self.diaAlpha.valueChanged.connect(self.draw)
        self.diaHatch.valueChanged.connect(self.draw)
        self.chkContour2D.clicked.connect(self.draw)

        self.mplwidget.mplToolbar.enable_update(
            state=False)  # disable initially
Exemplo n.º 40
0
    def _construct_UI(self):
        self.chkLog = QCheckBox("Log.", self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("Logarithmic scale")
        self.chkLog.setChecked(False)

        self.chkPolar = QCheckBox("Polar", self)
        self.chkPolar.setObjectName("chkPolar")
        self.chkPolar.setToolTip("Polar coordinate range")
        self.chkPolar.setChecked(False)

        self.lblBottom = QLabel("Bottom =", self)
        self.ledBottom = QLineEdit(self)
        self.ledBottom.setObjectName("ledBottom")
        self.ledBottom.setText(str(self.zmin))
        self.ledBottom.setToolTip("Minimum display value.")

        self.lblTop = QLabel("Top:", self)
        self.ledTop = QLineEdit(self)
        self.ledTop.setObjectName("ledTop")
        self.ledTop.setText(str(self.zmax))
        self.ledTop.setToolTip("Maximum display value.")

        self.chkUC = QCheckBox("UC", self)
        self.chkUC.setObjectName("chkUC")
        self.chkUC.setToolTip("Plot unit circle")
        self.chkUC.setChecked(True)

        self.chkPZ = QCheckBox("P/Z", self)
        self.chkPZ.setObjectName("chkPZ")
        self.chkPZ.setToolTip("Plot poles and zeros")
        self.chkPZ.setChecked(True)

        self.chkHf = QCheckBox("H(f)", 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 = QComboBox(self)
        self.cmbMode3D.addItems(modes)
        self.cmbMode3D.setObjectName("cmbShow3D")
        self.cmbMode3D.setToolTip("Select 3D-plot mode.")
        self.cmbMode3D.setCurrentIndex(0)
        self.cmbMode3D.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.chkColormap_r = QCheckBox("reverse", self)
        self.chkColormap_r.setToolTip("reverse colormap")
        self.chkColormap_r.setChecked(True)

        self.cmbColormap = QComboBox(self)
        self._init_cmb_colormap()
        self.cmbColormap.setToolTip("Select colormap")

        self.chkColBar = QCheckBox("Colorbar", self)
        self.chkColBar.setObjectName("chkColBar")
        self.chkColBar.setToolTip("Show colorbar")
        self.chkColBar.setChecked(False)

        self.chkLighting = QCheckBox("Lighting", self)
        self.chkLighting.setObjectName("chkLighting")
        self.chkLighting.setToolTip("Enable light source")
        self.chkLighting.setChecked(False)

        self.lblAlpha = QLabel("Alpha", self)
        self.diaAlpha = QDial(self)
        self.diaAlpha.setRange(0., 10.)
        self.diaAlpha.setValue(10)
        self.diaAlpha.setTracking(False) # produce less events when turning
        self.diaAlpha.setFixedHeight(30)
        self.diaAlpha.setFixedWidth(30)
        self.diaAlpha.setWrapping(False)
        self.diaAlpha.setToolTip("<span>Set transparency for surf and contour plots.</span>")

        self.lblHatch = QLabel("Stride", self)
        self.diaHatch = 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 line density for various plots.")

        self.chkContour2D = QCheckBox("Contour2D", self)
        self.chkContour2D.setObjectName("chkContour2D")
        self.chkContour2D.setToolTip("Plot 2D-contours at z =0")
        self.chkContour2D.setChecked(False)

        #----------------------------------------------------------------------
        # LAYOUT for UI widgets
        #----------------------------------------------------------------------

        layGControls = QGridLayout()
        layGControls.addWidget(self.chkLog, 0, 0)
        layGControls.addWidget(self.chkPolar, 1, 0)
        layGControls.addWidget(self.lblTop, 0, 2)
        layGControls.addWidget(self.lblBottom, 1, 2)
        layGControls.addWidget(self.ledTop, 0, 4)
        layGControls.addWidget(self.ledBottom, 1, 4)
        layGControls.setColumnStretch(5,1)

        layGControls.addWidget(self.chkUC, 0, 6)
        layGControls.addWidget(self.chkHf, 1, 6)
        layGControls.addWidget(self.chkPZ, 0, 8)
        
        layGControls.addWidget(self.cmbMode3D, 0, 10)
        layGControls.addWidget(self.chkContour2D, 1, 10)        
        layGControls.addWidget(self.cmbColormap, 0,12,1,1)
        layGControls.addWidget(self.chkColormap_r, 1,12)

        layGControls.addWidget(self.chkLighting, 0, 14)
        layGControls.addWidget(self.chkColBar, 1, 14)

        layGControls.addWidget(self.lblAlpha, 0, 15)
        layGControls.addWidget(self.diaAlpha, 0, 16)

        layGControls.addWidget(self.lblHatch, 1, 15)
        layGControls.addWidget(self.diaHatch, 1, 16)

        # This widget encompasses all control subwidgets   
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layGControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        # This is the plot pane widget, encompassing the other widgets        
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)
        
        self._init_grid() # initialize grid and do initial plot

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self._log_clicked)
        self.ledBottom.editingFinished.connect(self._log_clicked)
        self.ledTop.editingFinished.connect(self._log_clicked)

        self.chkPolar.clicked.connect(self._init_grid)
        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.cmbColormap.currentIndexChanged.connect(self.draw)
        self.chkColormap_r.clicked.connect(self._init_cmb_colormap)

        self.chkLighting.clicked.connect(self.draw)
        self.diaAlpha.valueChanged.connect(self.draw)
        self.diaHatch.valueChanged.connect(self.draw)
        self.chkContour2D.clicked.connect(self.draw)

        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)
        self.mplwidget.mplToolbar.enable_update(state = False) # disable initially
Exemplo n.º 41
0
    def _init_UI(self):
        self.chkLog = QtGui.QCheckBox(self)
        self.chkLog.setText("Log.")
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("Logarithmic scale")
        self.chkLog.setChecked(False)

        self.chkPolar = QtGui.QCheckBox(self)
        self.chkPolar.setText("Polar")
        self.chkPolar.setObjectName("chkPolar")
        self.chkPolar.setToolTip("Polar coordinates")
        self.chkPolar.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.chkUC = QtGui.QCheckBox(self)
        self.chkUC.setText("UC")
        self.chkUC.setObjectName("chkUC")
        self.chkUC.setToolTip("Plot unit circle")
        self.chkUC.setChecked(True)

        self.chkPZ = QtGui.QCheckBox(self)
        self.chkPZ.setText("P/Z")
        self.chkPZ.setObjectName("chkPZ")
        self.chkPZ.setToolTip("Plot poles and zeros")
        self.chkPZ.setChecked(True)

        self.chkHf = QtGui.QCheckBox(self)
        self.chkHf.setText("H(f)")
        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.chkColormap_r = QtGui.QCheckBox(self)
        self.chkColormap_r.setText("reverse")
        self.chkColormap_r.setToolTip("reverse colormap")
        self.chkColormap_r.setChecked(True)

        self.cmbColormap = QtGui.QComboBox(self)
        self._init_cmb_colormap()
        self.cmbColormap.setToolTip("Select colormap")

        self.chkColBar = QtGui.QCheckBox(self)
        self.chkColBar.setText("Colorbar")
        self.chkColBar.setObjectName("chkColBar")
        self.chkColBar.setToolTip("Show colorbar")
        self.chkColBar.setChecked(False)

        self.chkLighting = QtGui.QCheckBox(self)
        self.chkLighting.setText("Lighting")
        self.chkLighting.setObjectName("chkLighting")
        self.chkLighting.setToolTip("Enable light source")
        self.chkLighting.setChecked(False)

        self.lblAlpha = QtGui.QLabel("Alpha")
        self.diaAlpha = QtGui.QDial(self)
        self.diaAlpha.setRange(0., 10.)
        self.diaAlpha.setValue(10)
        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 3D-contour plot.")

        self.lblHatch = QtGui.QLabel("Stride")
        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.chkContour2D = QtGui.QCheckBox(self)
        self.chkContour2D.setText("Contour2D")
        self.chkContour2D.setObjectName("chkContour2D")
        self.chkContour2D.setToolTip("Plot 2D-contours at z =0")
        self.chkContour2D.setChecked(False)

        #----------------------------------------------------------------------
        # LAYOUT for UI widgets
        #----------------------------------------------------------------------
        spc = QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Expanding,
                                QtGui.QSizePolicy.Minimum)
        self.layGSelect = QtGui.QGridLayout()
        self.layGSelect.setObjectName('plotSpecSelect')
        self.layGSelect.addWidget(self.chkLog, 0, 0)
        self.layGSelect.addWidget(self.chkPolar, 1, 0)
        self.layGSelect.addWidget(self.lblTop, 0, 2)
        self.layGSelect.addWidget(self.lblBottom, 1, 2)
        self.layGSelect.addWidget(self.ledTop, 0, 4)
        self.layGSelect.addWidget(self.ledBottom, 1, 4)
        self.layGSelect.addItem(spc, 0, 5)

        self.layGSelect.addWidget(self.chkUC, 0, 6)
        self.layGSelect.addWidget(self.chkHf, 1, 6)
        self.layGSelect.addWidget(self.chkPZ, 0, 8)
        self.layGSelect.addWidget(self.cmbColormap, 0, 10, 1, 1)
        self.layGSelect.addWidget(self.chkColormap_r, 1, 10)
        self.layGSelect.addWidget(self.cmbMode3D, 0, 12)
        self.layGSelect.addWidget(self.chkContour2D, 1, 12)
        self.layGSelect.addWidget(self.chkLighting, 0, 14)
        self.layGSelect.addWidget(self.chkColBar, 1, 14)

        self.layGSelect.addWidget(self.diaAlpha, 0, 16)
        self.layGSelect.addWidget(self.lblAlpha, 0, 15)
        self.layGSelect.addWidget(self.diaHatch, 1, 16)
        self.layGSelect.addWidget(self.lblHatch, 1, 15)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)

        self.mplwidget.layVMainMpl.addLayout(self.layGSelect)

        self.setLayout(self.mplwidget.layVMainMpl)

        #        self.mplwidget.setFocus()
        # make this the central widget, taking all available space:
        #        self.setCentralWidget(self.mplwidget)

        self._init_grid()  # initialize grid and do initial plot

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self._log_clicked)
        self.ledBottom.editingFinished.connect(self._log_clicked)
        self.ledTop.editingFinished.connect(self._log_clicked)

        self.chkPolar.clicked.connect(self._init_grid)
        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.cmbColormap.currentIndexChanged.connect(self.draw)
        self.chkColormap_r.clicked.connect(self._init_cmb_colormap)

        self.chkLighting.clicked.connect(self.draw)
        self.diaAlpha.valueChanged.connect(self.draw)
        self.diaHatch.valueChanged.connect(self.draw)
        self.chkContour2D.clicked.connect(self.draw)

        logger.debug("UI initialized")
Exemplo n.º 42
0
class PlotTauG(QWidget):

    def __init__(self, parent):
        super(PlotTauG, self).__init__(parent)
        self.verbose = False # suppress warnings

# =============================================================================
# #### not needed at the moment ###
#         self.chkWarnings = QCheckBox("Enable Warnings", self)
#         self.chkWarnings.setChecked(False)
#         self.chkWarnings.setToolTip("Print warnings about singular group delay")
# 
#         layHControls = QHBoxLayout()
#         layHControls.addStretch(10)
#         layHControls.addWidget(self.chkWarnings)
#         
#         # This widget encompasses all control subwidgets:
#         self.frmControls = QFrame(self)
#         self.frmControls.setObjectName("frmControls")
#         self.frmControls.setLayout(layHControls)
# 
# =============================================================================
        self.mplwidget = MplWidget(self)
#        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()        
        self.draw() # initial drawing of tau_g

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)

#------------------------------------------------------------------------------
    def init_axes(self):
        """
        Initialize and clear the axes
        """
        self.ax = self.mplwidget.fig.add_subplot(111)
        self.ax.clear()
        self.ax.get_xaxis().tick_bottom() # remove axis ticks on top
        self.ax.get_yaxis().tick_left() # remove axis ticks right

#------------------------------------------------------------------------------
    def calc_tau_g(self):
        """
        (Re-)Calculate the complex frequency response H(f)
        """
        bb = fb.fil[0]['ba'][0]
        aa = fb.fil[0]['ba'][1]

        # calculate H_cmplx(W) (complex) for W = 0 ... 2 pi:
        self.W, self.tau_g = grpdelay(bb, aa, params['N_FFT'], whole = True,
            verbose = self.verbose) # self.chkWarnings.isChecked())

        # Zero phase filters have no group delay (Causal+AntiCausal)
        if 'baA' in fb.fil[0]:
           self.tau_g = np.zeros(self.tau_g.size)

#------------------------------------------------------------------------------
    def enable_ui(self):
        """
        Triggered when the toolbar is enabled or disabled
        """
#        self.frmControls.setEnabled(self.mplwidget.mplToolbar.enabled)
        if self.mplwidget.mplToolbar.enabled:
            self.init_axes()
            self.draw()

#------------------------------------------------------------------------------
    def draw(self):
        if self.mplwidget.mplToolbar.enabled:
            self.calc_tau_g()
            self.update_view()

#------------------------------------------------------------------------------
    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """
        #========= select frequency range to be displayed =====================
        #=== shift, scale and select: W -> F, H_cplx -> H_c
        f_S2 = fb.fil[0]['f_S'] / 2.
        F = self.W * f_S2 / np.pi

        if fb.fil[0]['freqSpecsRangeType'] == 'sym':
            # shift tau_g and F by f_S/2
            tau_g = np.fft.fftshift(self.tau_g)
            F -= f_S2
        elif fb.fil[0]['freqSpecsRangeType'] == 'half':
            # only use the first half of H and F
            tau_g = self.tau_g[0:params['N_FFT']//2]
            F = F[0:params['N_FFT']//2]
        else: # fb.fil[0]['freqSpecsRangeType'] == 'whole'
            # use H and F as calculated
            tau_g = self.tau_g

        #================ Main Plotting Routine =========================
        #===  clear the axes and (re)draw the plot
            
        if fb.fil[0]['freq_specs_unit'] in {'f_S', 'f_Ny'}:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega}) / T_S \; \rightarrow $'
        else:
            tau_str = r'$ \tau_g(\mathrm{e}^{\mathrm{j} \Omega})$'\
                + ' in ' + fb.fil[0]['plt_tUnit'] + r' $ \rightarrow $'
            tau_g = tau_g / fb.fil[0]['f_S']

        #---------------------------------------------------------
        self.ax.clear() # need to clear, doesn't overwrite
        line_tau_g, = self.ax.plot(F, tau_g, label = "Group Delay")
        #---------------------------------------------------------

        self.ax.set_title(r'Group Delay $ \tau_g$')
        self.ax.set_xlabel(fb.fil[0]['plt_fLabel'])
        self.ax.set_ylabel(tau_str)
        # widen y-limits to suppress numerical inaccuracies when tau_g = constant
        self.ax.set_ylim([max(min(tau_g)-0.5,0), max(tau_g) + 0.5])
        self.ax.set_xlim(fb.fil[0]['freqSpecsRange'])

        self.redraw()

#------------------------------------------------------------------------------
    def redraw(self):
        """
        Redraw the canvas when e.g. the canvas size has changed
        """
        self.mplwidget.redraw()
Exemplo n.º 43
0
    def _init_UI(self):
        self.chkLog = QCheckBox(self)
        self.chkLog.setObjectName("chkLog")
        self.chkLog.setToolTip("<span>Logarithmic scale for y-axis.</span>")
        self.chkLog.setChecked(False)
        self.lblLog = QLabel("Log. y-axis", self)

        self.lblLogBottom = QLabel("Bottom = ", self)
        self.ledLogBottom = QLineEdit(self)
        self.ledLogBottom.setText("-80")
        self.ledLogBottom.setToolTip("Minimum display value for log. scale.")
        self.lbldB = QLabel("dB")

        self.lblPltStim = QLabel(self)
        self.lblPltStim.setText("Stimulus:")
        self.chkPltStim = QCheckBox("Show", self)
        self.chkPltStim.setChecked(False)
        self.chkPltStim.setToolTip("Show stimulus signal.")

        self.lblStimulus = QLabel("Type = ", self)
        self.cmbStimulus = QComboBox(self)
        self.cmbStimulus.addItems([
            "Pulse", "Step", "StepErr", "Sine", "Rect", "Saw", "RandN", "RandU"
        ])
        self.cmbStimulus.setToolTip("Select stimulus type.")

        self.lblFreq = QLabel("<i>f</i>&nbsp; =", self)

        self.ledFreq = QLineEdit(self)
        self.ledFreq.setText(str(self.stim_freq))
        self.ledFreq.setToolTip("Stimulus frequency.")

        self.lblFreqUnit = QLabel("f_S", self)

        self.lblNPoints = QLabel("<i>N</i>&nbsp; =", self)

        self.ledNPoints = QLineEdit(self)
        self.ledNPoints.setText("0")
        self.ledNPoints.setToolTip(
            "Number of points to calculate and display.\n"
            "N = 0 selects automatically.")

        layHControls = QHBoxLayout()

        layHControls.addWidget(self.lblNPoints)
        layHControls.addWidget(self.ledNPoints)
        layHControls.addStretch(2)
        layHControls.addWidget(self.chkLog)
        layHControls.addWidget(self.lblLog)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblLogBottom)
        layHControls.addWidget(self.ledLogBottom)
        layHControls.addWidget(self.lbldB)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblPltStim)
        layHControls.addWidget(self.chkPltStim)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblStimulus)
        layHControls.addWidget(self.cmbStimulus)
        layHControls.addStretch(2)
        layHControls.addWidget(self.lblFreq)
        layHControls.addWidget(self.ledFreq)
        layHControls.addWidget(self.lblFreqUnit)

        layHControls.addStretch(10)

        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        #----------------------------------------------------------------------
        # SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.chkLog.clicked.connect(self.draw)
        self.ledNPoints.editingFinished.connect(self.draw)
        self.ledLogBottom.editingFinished.connect(self.draw)
        self.chkPltStim.clicked.connect(self.draw)
        #        self.cmbStimulus.currentIndexChanged.connect(self.draw)
        self.cmbStimulus.activated.connect(self.draw)
        self.ledFreq.installEventFilter(self)

        self.draw()  # initial calculation and drawing
Exemplo n.º 44
0
    def __init__(self, parent):
        super(PlotHf, self).__init__(parent)

        modes = ['| H |', 're{H}', 'im{H}']
        self.cmbShowH = 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 = QLabel("in", self)

        units = ['dB', 'V', 'W', 'Auto']
        self.cmbUnitsA = QComboBox(self)
        self.cmbUnitsA.addItems(units)
        self.cmbUnitsA.setObjectName("cmbUnitsA")
        self.cmbUnitsA.setToolTip(
            "<span>Set unit for y-axis:\n"
            "dB is attenuation (positive values), V and W are gain (less than 1).</span>"
        )
        self.cmbUnitsA.setCurrentIndex(0)

        self.cmbShowH.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbUnitsA.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        self.chkLinphase = QCheckBox("Zero phase", self)
        self.chkLinphase.setToolTip(
            "<span>Subtract linear phase according to filter order.\n"
            "Attention: this makes no sense for a non-linear phase system!</span>"
        )

        self.lblInset = QLabel("Inset", self)
        self.cmbInset = 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.chkSpecs = QCheckBox("Show Specs", self)
        self.chkSpecs.setChecked(False)
        self.chkSpecs.setToolTip("Display filter specs as hatched regions")

        self.chkPhase = QCheckBox("Phase", self)
        self.chkPhase.setToolTip("Overlay phase")

        layHControls = QHBoxLayout()
        layHControls.addStretch(10)
        layHControls.addWidget(self.cmbShowH)
        layHControls.addWidget(self.lblIn)
        layHControls.addWidget(self.cmbUnitsA)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkLinphase)
        layHControls.addStretch(1)
        layHControls.addWidget(self.lblInset)
        layHControls.addWidget(self.cmbInset)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkSpecs)
        layHControls.addStretch(1)
        layHControls.addWidget(self.chkPhase)
        layHControls.addStretch(10)

        # This widget encompasses all control subwidgets:
        self.frmControls = QFrame(self)
        self.frmControls.setObjectName("frmControls")
        self.frmControls.setLayout(layHControls)

        #----------------------------------------------------------------------
        # mplwidget
        #----------------------------------------------------------------------
        # This is the plot pane widget, encompassing the other widgets
        self.mplwidget = MplWidget(self)
        self.mplwidget.layVMainMpl.addWidget(self.frmControls)
        self.mplwidget.layVMainMpl.setContentsMargins(*params['wdg_margins'])
        self.setLayout(self.mplwidget.layVMainMpl)

        self.init_axes()

        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)
        self.mplwidget.mplToolbar.sigEnabled.connect(self.enable_ui)