Esempio n. 1
0
 def _init_axes_time(self):
     """
     Clear the axes of the time domain matplotlib widgets and (re)draw the plots.
     """
     self.plt_time_stim = qget_cmb_box(self.ui.cmb_plt_time_stim, data=False).lower()
     self.plt_time_resp = qget_cmb_box(self.ui.cmb_plt_time_resp, data=False).lower()
     plt_time = self.plt_time_resp != "none" or self.plt_time_stim != "none"
     
     self.mplwidget_t.fig.clf() # clear figure with axes
         
     if plt_time:
         num_subplots = 1 + (self.cmplx and self.plt_time_resp != "none")
 
         self.mplwidget_t.fig.subplots_adjust(hspace = 0.5)
     
         self.ax_r = self.mplwidget_t.fig.add_subplot(num_subplots,1 ,1)
         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
 
         if self.cmplx and self.plt_time_resp != "none":
             self.ax_i = self.mplwidget_t.fig.add_subplot(num_subplots, 1, 2, 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
 
         if self.ACTIVE_3D: # not implemented / tested yet
             self.ax3d = self.mplwidget_t.fig.add_subplot(111, projection='3d')
Esempio n. 2
0
    def update_accu_settings(self):
        """
        Calculate number of extra integer bits needed in the accumulator (bit 
        growth) depending on the coefficient area (sum of absolute coefficient
        values) for `cmbW == 'auto'` or depending on the number of coefficients
        for `cmbW == 'full'`. The latter works for arbitrary coefficients but
        requires more bits.
        
        The new values are written to the fixpoint coefficient dict 
        `fb.fil[0]['fxqc']['QA']`.
        """
        if qget_cmb_box(self.wdg_w_accu.cmbW, data=False) == "full":
            A_coeff = int(np.ceil(np.log2(len(fb.fil[0]['fxqc']['b']))))
        elif qget_cmb_box(self.wdg_w_accu.cmbW, data=False) == "auto":
            A_coeff = int(np.ceil(np.log2(np.sum(np.abs(fb.fil[0]['ba'][0])))))

        if qget_cmb_box(self.wdg_w_accu.cmbW, data=False) == "full" or\
            qget_cmb_box(self.wdg_w_accu.cmbW, data=False) == "auto":
            fb.fil[0]['fxqc']['QA']['WF'] = fb.fil[0]['fxqc']['QI']['WF']\
                + fb.fil[0]['fxqc']['QCB']['WF']
            fb.fil[0]['fxqc']['QA']['WI'] = fb.fil[0]['fxqc']['QI']['WI']\
                + fb.fil[0]['fxqc']['QCB']['WI'] + A_coeff

        # calculate total accumulator word length
        fb.fil[0]['fxqc']['QA']['W'] = fb.fil[0]['fxqc']['QA']['WI']\
            + fb.fil[0]['fxqc']['QA']['WF'] + 1

        # update quantization settings
        fb.fil[0]['fxqc']['QA'].update(self.wdg_q_accu.q_dict)

        self.wdg_w_accu.dict2ui(fb.fil[0]['fxqc']['QA'])
Esempio n. 3
0
    def _init_axes_freq(self):
        """
        Clear the axes of the frequency domain matplotlib widgets and 
        calculate the fft
        """
        self.plt_freq_stim = qget_cmb_box(self.ui.cmb_plt_freq_stim, data=False).lower()
        self.plt_freq_resp = qget_cmb_box(self.ui.cmb_plt_freq_resp, data=False).lower()
        self.plt_freq_disabled = self.plt_freq_stim == "none" and self.plt_freq_resp == "none"
       
        if not self.ui.chk_log_freq.isChecked() and len(self.mplwidget_f.fig.get_axes()) == 2:
            self.mplwidget_f.fig.clear() # get rid of second axis when returning from log mode by clearing all

        if len(self.mplwidget_f.fig.get_axes()) == 0: # empty figure, no axes
            self.ax_fft = self.mplwidget_f.fig.add_subplot(111)        
            self.ax_fft.get_xaxis().tick_bottom() # remove axis ticks on top
            self.ax_fft.get_yaxis().tick_left() # remove axis ticks right
            self.ax_fft.set_title("FFT of Transient Response")

        for ax in self.mplwidget_f.fig.get_axes(): # clear but don't delete all axes
            ax.cla()

        if self.ui.chk_log_freq.isChecked() and len(self.mplwidget_f.fig.get_axes()) == 1:
            # create second axis scaled for noise power scale if it doesn't exist yet
            self.ax_fft_noise = self.ax_fft.twinx()
            self.ax_fft_noise.is_twin = True

        self.calc_fft()
Esempio n. 4
0
    def _init_axes_time(self):
        """
        Clear the axes of the time domain matplotlib widgets and (re)draw the plots.
        """
        self.plt_time_stim = qget_cmb_box(self.ui.cmb_plt_time_stim,
                                          data=False).lower()
        self.plt_time_resp = qget_cmb_box(self.ui.cmb_plt_time_resp,
                                          data=False).lower()
        plt_time = self.plt_time_resp != "none" or self.plt_time_stim != "none"

        self.mplwidget_t.fig.clf()  # clear figure with axes

        if plt_time:
            num_subplots = 1 + (self.cmplx and self.plt_time_resp != "none")

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

            self.ax_r = self.mplwidget_t.fig.add_subplot(num_subplots, 1, 1)
            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

            if self.cmplx and self.plt_time_resp != "none":
                self.ax_i = self.mplwidget_t.fig.add_subplot(num_subplots,
                                                             1,
                                                             2,
                                                             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

            if self.ACTIVE_3D:  # not implemented / tested yet
                self.ax3d = self.mplwidget_t.fig.add_subplot(111,
                                                             projection='3d')
Esempio n. 5
0
    def _init_axes_freq(self):
        """
        Clear the axes of the frequency domain matplotlib widgets and 
        calculate the fft
        """
        self.plt_freq_stim = qget_cmb_box(self.ui.cmb_plt_freq_stim,
                                          data=False).lower()
        self.plt_freq_resp = qget_cmb_box(self.ui.cmb_plt_freq_resp,
                                          data=False).lower()
        self.plt_freq_disabled = self.plt_freq_stim == "none" and self.plt_freq_resp == "none"

        if not self.ui.chk_log_freq.isChecked() and len(
                self.mplwidget_f.fig.get_axes()) == 2:
            self.mplwidget_f.fig.clear(
            )  # get rid of second axis when returning from log mode by clearing all

        if len(self.mplwidget_f.fig.get_axes()) == 0:  # empty figure, no axes
            self.ax_fft = self.mplwidget_f.fig.add_subplot(111)
            self.ax_fft.get_xaxis().tick_bottom()  # remove axis ticks on top
            self.ax_fft.get_yaxis().tick_left()  # remove axis ticks right
            self.ax_fft.set_title("FFT of Transient Response")

        for ax in self.mplwidget_f.fig.get_axes(
        ):  # clear but don't delete all axes
            ax.cla()

        if self.ui.chk_log_freq.isChecked() and len(
                self.mplwidget_f.fig.get_axes()) == 1:
            # create second axis scaled for noise power scale if it doesn't exist yet
            self.ax_fft_noise = self.ax_fft.twinx()
            self.ax_fft_noise.is_twin = True

        self.calc_fft()
    def _construct_UI(self, **kwargs):
        """ Construct widget """

        dict_ui = {'label_q':'Quant.', 'tip_q':'Select the kind of quantization.',
                   'cmb_q':['round', 'fix', 'floor'], 'cur_q':'round',
                   'label_ov':'Ovfl.', 'tip_ov':'Select overflow behaviour.',
                   'cmb_ov':['wrap', 'sat'], 'cur_ov':'wrap',
                   'enabled':True, 'visible':True
                   }
        for key, val in kwargs.items():
            dict_ui.update({key:val})
        # dict_ui.update(map(kwargs)) # same as above?

        lblQuant = QLabel(dict_ui['label_q'], self)
        self.cmbQuant = QComboBox(self)
        self.cmbQuant.addItems(dict_ui['cmb_q'])
        qset_cmb_box(self.cmbQuant, dict_ui['cur_q'])
        self.cmbQuant.setToolTip(dict_ui['tip_q'])

        lblOvfl = QLabel(dict_ui['label_ov'], self)
        self.cmbOvfl = QComboBox(self)
        self.cmbOvfl.addItems(dict_ui['cmb_ov'])
        qset_cmb_box(self.cmbOvfl, dict_ui['cur_ov'])
        self.cmbOvfl.setToolTip(dict_ui['tip_ov'])

        # ComboBox size is adjusted automatically to fit the longest element
        self.cmbQuant.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbOvfl.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        layH = QHBoxLayout()
        layH.addWidget(lblOvfl)
        layH.addWidget(self.cmbOvfl)
        layH.addStretch()
        layH.addWidget(lblQuant)
        layH.addWidget(self.cmbQuant)
        layH.setContentsMargins(0,0,0,0)

        frmMain = QFrame(self)
        frmMain.setLayout(layH)

        layVMain = QVBoxLayout() # Widget main layout
        layVMain.addWidget(frmMain)
        layVMain.setContentsMargins(5,0,0,0)#*params['wdg_margins'])

        self.setLayout(layVMain)

        #----------------------------------------------------------------------
        # INITIAL SETTINGS
        #----------------------------------------------------------------------
        self.ovfl = qget_cmb_box(self.cmbOvfl, data=False)
        self.quant = qget_cmb_box(self.cmbQuant, data=False)
        
        frmMain.setEnabled(dict_ui['enabled'])
        frmMain.setVisible(dict_ui['visible'])

        #----------------------------------------------------------------------
        # LOCAL SIGNALS & SLOTs
        #----------------------------------------------------------------------
        self.cmbOvfl.currentIndexChanged.connect(self.save_ui)
        self.cmbQuant.currentIndexChanged.connect(self.save_ui)
Esempio n. 7
0
 def _update_time_freq(self):
     """
     Trigger 'draw' when one of the comboboxes PltTime or PltFreq is modified,
     enable frequency domain controls only when needed
     """
     self.plt_time = qget_cmb_box(self.cmbPltTime, data=False)
     self.plt_freq = qget_cmb_box(self.cmbPltFreq, data=False)
     self.wdgHControlsF.setVisible(self.plt_freq != "None")
     self.sig_tx.emit({'sender': __name__, 'view_changed': ''})
Esempio n. 8
0
    def _W_changed(self):
        """
        Set fractional and integer length `WF` and `WI` when wordlength Ẁ` has
        been changed. Try to preserve `WI` or `WF` settings depending on the
        number format (integer or fractional).
        """
        
        # if self.ui.ledW.isModified() ... self.ui.ledW.setModified(False)
        W = safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos')

        if W < 2:
            logger.warn("W must be > 1, restoring previous value.")
            W = self.myQ.W # fall back to previous value
        self.ui.ledW.setText(str(W))

        if qget_cmb_box(self.ui.cmbQFrmt) == 'qint': # integer format, preserve WI bits
            WI = W - self.myQ.WF - 1
            self.ui.ledWI.setText(str(WI))
            self.ui.ledScale.setText(str(1 << (W-1)))
        else: # fractional format, preserve WF bit setting
            WF = W - self.myQ.WI - 1
            if WF < 0:
                self.ui.ledWI.setText(str(W - 1))
                WF = 0
            self.ui.ledWF.setText(str(WF))

        self._store_q_settings()
        self._refresh_table()
Esempio n. 9
0
    def _normalize_gain(self):
        """
        Normalize the gain factor so that the maximum of |H(f)| stays 1 or a
        previously stored maximum value of |H(f)|. Do this every time a P or Z
        has been changed.
        Called by setModelData() and when cmbNorm is activated

        """
        norm = qget_cmb_box(self.ui.cmbNorm, data=False)
        self.ui.ledGain.setEnabled(norm == 'None')
        if norm != self.norm_last:
            qstyle_widget(self.ui.butSave, 'changed')
        if not np.isfinite(self.zpk[2]):
            self.zpk[2] = 1.
        self.zpk[2] = np.real_if_close(self.zpk[2]).item()
        if np.iscomplex(self.zpk[2]):
            logger.warning("Casting complex to real for gain k!")
            self.zpk[2] = np.abs(self.zpk[2])

        if norm != "None":
            b, a = zpk2tf(self.zpk[0], self.zpk[1], self.zpk[2])
            [w, H] = freqz(b, a, whole=True)
            Hmax = max(abs(H))
            if not np.isfinite(Hmax) or Hmax > 1e4 or Hmax < 1e-4:
                Hmax = 1.
            if norm == "1":
                self.zpk[2] = self.zpk[2] / Hmax # normalize to 1
            elif norm == "Max":
                if norm != self.norm_last: # setting has been changed -> 'Max'
                    self.Hmax_last = Hmax # use current design to set Hmax_last
                self.zpk[2] = self.zpk[2] / Hmax * self.Hmax_last
        self.norm_last = norm # store current setting of combobox

        self._restore_gain()
Esempio n. 10
0
    def _normalize_gain(self):
        """
        Normalize the gain factor so that the maximum of |H(f)| stays 1 or a
        previously stored maximum value of |H(f)|. Do this every time a P or Z
        has been changed.
        Called by setModelData() and when cmbNorm is activated

        """
        norm = qget_cmb_box(self.ui.cmbNorm, data=False)
        self.ui.ledGain.setEnabled(norm == 'None')
        if norm != self.norm_last:
            qstyle_widget(self.ui.butSave, 'changed')
        if not np.isfinite(self.zpk[2]):
            self.zpk[2] = 1.
        self.zpk[2] = np.real_if_close(self.zpk[2]).item()
        if np.iscomplex(self.zpk[2]):
            logger.warning("Casting complex to real for gain k!")
            self.zpk[2] = np.abs(self.zpk[2])

        if norm != "None":
            b, a = zpk2tf(self.zpk[0], self.zpk[1], self.zpk[2])
            [w, H] = freqz(b, a, whole=True)
            Hmax = max(abs(H))
            if not np.isfinite(Hmax) or Hmax > 1e4 or Hmax < 1e-4:
                Hmax = 1.
            if norm == "1":
                self.zpk[2] = self.zpk[2] / Hmax  # normalize to 1
            elif norm == "Max":
                if norm != self.norm_last:  # setting has been changed -> 'Max'
                    self.Hmax_last = Hmax  # use current design to set Hmax_last
                self.zpk[2] = self.zpk[2] / Hmax * self.Hmax_last
        self.norm_last = norm  # store current setting of combobox

        self._restore_gain()
Esempio n. 11
0
    def _set_number_format(self):
        """
        Set one of three number formats: Integer, fractional, normalized fractional
        """

        qfrmt = qget_cmb_box(self.ui.cmbQFrmt)
        is_qfrac = False
        W = safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos')
        if qfrmt == 'qint':
            self.ui.ledWI.setText(str(W - 1))
            self.ui.ledWF.setText("0")
            self.ui.ledScale.setText(str(1 << (W-1)))
        elif qfrmt == 'qnfrac': # normalized fractional format
            self.ui.ledWI.setText("0")
            self.ui.ledWF.setText(str(W - 1))
            self.ui.ledScale.setText("1")
        else: # qfrmt == 'qfrac':
            is_qfrac = True

        self.ui.ledWI.setEnabled(is_qfrac)
        self.ui.lblDot.setEnabled(is_qfrac)
        self.ui.ledWF.setEnabled(is_qfrac)
        self.ui.ledW.setEnabled(not is_qfrac)
        self.ui.ledScale.setEnabled(is_qfrac)

        self._store_q_settings()
        self._refresh_table()
Esempio n. 12
0
    def _enable_stim_widgets(self):
        """ Enable / disable widgets depending on the selected stimulus"""
        self.stim = qget_cmb_box(self.cmbStimulus, data=False)
        f1_en = self.stim in {"Cos", "Sine", "Rect", "Saw", "Triang", "Comb"}
        f2_en = self.stim in {"Cos", "Sine"}
        dc_en = self.stim not in {"Step", "StepErr"}

        self.chk_stim_bl.setVisible(self.stim in {"Triang", "Saw", "Rect"})

        self.lblAmp1.setVisible(self.stim != "None")
        self.ledAmp1.setVisible(self.stim != "None")

        self.lblPhi1.setVisible(f1_en)
        self.ledPhi1.setVisible(f1_en)
        self.lblPhU1.setVisible(f1_en)
        self.lblFreq1.setVisible(f1_en)
        self.ledFreq1.setVisible(f1_en)
        self.lblFreqUnit1.setVisible(f1_en)

        self.lblFreq2.setVisible(f2_en)
        self.ledFreq2.setVisible(f2_en)
        self.lblFreqUnit2.setVisible(f2_en)
        self.lblAmp2.setVisible(f2_en)
        self.ledAmp2.setVisible(f2_en)
        self.lblPhi2.setVisible(f2_en)
        self.ledPhi2.setVisible(f2_en)
        self.lblPhU2.setVisible(f2_en)

        self.lblDC.setVisible(dc_en)
        self.ledDC.setVisible(dc_en)

        self.sig_tx.emit({'sender': __name__, 'data_changed': 'stim'})
Esempio n. 13
0
    def _set_number_format(self):
        """
        Set one of three number formats: Integer, fractional, normalized fractional
        """

        qfrmt = qget_cmb_box(self.ui.cmbQFrmt)
        is_qfrac = False
        W = safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos')
        if qfrmt == 'qint':
            self.ui.ledWI.setText(str(W - 1))
            self.ui.ledWF.setText("0")
            self.ui.ledScale.setText(str(1 << (W-1)))
        elif qfrmt == 'qnfrac': # normalized fractional format
            self.ui.ledWI.setText("0")
            self.ui.ledWF.setText(str(W - 1))
            self.ui.ledScale.setText("1")
        else: # qfrmt == 'qfrac':
            is_qfrac = True

        self.ui.ledWI.setEnabled(is_qfrac)
        self.ui.lblDot.setEnabled(is_qfrac)
        self.ui.ledWF.setEnabled(is_qfrac)
        self.ui.ledW.setEnabled(not is_qfrac)
        self.ui.ledScale.setEnabled(is_qfrac)

        self._store_q_settings()
        self._refresh_table()
Esempio n. 14
0
    def _W_changed(self):
        """
        Set fractional and integer length `WF` and `WI` when wordlength Ẁ` has
        been changed. Try to preserve `WI` or `WF` settings depending on the
        number format (integer or fractional).
        """
        
        # if self.ui.ledW.isModified() ... self.ui.ledW.setModified(False)
        W = safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos')

        if W < 2:
            logger.warn("W must be > 1, restoring previous value.")
            W = self.myQ.W # fall back to previous value
        self.ui.ledW.setText(str(W))

        if qget_cmb_box(self.ui.cmbQFrmt) == 'qint': # integer format, preserve WI bits
            WI = W - self.myQ.WF - 1
            self.ui.ledWI.setText(str(WI))
            self.ui.ledScale.setText(str(1 << (W-1)))
        else: # fractional format, preserve WF bit setting
            WF = W - self.myQ.WI - 1
            if WF < 0:
                self.ui.ledWI.setText(str(W - 1))
                WF = 0
            self.ui.ledWF.setText(str(WF))

        self._store_q_settings()
        self._refresh_table()
Esempio n. 15
0
    def _set_response_type(self, enb_signal=False):
        """
        Triggered when cmbResponseType (LP, HP, ...) is changed:
        Copy selection to self.rt and fb.fil[0] and reconstruct filter type combo

        If previous filter type (FIR, IIR, ...) exists for new rt, set the
        filter type combo box to the old setting
        """
        # Read current setting of comboBox as string and store it in the filter dict
        fb.fil[0]['rt'] = self.rt = qget_cmb_box(self.cmbResponseType)

        # Get list of available filter types for new rt
        ft_list = list(
            fb.fil_tree[self.rt].keys())  # explicit list() needed for Py3
        #---------------------------------------------------------------
        # Rebuild filter type combobox entries for new rt setting
        self.cmbFilterType.blockSignals(
            True)  # don't fire when changed programmatically
        self.cmbFilterType.clear()
        for ft in fb.fil_tree[self.rt]:
            self.cmbFilterType.addItem(rc.ft_names[ft], ft)

        # Is current filter type (e.g. IIR) in list for new rt?
        if fb.fil[0]['ft'] in ft_list:
            ft_idx = self.cmbFilterType.findText(fb.fil[0]['ft'])
            self.cmbFilterType.setCurrentIndex(
                ft_idx)  # yes, set same ft as before
        else:
            self.cmbFilterType.setCurrentIndex(0)  # no, set index 0

        self.cmbFilterType.blockSignals(False)
        #---------------------------------------------------------------

        self._set_filter_type(enb_signal)
Esempio n. 16
0
    def _set_response_type(self, enb_signal=False):
        """
        Triggered when cmbResponseType (LP, HP, ...) is changed:
        Copy selection to self.rt and fb.fil[0] and reconstruct filter type combo

        If previous filter type (FIR, IIR, ...) exists for new rt, set the
        filter type combo box to the old setting
        """
        # Read current setting of comboBox as string and store it in the filter dict
        fb.fil[0]['rt'] = self.rt = qget_cmb_box(self.cmbResponseType)

        # Get list of available filter types for new rt
        ft_list = list(fb.fil_tree[self.rt].keys()) # explicit list() needed for Py3
        #---------------------------------------------------------------
        # Rebuild filter type combobox entries for new rt setting
        self.cmbFilterType.blockSignals(True) # don't fire when changed programmatically
        self.cmbFilterType.clear()
        for ft in fb.fil_tree[self.rt]:
            self.cmbFilterType.addItem(rc.ft_names[ft], ft)

        # Is current filter type (e.g. IIR) in list for new rt?
        if fb.fil[0]['ft'] in ft_list:
            ft_idx = self.cmbFilterType.findText(fb.fil[0]['ft'])
            self.cmbFilterType.setCurrentIndex(ft_idx) # yes, set same ft as before
        else:
            self.cmbFilterType.setCurrentIndex(0)     # no, set index 0

        self.cmbFilterType.blockSignals(False)
        #---------------------------------------------------------------

        self._set_filter_type(enb_signal)
Esempio n. 17
0
    def _update_noi(self):
        """ Update type + value + label for self.noi for noise"""
        self.noise = qget_cmb_box(self.cmbNoise, data=False).lower()
        self.lblNoi.setVisible(self.noise != 'none')
        self.ledNoi.setVisible(self.noise != 'none')
        if self.noise != 'none':
            self.noi = safe_eval(self.ledNoi.text(),
                                 0,
                                 return_type='float',
                                 sign='pos')
            self.ledNoi.setText(str(self.noi))
            if self.noise == 'gauss':
                self.lblNoi.setText(to_html("&sigma; =", frmt='bi'))
                self.ledNoi.setToolTip(
                    "<span>Standard deviation of statistical process,"
                    "noise power is <i>P</i> = &sigma;<sup>2</sup></span>")
            elif self.noise == 'uniform':
                self.lblNoi.setText(to_html("&Delta; =", frmt='bi'))
                self.ledNoi.setToolTip(
                    "<span>Interval size for uniformly distributed process "
                    "(e.g. quantization step size for quantization noise), "
                    "centered around 0. Noise power is "
                    "<i>P</i> = &Delta;<sup>2</sup>/12.</span>")
            elif self.noise == 'prbs':
                self.lblNoi.setText(to_html("A =", frmt='bi'))
                self.ledNoi.setToolTip(
                    "<span>Amplitude of bipolar Pseudorandom Binary Sequence. "
                    "Noise power is <i>P</i> = A<sup>2</sup>.</span>")

        self.sig_tx.emit({'sender': __name__, 'data_changed': 'noi'})
Esempio n. 18
0
    def _set_number_format(self):
        """
        Set one of three number formats: Integer, fractional, normalized fractional
        (triggered by self.ui.cmbQFrmt combobox)
        """

        qfrmt = qget_cmb_box(self.ui.cmbQFrmt)
        is_qfrac = False
        W = safe_eval(self.ui.ledW.text(),
                      self.myQ.W,
                      return_type='int',
                      sign='pos')
        if qfrmt == 'qint':
            self.ui.ledWI.setText(str(W - 1))
            self.ui.ledWF.setText("0")
        elif qfrmt == 'qnfrac':  # normalized fractional format
            self.ui.ledWI.setText("0")
            self.ui.ledWF.setText(str(W - 1))
        else:  # qfrmt == 'qfrac':
            is_qfrac = True

        WI = safe_eval(self.ui.ledWI.text(), self.myQ.WI, return_type='int')

        self.ui.ledScale.setText(str(1 << WI))
        self.ui.ledWI.setEnabled(is_qfrac)
        self.ui.lblDot.setEnabled(is_qfrac)
        self.ui.ledWF.setEnabled(is_qfrac)
        self.ui.ledW.setEnabled(not is_qfrac)
        self.ui.ledScale.setEnabled(False)

        self.ui2qdict()  # save UI to dict and to class attributes
Esempio n. 19
0
    def _construct_UI(self):
        """
        Intitialize the UI with widgets for coefficient format and input and 
        output quantization
        """
        if not 'QA' in fb.fil[0]['fxqc']:
            fb.fil[0]['fxqc']['QA'] = {}
        set_dict_defaults(fb.fil[0]['fxqc']['QA'], {
            'WI': 0,
            'WF': 30,
            'W': 32,
            'ovfl': 'wrap',
            'quant': 'floor'
        })

        self.wdg_w_coeffs = UI_W(
            self,
            fb.fil[0]['fxqc']['QCB'],
            label='Coeff. Format <i>B<sub>I.F&nbsp;</sub></i>:',
            tip_WI='Number of integer bits - edit in the "b,a" tab',
            tip_WF='Number of fractional bits - edit in the "b,a" tab',
            WI=fb.fil[0]['fxqc']['QCB']['WI'],
            WF=fb.fil[0]['fxqc']['QCB']['WF'])
        self.wdg_w_coeffs.sig_tx.connect(self.update_q_coeff)
        #        self.wdg_w_coeffs.setEnabled(False)

        #        self.wdg_q_coeffs = UI_Q(self, fb.fil[0]['fxqc']['QCB'],
        #                                        cur_ov=fb.fil[0]['fxqc']['QCB']['ovfl'],
        #                                        cur_q=fb.fil[0]['fxqc']['QCB']['quant'])
        #        self.wdg_q_coeffs.sig_tx.connect(self.update_q_coeff)

        self.wdg_w_accu = UI_W(self,
                               fb.fil[0]['fxqc']['QA'],
                               label='',
                               fractional=True,
                               combo_visible=True)
        self.wdg_w_accu.sig_tx.connect(self.process_sig_rx)

        self.wdg_q_accu = UI_Q(self,
                               fb.fil[0]['fxqc']['QA'],
                               label='Accu Format <i>Q<sub>A&nbsp;</sub></i>:')
        self.wdg_q_accu.sig_tx.connect(self.process_sig_rx)
        # initial setting for accumulator
        cmbW = qget_cmb_box(self.wdg_w_accu.cmbW, data=False)
        self.wdg_w_accu.ledWF.setEnabled(cmbW == 'man')
        self.wdg_w_accu.ledWI.setEnabled(cmbW == 'man')
        #------------------------------------------------------------------------------

        layVWdg = QVBoxLayout()
        layVWdg.setContentsMargins(0, 0, 0, 0)

        layVWdg.addWidget(self.wdg_w_coeffs)
        #        layVWdg.addWidget(self.wdg_q_coeffs)
        layVWdg.addWidget(self.wdg_q_accu)
        layVWdg.addWidget(self.wdg_w_accu)

        layVWdg.addStretch()

        self.setLayout(layVWdg)
Esempio n. 20
0
    def _set_amp_unit(self, source):
        """
        Store unit for amplitude in filter dictionary, reload amplitude spec 
        entries via load_dict and fire a sigUnitChanged signal
        """
        fb.fil[0]['amp_specs_unit'] = qget_cmb_box(self.cmbUnitsA, data=False)
        self.load_dict()

        self.sig_tx.emit({'sender':__name__, 'view_changed':'a_unit'})
Esempio n. 21
0
    def _set_amp_unit(self, source):
        """
        Store unit for amplitude in filter dictionary, reload amplitude spec 
        entries via load_dict and fire a sigUnitChanged signal
        """
        fb.fil[0]['amp_specs_unit'] = qget_cmb_box(self.cmbUnitsA, data=False)
        self.load_dict()

        self.sigUnitChanged.emit() # -> input_widgets
Esempio n. 22
0
    def _set_amp_unit(self, source):
        """
        Store unit for amplitude in filter dictionary, reload amplitude spec 
        entries via load_dict and fire a sigUnitChanged signal
        """
        fb.fil[0]['amp_specs_unit'] = qget_cmb_box(self.cmbUnitsA, data=False)
        self.load_dict()

        self.sigUnitChanged.emit()  # -> input_widgets
Esempio n. 23
0
    def _set_amp_unit(self, source):
        """
        Store unit for amplitude in filter dictionary, reload amplitude spec 
        entries via load_dict and fire a sigUnitChanged signal
        """
        fb.fil[0]['amp_specs_unit'] = qget_cmb_box(self.cmbUnitsA, data=False)
        self.load_dict()

        self.sig_tx.emit({'sender': __name__, 'view_changed': 'a_unit'})
Esempio n. 24
0
    def test_cmb_filter_type(self):
        """Test <Filter Type> ComboBox"""
        self.assertEqual(qget_cmb_box(self.form.cmbFilterType, data=False), "IIR")
        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 2)

        self.set_cmb_box(self.form.cmbFilterType, 'FIR')

        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 1)
Esempio n. 25
0
    def test_cmb_filter_type(self):
        """Test <Filter Type> ComboBox"""
        self.assertEqual(qget_cmb_box(self.form.cmbFilterType, data=False), "IIR")
        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 2)

        self.set_cmb_box(self.form.cmbFilterType, 'FIR')

        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 1)
Esempio n. 26
0
    def __init__(self, parent):
        super(FilterPZ, self).__init__(parent)

        self.Hmax_last = 1  # initial setting for maximum gain
        self.angle_char = "<" # "∠" may give problems with some encodings
        self.angle_char = unichr_23(int('2220', 16))

        self.ui = FilterPZ_UI(self) # create the UI part with buttons etc.
        self.norm_last = qget_cmb_box(self.ui.cmbNorm, data=False) # initial setting of cmbNorm
        self._construct_UI() # construct the rest of the UI
Esempio n. 27
0
    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """

        self.unitPhi = qget_cmb_box(self.cmbUnitsPhi, data=False)

        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()
Esempio n. 28
0
    def update_view(self):
        """
        Draw the figure with new limits, scale etc without recalculating H(f)
        """

        self.unitPhi = qget_cmb_box(self.cmbUnitsPhi, data=False)

        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()
Esempio n. 29
0
    def test_defaults(self):
        """Test GUI setting in its default state"""
        self.assertEqual(self.form.spnDigits.value(), 4)
        self.assertEqual(self.form.ledW.text(), "16")
        self.assertEqual(self.form.ledWF.text(), "0")
        self.assertEqual(self.form.ledWI.text(), "15")
        self.assertEqual(qget_cmb_box(self.form.cmbFormat, data=False).lower(), "float")
        self.assertEqual(self.form.butSetZero.text(), "= 0")

        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 1)
        self.assertEqual(self.form.tblCoeff.item(0,0).text(), "1")
Esempio n. 30
0
    def test_defaults(self):
        """Test GUI setting in its default state"""
        self.assertEqual(self.form.spnDigits.value(), 4)
        self.assertEqual(self.form.ledW.text(), "16")
        self.assertEqual(self.form.ledWF.text(), "0")
        self.assertEqual(self.form.ledWI.text(), "15")
        self.assertEqual(qget_cmb_box(self.form.cmbFormat, data=False).lower(), "float")
        self.assertEqual(self.form.butSetZero.text(), "= 0")

        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 1)
        self.assertEqual(self.form.tblCoeff.item(0,0).text(), "1")
Esempio n. 31
0
    def _set_design_method(self, enb_signal=False):
        """
        Triggered when cmbFilterClass (cheby1, ...) is changed:
        - read design method fc and copy it to fb.fil[0]
        - create / update global filter instance fb.fil_inst of fc class
        - update dynamic widgets (if fc has changed and if there are any)
        - call load filter order
        """
        fb.fil[0]['fc'] = fc = qget_cmb_box(self.cmbFilterClass)

        if fc != self.fc_last:  # fc has changed:

            # when filter has been changed, try to destroy dynamic widgets of last fc:
            if self.fc_last:
                self._destruct_dyn_widgets()

            #==================================================================
            """
            Create new instance of the selected filter class, accessible via
            its handle fb.fil_inst
            """
            err = ff.fil_factory.create_fil_inst(fc)
            logger.debug("InputFilter.set_design_method triggered: %s\n"
                         "Returned error code %d" % (fc, err))
            #==================================================================

            # Check whether new design method also provides the old filter order
            # method. If yes, don't change it, else set first available
            # filter order method
            if fb.fil[0]['fo'] not in fb.fil_tree[self.rt][self.ft][fc].keys():
                fb.fil[0].update({'fo': {}})
                # explicit list(dict.keys()) needed for Python 3
                fb.fil[0]['fo'] = list(
                    fb.fil_tree[self.rt][self.ft][fc].keys())[0]

# =============================================================================
#             logger.debug("selFilter = %s"
#                    "filterTree[fc] = %s"
#                    "filterTree[fc].keys() = %s"
#                   %(fb.fil[0], fb.fil_tree[self.rt][self.ft][fc],\
#                     fb.fil_tree[self.rt][self.ft][fc].keys()
#                     ))
#
# =============================================================================
            if hasattr(
                    ff.fil_inst,
                    'construct_UI'):  # construct dyn. subwidgets if available
                self._construct_dyn_widgets()

            self.fc_last = fb.fil[0]['fc']

        self.load_filter_order(enb_signal)
Esempio n. 32
0
    def cmplx2frmt(self, text, places=-1):
        """
        Convert number "text" (real or complex or string) to the format defined 
        by cmbPZFrmt.
        
        Returns: 
            string
        """
        # convert to "normal" string and prettify via safe_eval:
        data = safe_eval(qstr(text), return_type='auto')
        frmt = qget_cmb_box(self.ui.cmbPZFrmt)  # get selected format

        if places == -1:
            full_prec = True
        else:
            full_prec = False

        if frmt == 'cartesian' or not (type(data) == complex):
            if full_prec:
                return "{0}".format(data)
            else:
                return "{0:.{plcs}g}".format(data, plcs=places)

        elif frmt == 'polar_rad':
            r, phi = np.absolute(data), np.angle(data, deg=False)
            if full_prec:
                return "{r} * {angle_char}{p} rad"\
                    .format(r=r, p=phi, angle_char=self.angle_char)
            else:
                return "{r:.{plcs}g} * {angle_char}{p:.{plcs}g} rad"\
                    .format(r=r, p=phi, plcs=places, angle_char=self.angle_char)

        elif frmt == 'polar_deg':
            r, phi = np.absolute(data), np.angle(data, deg=True)
            if full_prec:
                return "{r} * {angle_char}{p}°"\
                    .format(r=r, p=phi, angle_char=self.angle_char)
            else:
                return "{r:.{plcs}g} * {angle_char}{p:.{plcs}g}°"\
                    .format(r=r, p=phi, plcs=places, angle_char=self.angle_char)

        elif frmt == 'polar_pi':
            r, phi = np.absolute(data), np.angle(data, deg=False) / np.pi
            if full_prec:
                return "{r} * {angle_char}{p} pi"\
                    .format(r=r, p=phi, angle_char=self.angle_char)
            else:
                return "{r:.{plcs}g} * {angle_char}{p:.{plcs}g} pi"\
                    .format(r=r, p=phi, plcs=places, angle_char=self.angle_char)

        else:
            logger.error("Unknown format {0}.".format(frmt))
Esempio n. 33
0
    def cmplx2frmt(self, text, places=-1):
        """
        Convert number "text" (real or complex or string) to the format defined 
        by cmbPZFrmt.
        
        Returns: 
            string
        """
        # convert to "normal" string and prettify via safe_eval:
        data = safe_eval(qstr(text), return_type='auto')
        frmt = qget_cmb_box(self.ui.cmbPZFrmt) # get selected format

        if places == -1:
            full_prec = True
        else:
            full_prec = False

        if frmt == 'cartesian' or not (type(data) == complex):
            if full_prec:
                return "{0}".format(data)
            else:
                return "{0:.{plcs}g}".format(data, plcs=places)

        elif frmt == 'polar_rad':
            r, phi = np.absolute(data), np.angle(data, deg=False)
            if full_prec:
                return "{r} * {angle_char}{p} rad"\
                    .format(r=r, p=phi, angle_char=self.angle_char)
            else:
                return "{r:.{plcs}g} * {angle_char}{p:.{plcs}g} rad"\
                    .format(r=r, p=phi, plcs=places, angle_char=self.angle_char)

        elif frmt == 'polar_deg':
            r, phi = np.absolute(data), np.angle(data, deg=True)
            if full_prec:
                return "{r} * {angle_char}{p}°"\
                    .format(r=r, p=phi, angle_char=self.angle_char)
            else:
                return "{r:.{plcs}g} * {angle_char}{p:.{plcs}g}°"\
                    .format(r=r, p=phi, plcs=places, angle_char=self.angle_char)

        elif frmt == 'polar_pi':
            r, phi = np.absolute(data), np.angle(data, deg=False) / np.pi
            if full_prec:
                return "{r} * {angle_char}{p} pi"\
                    .format(r=r, p=phi, angle_char=self.angle_char)
            else:
                return "{r:.{plcs}g} * {angle_char}{p:.{plcs}g} pi"\
                    .format(r=r, p=phi, plcs=places, angle_char=self.angle_char)

        else:
            logger.error("Unknown format {0}.".format(frmt))
Esempio n. 34
0
    def _set_design_method(self, enb_signal=False):
        """
        Triggered when cmbFilterClass (cheby1, ...) is changed:
        - read design method fc and copy it to fb.fil[0]
        - create / update global filter instance fb.fil_inst of fc class
        - update dynamic widgets (if fc has changed and if there are any)
        - call load filter order
        """
        fb.fil[0]['fc'] = fc = qget_cmb_box(self.cmbFilterClass)

        if fc != self.fc_last: # fc has changed:

            # when filter has been changed, try to destroy dynamic widgets of last fc:
            if self.fc_last:
                self._destruct_dyn_widgets()

            #==================================================================
            """
            Create new instance of the selected filter class, accessible via
            its handle fb.fil_inst
            """
            err = ff.fil_factory.create_fil_inst(fc)
            logger.debug("InputFilter.set_design_method triggered: %s\n"
                        "Returned error code %d" %(fc, err))
            #==================================================================


            # Check whether new design method also provides the old filter order
            # method. If yes, don't change it, else set first available
            # filter order method
            if fb.fil[0]['fo'] not in fb.fil_tree[self.rt][self.ft][fc].keys():
                fb.fil[0].update({'fo':{}})
                # explicit list(dict.keys()) needed for Python 3
                fb.fil[0]['fo'] = list(fb.fil_tree[self.rt][self.ft][fc].keys())[0]

            logger.debug("selFilter = %s"
                   "filterTree[fc] = %s"
                   "filterTree[fc].keys() = %s"
                  %(fb.fil[0], fb.fil_tree[self.rt][self.ft][fc],\
                    fb.fil_tree[self.rt][self.ft][fc].keys()
                    ))

            if hasattr(ff.fil_inst, 'wdg'): # construct dyn. subwidgets if available
                self._construct_dyn_widgets()
            else:
                self.frmDynWdg.setVisible(False) # no subwidget, hide empty frame

            self.fc_last = fb.fil[0]['fc']

        self.load_filter_order(enb_signal)
Esempio n. 35
0
    def __init__(self, parent):
        super(FilterPZ, self).__init__(parent)

        self.Hmax_last = 1  # initial setting for maximum gain
        self.angle_char = "<" # "∠" may give problems with some encodings
        self.angle_char = unichr_23(int('2220', 16))

        self.ui = FilterPZ_UI(self) # create the UI part with buttons etc.
        self.norm_last = qget_cmb_box(self.ui.cmbNorm, data=False) # initial setting of cmbNorm
        self._construct_UI() # construct the rest of the UI

        self.load_dict() # initialize table from filterbroker
        self._refresh_table() # initialize table with values

        self.setup_signal_slot() # setup signal-slot connections and eventFilters
Esempio n. 36
0
    def fx_select(self):
        """
        Select between fixpoint and floating point simulation
        """
        self.sim_select = qget_cmb_box(self.ui.cmb_sim_select, data=False)
        self.fx_sim = (self.sim_select == 'Fixpoint')
        self.ui.but_run.setVisible(self.fx_sim)
        self.ui.chk_fx_scale.setVisible(self.fx_sim)
        self.ui.chk_fx_range.setVisible(self.fx_sim)
        self.hdl_dict = None

        if self.fx_sim:
            qstyle_widget(self.ui.but_run, "changed")
            self.fx_run()
        else:
            self.draw()
Esempio n. 37
0
    def fx_select(self):
        """
        Select between fixpoint and floating point simulation
        """
        self.sim_select = qget_cmb_box(self.ui.cmb_sim_select, data=False)
        self.fx_sim = (self.sim_select == 'Fixpoint')
        self.ui.but_run.setVisible(self.fx_sim)
        self.ui.chk_fx_scale.setVisible(self.fx_sim)
        self.ui.chk_fx_range.setVisible(self.fx_sim)
        self.hdl_dict = None

        if self.fx_sim:
            qstyle_widget(self.ui.but_run, "changed")
            self.fx_run()
        else:
            self.draw()
Esempio n. 38
0
    def frmt2cmplx(self, text, default=0.):
        """
        Convert format defined by cmbPZFrmt to real or complex
        """
        conv_error = False
        text = qstr(text).replace(
            " ", "")  # convert to "proper" string without blanks
        if qget_cmb_box(self.ui.cmbPZFrmt) == 'cartesian':
            return safe_eval(text, default, return_type='auto')
        else:
            polar_str = text.split('*' + self.angle_char, 1)
            if len(polar_str) < 2:  # input is real or imaginary
                r = safe_eval(re.sub('[' + self.angle_char + '<∠°]', '', text),
                              default,
                              return_type='auto')
                x = r.real
                y = r.imag
            else:
                r = safe_eval(polar_str[0], sign='pos')
                if safe_eval.err > 0:
                    conv_error = True

                if "°" in polar_str[1]:
                    scale = np.pi / 180.  # angle in degrees
                elif re.search('π$|pi$', polar_str[1]):
                    scale = np.pi
                else:
                    scale = 1.  # angle in rad

                # remove right-most special characters (regex $)
                polar_str[1] = re.sub(
                    '[' + self.angle_char + '<∠°π]$|rad$|pi$', '',
                    polar_str[1])
                phi = safe_eval(polar_str[1]) * scale
                if safe_eval.err > 0:
                    conv_error = True

                if not conv_error:
                    x = r * np.cos(phi)
                    y = r * np.sin(phi)
                else:
                    x = default.real
                    y = default.imag
                    logger.error(
                        "Expression {0} could not be evaluated.".format(text))
            return x + 1j * y
Esempio n. 39
0
    def _enable_stim_widgets(self):
        """ Enable / disable widgets depending on the selected stimulus"""
        self.stim = qget_cmb_box(self.cmbStimulus, data=False)
        f1_en = self.stim in {"Cos", "Sine", "Rect", "Saw"}
        f2_en = self.stim in {"Cos", "Sine"}
        a2_en = self.stim in {"Cos", "Sine"}
        dc_en = self.stim not in {"Step", "StepErr"}
        self.lblFreq1.setVisible(f1_en)
        self.ledFreq1.setVisible(f1_en)
        self.lblFreqUnit1.setVisible(f1_en)
        self.lblFreq2.setVisible(f2_en)
        self.ledFreq2.setVisible(f2_en)
        self.lblFreqUnit2.setVisible(f2_en)
        self.lblAmp2.setVisible(a2_en)
        self.ledAmp2.setVisible(a2_en)
        self.lblDC.setVisible(dc_en)
        self.ledDC.setVisible(dc_en)

        self.sig_tx.emit({'sender':__name__, 'data_changed':'stim'})
Esempio n. 40
0
    def _freq_range(self, emit_sig_range = True):
        """
        Set frequency plotting range for single-sided spectrum up to f_S/2 or f_S
        or for double-sided spectrum between -f_S/2 and f_S/2 and emit
        'view_changed':'f_range'.
        """
        rangeType = qget_cmb_box(self.cmbFRange)

        fb.fil[0].update({'freqSpecsRangeType':rangeType})
        if rangeType == 'whole':
            f_lim = [0, fb.fil[0]["f_S"]]
        elif rangeType == 'sym':
            f_lim = [-fb.fil[0]["f_S"]/2., fb.fil[0]["f_S"]/2.]
        else:
            f_lim = [0, fb.fil[0]["f_S"]/2.]

        fb.fil[0]['freqSpecsRange'] = f_lim # store settings in dict

        self.sig_tx.emit({'sender':__name__, 'view_changed':'f_range'})
Esempio n. 41
0
 def _update_noi(self):
     """ Update type + value + label for self.noi for noise"""
     self.noise = qget_cmb_box(self.cmbNoise, data=False).lower()
     self.lblNoi.setVisible(self.noise!='none')
     self.ledNoi.setVisible(self.noise!='none')
     if self.noise!='none':
         self.noi = safe_eval(self.ledNoi.text(), 0, return_type='float', sign='pos')
         self.ledNoi.setText(str(self.noi))
         if self.noise == 'gauss':
             self.lblNoi.setText(to_html("&sigma; =", frmt='bi'))
             self.ledNoi.setToolTip("<span>Standard deviation of statistical process,"
                                    "noise power is <i>P</i> = &sigma;<sup>2</sup></span>")
         elif self.noise == 'uniform':
             self.lblNoi.setText(to_html("&Delta; =", frmt='bi'))
             self.ledNoi.setToolTip("<span>Interval size for uniformly distributed process "
                                    "(e.g. quantization step size for quantization noise), "
                                    "centered around 0. Noise power is "
                                    "<i>P</i> = &Delta;<sup>2</sup>/12.</span>")
     self.sig_tx.emit({'sender':__name__, 'data_changed':'noi'})
Esempio n. 42
0
    def _set_filter_type(self, enb_signal=False):
        """"
        Triggered when cmbFilterType (IIR, FIR, ...) is changed:
        - read filter type ft and copy it to fb.fil[0]['ft'] and self.ft
        - (re)construct design method combo, adding
          displayed text (e.g. "Chebychev 1") and hidden data (e.g. "cheby1")
        """
        # Read out current setting of comboBox and convert to string
        fb.fil[0]['ft'] = self.ft = qget_cmb_box(self.cmbFilterType)
        #
        logger.debug("InputFilter.set_filter_type triggered: {0}".format(
            self.ft))

        #---------------------------------------------------------------
        # Get all available design methods for new ft from fil_tree and
        # - Collect them in fc_list
        # - Rebuild design method combobox entries for new ft setting:
        #    The combobox is populated with the "long name",
        #    the internal name is stored in comboBox.itemData
        self.cmbFilterClass.blockSignals(True)
        self.cmbFilterClass.clear()
        fc_list = []

        for fc in sorted(fb.fil_tree[self.rt][self.ft]):
            self.cmbFilterClass.addItem(fb.fil_classes[fc]['name'], fc)
            fc_list.append(fc)

        logger.debug("fc_list: {0}\n{1}".format(fc_list, fb.fil[0]['fc']))

        # Does new ft also provide the previous design method (e.g. ellip)?
        # Has filter been instantiated?
        if fb.fil[0]['fc'] in fc_list and ff.fil_inst:
            # yes, set same fc as before
            fc_idx = self.cmbFilterClass.findText(
                fb.fil_classes[fb.fil[0]['fc']]['name'])
            logger.debug("fc_idx : %s", fc_idx)
            self.cmbFilterClass.setCurrentIndex(fc_idx)
        else:
            self.cmbFilterClass.setCurrentIndex(0)  # no, set index 0

        self.cmbFilterClass.blockSignals(False)

        self._set_design_method(enb_signal)
Esempio n. 43
0
    def _freq_range(self, emit_sig_range = True):
        """
        Set frequency plotting range for single-sided spectrum up to f_S/2 or f_S
        or for double-sided spectrum between -f_S/2 and f_S/2 and emit
        sigUnitChanged signal
        """

        rangeType = qget_cmb_box(self.cmbFRange)

        fb.fil[0].update({'freqSpecsRangeType':rangeType})
        if rangeType == 'whole':
            f_lim = [0, fb.fil[0]["f_S"]]
        elif rangeType == 'sym':
            f_lim = [-fb.fil[0]["f_S"]/2., fb.fil[0]["f_S"]/2.]
        else:
            f_lim = [0, fb.fil[0]["f_S"]/2.]

        fb.fil[0]['freqSpecsRange'] = f_lim # store settings in dict

        self.sigUnitChanged.emit() # -> input_widgets
Esempio n. 44
0
    def _freq_range(self, emit_sig_range=True):
        """
        Set frequency plotting range for single-sided spectrum up to f_S/2 or f_S
        or for double-sided spectrum between -f_S/2 and f_S/2 and emit
        sigUnitChanged signal
        """

        rangeType = qget_cmb_box(self.cmbFRange)

        fb.fil[0].update({'freqSpecsRangeType': rangeType})
        if rangeType == 'whole':
            f_lim = [0, fb.fil[0]["f_S"]]
        elif rangeType == 'sym':
            f_lim = [-fb.fil[0]["f_S"] / 2., fb.fil[0]["f_S"] / 2.]
        else:
            f_lim = [0, fb.fil[0]["f_S"] / 2.]

        fb.fil[0]['freqSpecsRange'] = f_lim  # store settings in dict

        self.sigUnitChanged.emit()  # -> input_widgets
Esempio n. 45
0
    def _update_win_fft(self, dict_sig=None):
        """ Update window type for FFT """
        def _update_param1():
            self.lblWinPar1.setText(
                to_html(self.win_dict['par'][0][0] + " =", frmt='bi'))
            self.ledWinPar1.setText(str(self.win_dict['par'][2][0]))
            self.ledWinPar1.setToolTip(self.win_dict['par'][4][0])

        def _update_param2():
            self.lblWinPar2.setText(
                to_html(self.win_dict['par'][0][1] + " =", frmt='bi'))
            self.ledWinPar2.setText(str(self.win_dict['par'][2][1]))
            self.ledWinPar2.setToolTip(self.win_dict['par'][4][1])
#------------------------------------------------------------------------------

        self.window_name = qget_cmb_box(self.cmb_win_fft, data=False)
        self.win = calc_window_function(self.win_dict,
                                        self.window_name,
                                        N=self.N,
                                        sym=False)

        n_par = self.win_dict['n_par']

        self.lblWinPar1.setVisible(n_par > 0)
        self.ledWinPar1.setVisible(n_par > 0)
        self.lblWinPar2.setVisible(n_par > 1)
        self.ledWinPar2.setVisible(n_par > 1)

        if n_par > 0:
            _update_param1()
        if n_par > 1:
            _update_param2()

        self.nenbw = self.N * np.sum(np.square(self.win)) / (np.square(
            np.sum(self.win)))

        self.scale = self.N / np.sum(self.win)
        self.win *= self.scale  # correct gain for periodic signals (coherent gain)

        if not dict_sig or type(dict_sig) != dict:
            self.sig_tx.emit({'sender': __name__, 'data_changed': 'win'})
Esempio n. 46
0
    def _set_filter_type(self, enb_signal=False):
        """"
        Triggered when cmbFilterType (IIR, FIR, ...) is changed:
        - read filter type ft and copy it to fb.fil[0]['ft'] and self.ft
        - (re)construct design method combo, adding
          displayed text (e.g. "Chebychev 1") and hidden data (e.g. "cheby1")
        """
        # Read out current setting of comboBox and convert to string
        fb.fil[0]['ft'] = self.ft = qget_cmb_box(self.cmbFilterType)
#
        logger.debug("InputFilter.set_filter_type triggered: {0}".format(self.ft))

        #---------------------------------------------------------------
        # Get all available design methods for new ft from fil_tree and
        # - Collect them in fc_list
        # - Rebuild design method combobox entries for new ft setting:
        #    The combobox is populated with the "long name",
        #    the internal name is stored in comboBox.itemData
        self.cmbFilterClass.blockSignals(True)
        self.cmbFilterClass.clear()
        fc_list = []

        for fc in sorted(fb.fil_tree[self.rt][self.ft]):
            self.cmbFilterClass.addItem(fb.fil_classes[fc]['name'], fc)
            fc_list.append(fc)

        logger.debug("fc_list: {0}\n{1}".format(fc_list, fb.fil[0]['fc']))

        # Does new ft also provide the previous design method (e.g. ellip)?
        # Has filter been instantiated?
        if fb.fil[0]['fc'] in fc_list and ff.fil_inst:
            # yes, set same fc as before
            fc_idx = self.cmbFilterClass.findText(fb.fil_classes[fb.fil[0]['fc']]['name'])
            logger.debug("fc_idx : %s", fc_idx)
            self.cmbFilterClass.setCurrentIndex(fc_idx)
        else:
            self.cmbFilterClass.setCurrentIndex(0)     # no, set index 0

        self.cmbFilterClass.blockSignals(False)

        self._set_design_method(enb_signal)
Esempio n. 47
0
    def process_sig_rx(self, dict_sig=None):
        logger.debug("sig_rx:\n{0}".format(pprint_log(dict_sig)))
        # check whether anything needs to be done locally
        # could also check here for 'quant', 'ovfl', 'WI', 'WF' (not needed at the moment)
        if 'ui' in dict_sig:
            if dict_sig['ui'] == 'cmbW':
                cmbW = qget_cmb_box(self.wdg_w_accu.cmbW, data=False)
                self.wdg_w_accu.ledWF.setEnabled(cmbW == 'man')
                self.wdg_w_accu.ledWI.setEnabled(cmbW == 'man')
                if cmbW in {'full', 'auto'}:
                    self.dict2ui()
                    self.sig_tx.emit({
                        'sender': __name__,
                        'specs_changed': 'cmbW'
                    })
                else:
                    return

            dict_sig.update({'sender': __name__})

        self.sig_tx.emit(dict_sig)
Esempio n. 48
0
    def __init__(self, parent):
        super(Input_PZ, self).__init__(parent)
        
        self.data_changed = True # initialize flag: filter data has been changed
        self.ui_changed = True # initialize flag: ui for csv options has been changed

        self.Hmax_last = 1  # initial setting for maximum gain
        self.angle_char = "<" # "∠" may give problems with some encodings
        self.angle_char = unichr_23(int('2220', 16))
        
        self.tab_label = "P/Z"
        self.tool_tip = "Display and edit filter poles and zeros."

        self.ui = Input_PZ_UI(self) # create the UI part with buttons etc.
        self.norm_last = qget_cmb_box(self.ui.cmbNorm, data=False) # initial setting of cmbNorm
        self._construct_UI() # construct the rest of the UI

        self.load_dict() # initialize table from filterbroker
        self._refresh_table() # initialize table with values

        self.setup_signal_slot() # setup signal-slot connections and eventFilters
Esempio n. 49
0
    def frmt2cmplx(self, text, default=0.):
        """
        Convert format defined by cmbPZFrmt to real or complex
        """
        conv_error = False
        text = qstr(text).replace(" ", "") # convert to "proper" string without blanks
        if qget_cmb_box(self.ui.cmbPZFrmt) == 'cartesian':
            return safe_eval(text, default, return_type='auto')
        else:
            polar_str = text.split('*' + self.angle_char, 1)
            if len(polar_str) < 2: # input is real or imaginary
                r = safe_eval(re.sub('['+self.angle_char+'<∠°]','', text), default, return_type='auto')
                x = r.real
                y = r.imag
            else:
                r = safe_eval(polar_str[0], sign='pos')
                if safe_eval.err > 0:
                    conv_error = True

                if "°" in polar_str[1]:
                    scale = np.pi / 180. # angle in degrees
                elif re.search('π$|pi$', polar_str[1]):
                    scale = np.pi
                else:
                    scale = 1. # angle in rad

                # remove right-most special characters (regex $)
                polar_str[1] = re.sub('['+self.angle_char+'<∠°π]$|rad$|pi$', '', polar_str[1])
                phi = safe_eval(polar_str[1]) * scale
                if safe_eval.err > 0:
                    conv_error = True

                if not conv_error:
                    x = r * np.cos(phi)
                    y = r * np.sin(phi)
                else:
                    x = default.real
                    y = default.imag
                    logger.error("Expression {0} could not be evaluated.".format(text))
            return x + 1j * y
Esempio n. 50
0
    def __init__(self, parent):
        super(Input_PZ, self).__init__(parent)

        self.data_changed = True  # initialize flag: filter data has been changed
        self.ui_changed = True  # initialize flag: ui for csv options has been changed

        self.Hmax_last = 1  # initial setting for maximum gain
        self.angle_char = "\u2220"

        self.tab_label = "P/Z"
        self.tool_tip = "Display and edit filter poles and zeros."

        self.ui = Input_PZ_UI(self)  # create the UI part with buttons etc.
        self.norm_last = qget_cmb_box(self.ui.cmbNorm,
                                      data=False)  # initial setting of cmbNorm
        self._construct_UI()  # construct the rest of the UI

        self.load_dict()  # initialize table from filterbroker
        self._refresh_table()  # initialize table with values

        self.setup_signal_slot(
        )  # setup signal-slot connections and eventFilters
Esempio n. 51
0
    def _update_filter_cmb(self):
        """
        (Re-)Read list of available fixpoint filters for a given filter design 
        every time a new filter design is selected. 
        
        Then try to import the fixpoint designs in the list and populate the 
        fixpoint implementation combo box `self.cmb_wdg_fixp` when successfull. 
        """
        inst_wdg_str = ""  # full names of successfully instantiated widgets for logging
        last_fx_wdg = qget_cmb_box(
            self.cmb_wdg_fixp, data=False)  # remember last fx widget setting
        self.cmb_wdg_fixp.clear()
        fc = fb.fil[0]['fc']
        if 'fix' in fb.filter_classes[fc]:
            for class_name in fb.filter_classes[fc]['fix']:  # get class name
                try:
                    # construct module + class name
                    mod_class_name = fb.fixpoint_classes[class_name][
                        'mod'] + '.' + class_name
                    disp_name = fb.fixpoint_classes[class_name][
                        'name']  # # and display name
                    self.cmb_wdg_fixp.addItem(disp_name, mod_class_name)
                    inst_wdg_str += '\t' + class_name + ' : ' + mod_class_name + '\n'
                except AttributeError as e:
                    logger.warning('Widget "{0}":\n{1}'.format(class_name, e))
                    continue
                except KeyError as e:
                    logger.warning(
                        "No fixpoint filter for filter type {0} available.".
                        format(e))
                    continue

        # restore last fxp widget if possible
            idx = self.cmb_wdg_fixp.findText(last_fx_wdg)
            # set to idx 0 if not found (returned -1)
            self.cmb_wdg_fixp.setCurrentIndex(max(idx, 0))
        return inst_wdg_str
Esempio n. 52
0
    def _update_fixp_widget(self):
        """
        This method is called at the initialization of the ``input_fixpoint_specs``
         widget and when  a new fixpoint filter implementation is selected 
        in the combo box:

        - Destruct old instance of fixpoint filter widget
        
        - Import and instantiate new fixpoint filter widget e.g. after changing the 
          filter topology
          
        - Try to load image for filter topology
        
        - Update the UI of the widget
        
        - Instantiate
          ``self.hdl_filter_inst = self.fx_wdg_inst.hdlfilter``
        """
        # destruct old fixpoint widget instance
        if hasattr(self, "fx_wdg_inst"): # is a fixpoint widget loaded?
            try:
                self.layHWdg.removeWidget(self.fx_wdg_inst) # remove widget from layout
                self.fx_wdg_inst.deleteLater() # delete QWidget when scope has been left
            except AttributeError as e:
                logger.error("Could not destruct_UI!\n{0}".format(e))

        # instantiate new fixpoint widget class as self.fx_wdg_inst
        # which instantiates filter class as self.fx_wdg_inst.hdlfilter
        cmb_wdg_fx_cur = qget_cmb_box(self.cmb_wdg_fixp, data=False)
        if cmb_wdg_fx_cur: # at least one valid fixpoint widget found
            self.fx_wdg_found = True
            fx_mod_name = qget_cmb_box(self.cmb_wdg_fixp, data=True) # module name and path
            fx_mod = importlib.import_module(fx_mod_name) # get module 
            fx_wdg_class = getattr(fx_mod, cmb_wdg_fx_cur) # get class
            self.fx_wdg_inst = fx_wdg_class(self) # instantiate the widget
            self.layHWdg.addWidget(self.fx_wdg_inst, stretch=1) # and add it to layout
            self.wdg_dict2ui() # initialize the fixpoint subwidgets from the fxqc_dict

            #---- connect signals to fx_wdg_inst ----
            if hasattr(self.fx_wdg_inst, "sig_rx"):
                self.sig_rx.connect(self.fx_wdg_inst.sig_rx)
            #if hasattr(self.fx_wdg_inst, "sig_tx"):
                #self.fx_wdg_inst.sig_tx.connect(self.sig_rx)

            #---- instantiate and scale graphic of filter topology ----        
            if not (hasattr(self.fx_wdg_inst, "img_name") and self.fx_wdg_inst.img_name): # is an image name defined?
                self.fx_wdg_inst.img_name = "no_img.png"
                # check whether file exists
            file_path = os.path.dirname(fx_mod.__file__) # get path of imported fixpoint widget and 
            img_file = os.path.join(file_path, self.fx_wdg_inst.img_name) # construct full image name from it
            # _, file_extension = os.path.splitext(self.fx_wdg_inst.img_name)
            if os.path.exists(img_file):
                self.img_fixp = QPixmap(img_file)
#                if file_extension == '.png':
#                    self.img_fixp = QPixmap(img_file)
#                elif file_extension == '.svg':
#                    self.img_fixp = QtSvg.QSvgWidget(img_file)
            else:
                logger.warning("Image file {0} doesn't exist.".format(img_file))
                img_file = os.path.join(file_path, "hdl_dummy.png")                
                self.img_fixp = QPixmap(img_file)
                #self.lbl_img_fixp.setPixmap(QPixmap(self.img_fixp)) # fixed size
            self.resize_img()
                
            self.lblTitle.setText(self.fx_wdg_inst.title)

            #--- try to reference Python fixpoint filter instance -----
            if hasattr(self.fx_wdg_inst,'fxpy_filter'):
                self.fxpy_filter_inst = self.fx_wdg_inst.fxpy_filter
                self.butSimFxPy.setEnabled(True)
            else:
                self.butSimFxPy.setEnabled(False)
                
            #--- try to reference HDL fixpoint filter instance -----
            if hasattr(self.fx_wdg_inst,'hdlfilter'):
                self.hdl_filter_inst = self.fx_wdg_inst.hdlfilter
                self.butExportHDL.setEnabled(hasattr(self.fx_wdg_inst.hdlfilter, "convert"))
                self.butSimHDL.setEnabled(hasattr(self.fx_wdg_inst.hdlfilter, "run_sim"))
                self.update_fxqc_dict()
                #self.fx_wdg_inst.update_hdl_filter()
            else:
                self.hdl_filter_inst = None
                self.butSimHDL.setEnabled(False)
                self.butExportHDL.setEnabled(False)

        else:
            self.fx_wdg_found = False
            self.butSimFxPy.setEnabled(False)
            self.butSimHDL.setEnabled(False)
            self.butExportHDL.setEnabled(False)
Esempio n. 53
0
    def _construct_UI(self):
        """
        Construct UI with comboboxes for selecting filter:

        - cmbResponseType for selecting response type rt (LP, HP, ...)

        - cmbFilterType for selection of filter type (IIR, FIR, ...)

        - cmbFilterClass for selection of design design class (Chebychev, ...)

        and populate them from the "filterTree" dict during the initial run.
        Later, calling _set_response_type() updates the three combo boxes.

        See filterbroker.py for structure and content of "filterTree" dict

        """
        #----------------------------------------------------------------------
        # Combo boxes for filter selection
        #----------------------------------------------------------------------
        self.cmbResponseType = QComboBox(self)
        self.cmbResponseType.setObjectName("comboResponseType")
        self.cmbResponseType.setToolTip("Select filter response type.")
        self.cmbFilterType = QComboBox(self)
        self.cmbFilterType.setObjectName("comboFilterType")
        self.cmbFilterType.setToolTip(
          "<span>Select the filter type (recursive (Infinite Impulse Response), nonRecursive (Finite IR).</span>")
        self.cmbFilterClass = QComboBox(self)
        self.cmbFilterClass.setObjectName("comboFilterClass")
        self.cmbFilterClass.setToolTip("Select the filter design class.")

        # Adapt comboboxes size dynamically to largest element
        self.cmbResponseType.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbFilterType.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.cmbFilterClass.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        #----------------------------------------------------------------------
        # Populate combo box with initial settings from fb.fil_tree
        #----------------------------------------------------------------------
        # Translate short response type ("LP") to displayed names ("Lowpass")
        # (correspondence is defined in pyfda_rc.py) and populate rt combo box
        #
        rt_list = sorted(list(fb.fil_tree.keys()))

        for rt in rt_list:
            try:
                self.cmbResponseType.addItem(rc.rt_names[rt], rt)
            except KeyError as e:
                logger.warning(
                  "KeyError: {0} has no corresponding full name in rc.rt_names.".format(e))
        idx = self.cmbResponseType.findData('LP') # find index for 'LP'

        if idx == -1: # Key 'LP' does not exist, use first entry instead
            idx = 0

        self.cmbResponseType.setCurrentIndex(idx) # set initial index
        rt = qget_cmb_box(self.cmbResponseType)

        for ft in fb.fil_tree[rt]:
            self.cmbFilterType.addItem(rc.ft_names[ft], ft)
        self.cmbFilterType.setCurrentIndex(0) # set initial index
        ft = qget_cmb_box(self.cmbFilterType)

        for fc in fb.fil_tree[rt][ft]:
            self.cmbFilterClass.addItem(fb.fil_classes[fc]['name'], fc)
        self.cmbFilterClass.setCurrentIndex(0) # set initial index

        #----------------------------------------------------------------------
        # Layout for Filter Type Subwidgets
        #----------------------------------------------------------------------

        layHFilWdg = QHBoxLayout() # container for filter subwidgets
        layHFilWdg.addWidget(self.cmbResponseType) #LP, HP, BP, etc.
        layHFilWdg.addStretch()
        layHFilWdg.addWidget(self.cmbFilterType)   #FIR, IIR
        layHFilWdg.addStretch()
        layHFilWdg.addWidget(self.cmbFilterClass)  #bessel, elliptic, etc.

        #----------------------------------------------------------------------
        # Layout for dynamic filter subwidgets (empty frame)
        #----------------------------------------------------------------------
        # see Summerfield p. 278
        self.layHDynWdg = QHBoxLayout() # for additional dynamic subwidgets

        #----------------------------------------------------------------------
        # Filter Order Subwidgets
        #----------------------------------------------------------------------
        self.lblOrder =  QLabel("<b>Order:</b>")
        self.chkMinOrder = QCheckBox("Minimum", self)
        self.chkMinOrder.setToolTip("<span>Minimum filter order / # of taps is determined automatically.</span>")
        self.lblOrderN = QLabel("<b><i>N =</i></b>")
        self.ledOrderN = QLineEdit(str(fb.fil[0]['N']),self)
        self.ledOrderN.setToolTip("Filter order (# of taps - 1).")

        #--------------------------------------------------
        #  Layout for filter order subwidgets
        layHOrdWdg = QHBoxLayout()
        layHOrdWdg.addWidget(self.lblOrder)
        layHOrdWdg.addWidget(self.chkMinOrder)
        layHOrdWdg.addStretch()
        layHOrdWdg.addWidget(self.lblOrderN)
        layHOrdWdg.addWidget(self.ledOrderN)

        #----------------------------------------------------------------------
        # OVERALL LAYOUT (stack standard + dynamic subwidgets vertically)
        #----------------------------------------------------------------------
        self.layVAllWdg = QVBoxLayout()
        self.layVAllWdg.addLayout(layHFilWdg)
        self.layVAllWdg.addLayout(self.layHDynWdg)
        self.layVAllWdg.addLayout(layHOrdWdg)

#==============================================================================
        frmMain = QFrame(self)
        frmMain.setLayout(self.layVAllWdg)

        layHMain = QHBoxLayout()
        layHMain.addWidget(frmMain)
        layHMain.setContentsMargins(*rc.params['wdg_margins'])

        self.setLayout(layHMain)

#==============================================================================

        #------------------------------------------------------------
        # SIGNALS & SLOTS
        #------------------------------------------------------------
        # Connect comboBoxes and setters, propgate change events hierarchically
        #  through all widget methods and generate the signal sigFiltChanged
        #  in the end.
        self.cmbResponseType.currentIndexChanged.connect(
                lambda: self._set_response_type(enb_signal=True))# 'LP'
        self.cmbFilterType.currentIndexChanged.connect(
                lambda: self._set_filter_type(enb_signal=True))  #'IIR'
        self.cmbFilterClass.currentIndexChanged.connect(
                lambda: self._set_design_method(enb_signal=True))#'cheby1'
        self.chkMinOrder.clicked.connect(
                lambda: self._set_filter_order(enb_signal=True)) # Min. Order
        self.ledOrderN.editingFinished.connect(
                lambda:self._set_filter_order(enb_signal=True))  # Manual Order
Esempio n. 54
0
    def _update_win_fft(self, dict_sig=None):
        """ Update window type for FFT """

        def _update_param1():
            self.ledWinPar1.setToolTip(tooltip)
            self.lblWinPar1.setText(to_html(txt_par1, frmt='bi'))

            self.param1 = safe_eval(self.ledWinPar1.text(), self.param1, return_type='float', sign='pos')
            self.ledWinPar1.setText(str(self.param1))
        #----------------------------------------------------------------------
        self.window_type = qget_cmb_box(self.cmb_win_fft, data=False)
#        self.param1 = None
        has_par1 = False
        txt_par1 = ""

        if self.window_type in {"Bartlett", "Triangular"}:
            window_name = "bartlett"
        elif self.window_type == "Flattop":
            window_name = "flattop"
        elif self.window_type == "Hamming":
            window_name = "hamming"
        elif self.window_type == "Hann":
            window_name = "hann"
        elif self.window_type == "Rect":
            window_name = "boxcar"
        elif self.window_type == "Kaiser":
            window_name = "kaiser"
            has_par1 = True
            txt_par1 = '&beta; ='

            tooltip = ("<span>Shape parameter; lower values reduce  main lobe width, "
                       "higher values reduce side lobe level, typ. value is 5.</span>")
            _update_param1()
            if not self.param1:
                self.param1 = 5

        elif self.window_type == "Chebwin":
            window_name = "chebwin"
            has_par1 = True
            txt_par1 = 'Attn ='
            tooltip = ("<span>Side lobe attenuation in dB (typ. 80 dB).</span>")
            _update_param1()
            if not self.param1:
                self.param1 = 80
            if self.param1 < 45:
                logger.warning("Attenuation needs to be larger than 45 dB!")

        else:
            logger.error("Unknown window type {0}".format(self.window_type))

        # get attribute window_name from submodule sig.windows and
        # returning the desired window function:
        win_fnct = getattr(sig.windows, window_name, None)
        if not win_fnct:
            logger.error("No window function {0} in scipy.signal.windows, using rectangular window instead!"\
                         .format(window_name))
            win_fnct = sig.windows.boxcar
            self.param1 = None

        self.lblWinPar1.setVisible(has_par1)
        self.ledWinPar1.setVisible(has_par1)
        if has_par1:
            self.win = win_fnct(self.N, self.param1) # use additional parameter
        else:
            self.win = win_fnct(self.N)

        self.nenbw = self.N * np.sum(np.square(self.win)) / (np.square(np.sum(self.win)))

        self.scale = self.N / np.sum(self.win)
        self.win *= self.scale # correct gain for periodic signals (coherent gain)

        if not dict_sig or type(dict_sig) != dict:
            self.sig_tx.emit({'sender':__name__, 'data_changed':'win'})
        else:
            self.sig_tx.emit(dict_sig)
Esempio n. 55
0
    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.a_lk.isChecked():
            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()
Esempio n. 56
0
    def _refresh_table(self):
        """
        (Re-)Create the displayed table from `self.ba` (list with 2 columns of
        float scalars). Data is displayed via `ItemDelegate.displayText()` in
        the number format set by `self.frmt`.

        The table dimensions are set according to the dimensions of `self.ba`:
        - self.ba[0] -> b coefficients
        - self.ba[1] -> a coefficients

        Called at the end of nearly every method.
        """
        try:
            self.num_rows = max(len(self.ba[1]), len(self.ba[0]))
        except IndexError:
            self.num_rows = len(self.ba[0])
        logger.debug("np.shape(ba) = {0}".format(np.shape(self.ba)))

        params['FMT_ba'] = int(self.ui.spnDigits.text())

        # When format is 'float', disable all fixpoint options
        is_float = (qget_cmb_box(self.ui.cmbFormat, data=False).lower() == 'float')

        self.ui.spnDigits.setVisible(is_float) # number of digits can only be selected
        self.ui.lblDigits.setVisible(is_float) # for format = 'float'
        self.ui.cmbQFrmt.setVisible(not is_float) # hide unneeded widgets for format = 'float'
        self.ui.lbl_W.setVisible(not is_float)
        self.ui.ledW.setVisible(not is_float)

        if self.ui.butEnable.isChecked():
            self.ui.frmQSettings.setVisible(not is_float) # hide all q-settings for float
            self.ui.butEnable.setIcon(QIcon(':/circle-x.svg'))
            self.tblCoeff.setVisible(True)

            self._store_q_settings() # store updated quantization / format settings

            # check whether filter is FIR and only needs one column
            if fb.fil[0]['ft'] == 'FIR':
                self.num_cols = 1
                self.tblCoeff.setColumnCount(1)
                self.tblCoeff.setHorizontalHeaderLabels(["b"])
                qset_cmb_box(self.ui.cmbFilterType, 'FIR')
            else:
                self.num_cols = 2
                self.tblCoeff.setColumnCount(2)
                self.tblCoeff.setHorizontalHeaderLabels(["b", "a"])
                qset_cmb_box(self.ui.cmbFilterType, 'IIR')

                self.ba[1][0] = 1.0 # restore fa[0] = 1 of denonimator polynome

            self.tblCoeff.setRowCount(self.num_rows)
            self.tblCoeff.setColumnCount(self.num_cols)
            # Create strings for index column (vertical header), starting with "0"
            idx_str = [str(n) for n in range(self.num_rows)]
            self.tblCoeff.setVerticalHeaderLabels(idx_str)

            self.tblCoeff.blockSignals(True)
            for col in range(self.num_cols):
                for row in range(self.num_rows):
                    self._refresh_table_item(row, col)

            # make a[0] selectable but not editable
            if fb.fil[0]['ft'] == 'IIR':
                item = self.tblCoeff.item(0,1)
                item.setFlags(Qt.ItemIsSelectable| Qt.ItemIsEnabled)
                item.setFont(self.ui.bfont)

            self.tblCoeff.blockSignals(False)

            self.tblCoeff.resizeColumnsToContents()
            self.tblCoeff.resizeRowsToContents()
            self.tblCoeff.clearSelection()

        else:
            self.ui.frmQSettings.setVisible(False)
            self.ui.butEnable.setIcon(QIcon(':/circle-check.svg'))
            self.tblCoeff.setVisible(False)