Ejemplo n.º 1
0
    def test_cmb_filter_type(self):
        """Test setting <Filter Type> ComboBox and the effect on the table shape"""
        self.init()
        self.set_cmb_box(self.ui.cmbFilterType, 'IIR')
        self.assertEqual(qget_cmb_box(self.ui.cmbFilterType, data=False),
                         "IIR")
        self.ui.cmbFilterType.currentIndexChanged.emit(1)
        QTest.mouseClick(self.ui.cmbFilterType, Qt.LeftButton)
        QTest.keyClick(QApplication.instance().focusWidget(), Qt.Key_PageDown)
        QTest.qWait(1000)
        QTest.keyClick(QApplication.instance().focusWidget(), Qt.Key_Return)
        QTest.qWait(1000)
        self.assertEqual(qget_cmb_box(self.ui.cmbFilterType, data=False),
                         "IIR")
        # https://vicrucann.github.io/tutorials/qttest-signals-qtreewidget/
        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 2)
        item_10 = self.form.tblCoeff.item(0, 1)  # row, col
        self.assertEqual(float(item_10.text()), 1)

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

        self.assertEqual(self.form.tblCoeff.rowCount(), 3)
        self.assertEqual(self.form.tblCoeff.columnCount(), 1)
        self.log.warning("test_cmb_filter_type finished")
Ejemplo 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']`.
        """
        try:
            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])))))
        except Exception as e:
            logger.error(e)
            return

        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'])
Ejemplo n.º 3
0
    def test_defaults(self):
        """Test GUI setting in its default state"""
        self.init()
        self.assertEqual(self.ui.spnDigits.value(), 4)
        self.assertEqual(qget_cmb_box(self.ui.cmbFilterType, data=False),
                         "FIR")

        self.assertEqual(
            qget_cmb_box(self.ui.cmbFormat, data=False).lower(), "float")
        self.assertEqual(self.ui.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")
        self.log.warning("test_defaults finished")
Ejemplo n.º 4
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()
Ejemplo n.º 5
0
    def _freq_range(self, emit=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

        Emit 'view_changed':'f_range' when `emit=True`
        """
        if type(emit) == int:  # signal was emitted by combobox
            emit = True

        rangeType = qget_cmb_box(self.cmbFRange)

        fb.fil[0].update({'freqSpecsRangeType': rangeType})
        f_max = fb.fil[0]["f_max"]

        if rangeType == 'whole':
            f_lim = [0, f_max]
        elif rangeType == 'sym':
            f_lim = [-f_max / 2., f_max / 2.]
        else:
            f_lim = [0, f_max / 2.]

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

        if emit:
            self.emit({'view_changed': 'f_range'})
Ejemplo n.º 6
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 not, just pass the dict 
        if 'ui' in dict_sig:
            if dict_sig['id'] == 'w_coeff': # coefficient format updated
                """
                Update coefficient quantization settings and coefficients.
        
                The new values are written to the fixpoint coefficient dict as
                `fb.fil[0]['fxqc']['QCB']` and  `fb.fil[0]['fxqc']['b']`.
                """  

                fb.fil[0]['fxqc'].update(self.ui2dict())
                
            elif 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__}) # currently only local

        self.sig_tx.emit(dict_sig)
Ejemplo n.º 7
0
    def _W_changed(self):
        """
        Set fractional and integer length `WF` and `WI` when wordlength `W` has
        been changed. Try to preserve `WI` or `WF` settings depending on the
        number format (integer or fractional).
        """
        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.ui2qdict()
Ejemplo n.º 8
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='poszero')
            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__, 'ui_changed': 'noi'})
Ejemplo n.º 9
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)
Ejemplo n.º 10
0
    def _set_number_format(self):
        """
        Triggered by `contruct_UI()`, `qdict2ui()`and by `ui.cmbQFrmt.currentIndexChanged()`
        
        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
Ejemplo n.º 11
0
    def _update_win_fft(self):
        """ Update window type for FirWin """
        self.alg = str(self.cmb_firwin_alg.currentText())
        self.fir_window_name = qget_cmb_box(self.cmb_firwin_win, data=False)
        self.win = calc_window_function(self.win_dict,
                                        self.fir_window_name,
                                        N=self.N,
                                        sym=True)
        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:
            self.lblWinPar1.setText(
                to_html(self.win_dict['par'][0]['name'] + " =", frmt='bi'))
            self.ledWinPar1.setText(str(self.win_dict['par'][0]['val']))
            self.ledWinPar1.setToolTip(self.win_dict['par'][0]['tooltip'])

        if n_par > 1:
            self.lblWinPar2.setText(
                to_html(self.win_dict['par'][1]['name'] + " =", frmt='bi'))
            self.ledWinPar2.setText(str(self.win_dict['par'][1]['val']))
            self.ledWinPar2.setToolTip(self.win_dict['par'][1]['tooltip'])

        # sig_tx -> select_filter -> filter_specs
        self.sig_tx.emit({'sender': __name__, 'filt_changed': 'firwin'})
Ejemplo 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", "PM", "FM", "AM"
        }
        f2_en = self.stim in {"Cos", "Sine", "PM", "FM", "AM"}
        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__, 'ui_changed': 'stim'})
Ejemplo n.º 13
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'})
Ejemplo n.º 14
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_max_2 = fb.fil[0]['f_max'] / 2.

        #========= select frequency range to be displayed =====================
        #=== shift, scale and select: W -> F, H_cplx -> H_c
        F = self.W * f_max_2 / 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_max_2
        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.xaxis.set_minor_locator(AutoMinorLocator()) # enable minor ticks
        self.ax.yaxis.set_minor_locator(AutoMinorLocator()) # enable minor ticks
        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()
Ejemplo n.º 15
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)
Ejemplo n.º 16
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'], id='w_coeff',
                                        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_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='', id='w_accu',
                               fractional=True, combo_visible=True)

        self.wdg_q_accu = UI_Q(self, fb.fil[0]['fxqc']['QA'], id='q_accu',
                               label='Accu Format <i>Q<sub>A&nbsp;</sub></i>:')

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

        #----------------------------------------------------------------------
        # LOCAL SIGNALS & SLOTs & EVENTFILTERS
        #----------------------------------------------------------------------      
        self.wdg_w_coeffs.sig_tx.connect(self.update_q_coeff)
        self.wdg_w_accu.sig_tx.connect(self.process_sig_rx)
        self.wdg_q_accu.sig_tx.connect(self.process_sig_rx)
#------------------------------------------------------------------------------

        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)
Ejemplo n.º 17
0
    def test_fixpoint_defaults(self):
        """Test fixpoint setting in its default state"""
        self.init()
        self.set_cmb_box(self.ui.cmbFormat, 'Dec')
        self.assertEqual(self.ui.spnDigits.value(), 4)
        self.assertEqual(qget_cmb_box(self.ui.cmbFilterType, data=False),
                         "FIR")

        self.assertEqual(self.ui.ledW.text(), "16")
        self.assertEqual(self.ui.ledWF.text(), "15")
        self.assertEqual(self.ui.ledWI.text(), "0")
        self.assertEqual(
            qget_cmb_box(self.ui.cmbFormat, data=False).lower(), "dec")
        self.assertEqual(self.get_cmb_box(self.ui.cmbQOvfl), 'wrap')
        self.assertEqual(self.get_cmb_box(self.ui.cmbQuant), 'floor')
        self.assertEqual(self.ui.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")
        self.log.warning("test_fixpoint_defaults finished")
Ejemplo n.º 18
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))
Ejemplo n.º 19
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)
Ejemplo n.º 20
0
    def draw(self):
        self.but_fir_poles.setVisible(fb.fil[0]['ft'] == 'FIR')
        contour = qget_cmb_box(self.cmb_overlay) in {"contour", "contourf"}
        self.ledBottom.setVisible(contour)
        self.lblBottom.setVisible(contour)
        self.lblBottomdB.setVisible(contour and self.but_log.isChecked())
        self.ledTop.setVisible(contour)
        self.lblTop.setVisible(contour)
        self.lblTopdB.setVisible(contour and self.but_log.isChecked())

        if True:
            self.init_axes()
        self.draw_pz()
Ejemplo n.º 21
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")
Ejemplo n.º 22
0
    def _update_win_fft(self, arg=None, emit=True):
        """
        Update window type for FFT  with different arguments:

        - signal-slot connection to combo-box -> index (int), absorbed by `arg`
                                                 emit is not set -> emit=True
        - called by _read_param() -> empty -> emit=True
        - called by update_N(emit=False)

        """
        if not isinstance(emit, bool):
            logger.error("update win: emit={0}".format(emit))
        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:
            self.lblWinPar1.setText(
                to_html(self.win_dict['par'][0]['name'] + " =", frmt='bi'))
            self.ledWinPar1.setText(str(self.win_dict['par'][0]['val']))
            self.ledWinPar1.setToolTip(self.win_dict['par'][0]['tooltip'])

        if n_par > 1:
            self.lblWinPar2.setText(
                to_html(self.win_dict['par'][1]['name'] + " =", frmt='bi'))
            self.ledWinPar2.setText(str(self.win_dict['par'][1]['val']))
            self.ledWinPar2.setToolTip(self.win_dict['par'][1]['tooltip'])

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

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

        # only emit a signal for local triggers to prevent infinite loop:
        # - signal-slot connection passes a bool or an integer
        # - local function calls don't pass anything
        if emit is True:
            self.sig_tx.emit({'sender': __name__, 'ui_changed': 'win'})
        # ... but always notify the FFT widget via sig_tx_fft
        self.sig_tx_fft.emit({'sender': __name__, 'view_changed': 'win'})
Ejemplo n.º 23
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:
            # try to split text string at "*<" or the angle character
            polar_str = text.replace(self.angle_char, '<').split('*<', 1)

            if len(polar_str) < 2:  # input is real or imaginary
                # remove special characters
                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
Ejemplo n.º 24
0
    def _update_filter_cmb(self) -> str:
        """
        (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_fx_wdg` when successfull.

        Returns
        -------
        inst_wdg_str: str
          string with all fixpoint widgets that could be instantiated successfully
        """
        inst_wdg_str = ""  # full names of successfully instantiated widgets for logging
        # remember last fx widget setting:
        last_fx_wdg = qget_cmb_box(self.cmb_fx_wdg, data=False)
        self.cmb_fx_wdg.clear()
        fc = fb.fil[0]['fc']

        if 'fix' in fb.filter_classes[fc]:
            self.cmb_fx_wdg.blockSignals(True)
            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
                    # ... and display name
                    disp_name = fb.fixpoint_classes[class_name]['name']
                    self.cmb_fx_wdg.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))
                    self.embed_fixp_img(self.no_fx_filter_img)
                    continue  # with next `class_name` of for loop
                except KeyError as e:
                    logger.warning("No fixpoint filter for filter type {0} available."
                                   .format(e))
                    self.embed_fixp_img(self.no_fx_filter_img)
                    continue  # with next `class_name` of for loop

            # restore last fx widget if possible
            idx = self.cmb_fx_wdg.findText(last_fx_wdg)
            # set to idx 0 if not found (returned -1)
            self.cmb_fx_wdg.setCurrentIndex(max(idx, 0))
            self.cmb_fx_wdg.blockSignals(False)
        else:  # no fixpoint widget
            self.embed_fixp_img(self.no_fx_filter_img)
        self._update_fixp_widget()
        return inst_wdg_str
Ejemplo n.º 25
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", "Chirp", "PM", "FM", "AM", "Formula", "Rect", "Saw",
            "Triang", "Comb"
        }
        f2_en = self.stim in {
            "Cos", "Sine", "Chirp", "PM", "FM", "AM", "Formula"
        }
        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.chk_scale_impz_f.setVisible(self.stim == 'Pulse')
        self.chk_scale_impz_f.setEnabled((self.noi == 0 or self.cmbNoise.currentText() == 'None')\
                                         and self.DC == 0)

        self.cmbChirpMethod.setVisible(self.stim == 'Chirp')

        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 and self.stim != "Chirp")
        self.ledAmp2.setVisible(f2_en and self.stim != "Chirp")
        self.lblPhi2.setVisible(f2_en and self.stim != "Chirp")
        self.ledPhi2.setVisible(f2_en and self.stim != "Chirp")
        self.lblPhU2.setVisible(f2_en and self.stim != "Chirp")

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

        self.lblStimFormula.setVisible(self.stim == "Formula")
        self.ledStimFormula.setVisible(self.stim == "Formula")

        self.sig_tx.emit({'sender': __name__, 'ui_changed': 'stim'})
Ejemplo n.º 26
0
    def process_sig_rx(self, dict_sig=None):
        logger.warning("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 not, just emit the dict.
        if 'ui' in dict_sig:
            if dict_sig['wdg_name'] == 'w_coeff':  # coefficient format updated
                """
                Update coefficient quantization settings and coefficients.

                The new values are written to the fixpoint coefficient dict as
                `fb.fil[0]['fxqc']['QCB']` and  `fb.fil[0]['fxqc']['b']`.
                """

                fb.fil[0]['fxqc'].update(self.ui2dict())

            elif dict_sig['wdg_name'] == 'w_accu':  # accu format updated
                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'}\
                        or ('ui' in dict_sig and dict_sig['ui'] in {'WF', 'WI'}):
                    pass

                elif cmbW == 'man':  # switched to manual, don't do anything
                    return

            # Accu quantization or overflow settings have been changed
            elif dict_sig['wdg_name'] == 'q_accu':
                pass

            else:
                logger.error(f"Unknown widget name '{dict_sig['wdg_name']}' "
                             f"in '{__name__}' !")
                return

            # - update fixpoint accu and coefficient quantization dict
            # - emit {'fx_sim': 'specs_changed'}
            fb.fil[0]['fxqc'].update(self.ui2dict())
            self.emit({'fx_sim': 'specs_changed'})

        else:
            logger.error(
                f"Unknown key '{dict_sig['wdg_name']}' (should be 'ui')"
                f"in '{__name__}' !")
Ejemplo n.º 27
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'})
Ejemplo n.º 28
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. "Chebyshev 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.filter_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.filter_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)
Ejemplo n.º 29
0
    def _enable_stim_widgets(self):
        """ Enable / disable widgets depending on the selected stimulus """
        self.stim = qget_cmb_box(self.cmbStimulus, data=False)
        stim_wdg = self.stim_wdg_dict[self.stim]

        self.lblDC.setVisible("dc" in stim_wdg)
        self.ledDC.setVisible("dc" in stim_wdg)

        self.chk_scale_impz_f.setVisible(self.stim == 'Impulse')
        self.chk_scale_impz_f.setEnabled(self.DC == 0 and (self.noi == 0 or\
            self.cmbNoise.currentText() == 'None'))

        self.lblStimPar1.setVisible("par1" in stim_wdg)
        self.ledStimPar1.setVisible("par1" in stim_wdg)

        self.chk_stim_bl.setVisible("bl" in stim_wdg)

        self.lblAmp1.setVisible("a1" in stim_wdg)
        self.ledAmp1.setVisible("a1" in stim_wdg)
        self.lblPhi1.setVisible("phi1" in stim_wdg)
        self.ledPhi1.setVisible("phi1" in stim_wdg)
        self.lblPhU1.setVisible("phi1" in stim_wdg)
        self.lblFreq1.setVisible("f1" in stim_wdg)
        self.ledFreq1.setVisible("f1" in stim_wdg)
        self.lblFreqUnit1.setVisible("f1" in stim_wdg)

        self.lblAmp2.setVisible("a2" in stim_wdg)
        self.ledAmp2.setVisible("a2" in stim_wdg)
        self.lblPhi2.setVisible("phi2" in stim_wdg)
        self.ledPhi2.setVisible("phi2" in stim_wdg)
        self.lblPhU2.setVisible("phi2" in stim_wdg)
        self.lblFreq2.setVisible("f2" in stim_wdg)
        self.ledFreq2.setVisible("f2" in stim_wdg)
        self.lblFreqUnit2.setVisible("f2" in stim_wdg)

        self.lblStimFormula.setVisible(self.stim == "Formula")
        self.ledStimFormula.setVisible(self.stim == "Formula")

        self.cmbChirpMethod.setVisible(self.stim == 'Chirp')

        self.sig_tx.emit({'sender':__name__, 'ui_changed':'stim'})
Ejemplo n.º 30
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))
                    self.embed_fixp_img(self.no_fx_filter_img)
                    continue
                except KeyError as e:
                    logger.warning(
                        "No fixpoint filter for filter type {0} available.".
                        format(e))
                    self.embed_fixp_img(self.no_fx_filter_img)
                    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))
        else:  # no fixpoint widget
            self.embed_fixp_img(self.no_fx_filter_img)
        return inst_wdg_str