Пример #1
0
    def dict2ui(self, q_dict=None):
        """
        Update the widgets `WI` and `WF` and the corresponding attributes
        from the dict passed as the argument
        """
        if q_dict is None:
            q_dict = self.q_dict

        if 'WI' in q_dict:
            self.WI = safe_eval(q_dict['WI'],
                                self.WI,
                                return_type="int",
                                sign='poszero')
            self.ledWI.setText(qstr(self.WI))
        else:
            logger.warning("No key 'WI' in dict!")

        if 'WF' in q_dict:
            self.WF = safe_eval(q_dict['WF'],
                                self.WF,
                                return_type="int",
                                sign='poszero')
            self.ledWF.setText(qstr(self.WF))
        else:
            logger.warning("No key 'WF' in dict!")

        self.W = self.WF + self.WI + 1
Пример #2
0
    def ui2qdict(self):
        """
        Triggered by modifying 
        `ui.cmbFormat`, `ui.cmbQOvfl`, `ui.cmbQuant`, `ui.ledWF`, `ui.ledWI`
        or `ui.ledW` (via `_W_changed()`)
        or `ui.cmbQFrmt` (via `_set_number_format()`)
        or `ui.ledScale()` (via `_set_scale()`)
        or 'qdict2ui()' via `_set_number_format()`
        
        Read out the settings of the quantization comboboxes.

        - Store them in the filter dict `fb.fil[0]['fxqc']['QCB']` and as class
            attributes in the fixpoint object `self.myQ`

        - Emit a signal with `'view_changed':'q_coeff'`

        - Refresh the table
        """
        fb.fil[0]['fxqc']['QCB'] = {
                'WI':safe_eval(self.ui.ledWI.text(), self.myQ.WI, return_type='int'),
                'WF':safe_eval(self.ui.ledWF.text(), self.myQ.WF, return_type='int', sign='poszero'),
                'W':safe_eval(self.ui.ledW.text(), self.myQ.W, return_type='int', sign='pos'),
                'quant':qstr(self.ui.cmbQuant.currentText()),
                'ovfl':qstr(self.ui.cmbQOvfl.currentText()),
                'frmt':qstr(self.ui.cmbFormat.currentText().lower()),
                'scale':qstr(self.ui.ledScale.text())
                }

        self.myQ.setQobj(fb.fil[0]['fxqc']['QCB']) # update fixpoint object

        self.sig_tx.emit({'sender':__name__, 'view_changed':'q_coeff'})
        
        self._update_MSB_LSB()

        self._refresh_table()
Пример #3
0
    def ui2dict(self, s=None):
        """
        Update the attributes `self.WI`, `self.WF` and `self.W` and `self.q_dict`
        when one of the QLineEdit widgets has been edited.

        Emit a signal with `{'ui':objectName of the sender}`.
        """

        self.WI = int(
            safe_eval(self.ledWI.text(),
                      self.WI,
                      return_type="int",
                      sign='poszero'))
        self.ledWI.setText(qstr(self.WI))
        self.WF = int(
            safe_eval(self.ledWF.text(),
                      self.WF,
                      return_type="int",
                      sign='poszero'))
        self.ledWF.setText(qstr(self.WF))
        self.W = int(self.WI + self.WF + 1)

        self.q_dict.update({'WI': self.WI, 'WF': self.WF, 'W': self.W})

        if self.sender():
            obj_name = self.sender().objectName()
            logger.debug("sender: {0}".format(obj_name))
            dict_sig = {'wdg_name': self.wdg_name, 'ui': obj_name}
            self.emit(dict_sig)
        elif s == 'init':
            logger.debug("called by __init__")
        else:
            logger.error("sender without name!")
Пример #4
0
    def qdict2ui(self):
        """
        Triggered by:
        - process_sig_rx()  if self.fx_specs_changed or dict_sig['fx_sim'] == 'specs_changed'
        - 
        Set the UI from the quantization dict and update the fixpoint object.
        When neither WI == 0 nor WF == 0, set the quantization format to general
        fractional format qfrac.
        """
        self.ui.ledWI.setText(qstr(fb.fil[0]['fxqc']['QCB']['WI']))
        self.ui.ledWF.setText(qstr(fb.fil[0]['fxqc']['QCB']['WF']))
        self.ui.ledW.setText(qstr(fb.fil[0]['fxqc']['QCB']['W']))
        if fb.fil[0]['fxqc']['QCB']['WI'] != 0 and fb.fil[0]['fxqc']['QCB'][
                'WF'] != 0:
            qset_cmb_box(self.ui.cmbQFrmt, 'qfrac', data=True)

        self.ui.ledScale.setText(qstr(fb.fil[0]['fxqc']['QCB']['scale']))
        qset_cmb_box(self.ui.cmbQuant, fb.fil[0]['fxqc']['QCB']['quant'])
        qset_cmb_box(self.ui.cmbQOvfl, fb.fil[0]['fxqc']['QCB']['ovfl'])

        self.myQ.setQobj(fb.fil[0]['fxqc']['QCB'])  # update class attributes

        self._set_number_format(
        )  # quant format has been changed, update display
        self._update_MSB_LSB()
Пример #5
0
    def setModelData(self, editor, model, index):
        """
        When editor has finished, read the updated data from the editor,
        convert it back to floating point format and store it in both the model
        (= QTableWidget) and in self.ba. Finally, refresh the table item to
        display it in the selected format (via `float2frmt()`).

        editor: instance of e.g. QLineEdit
        model:  instance of QAbstractTableModel
        index:  instance of QModelIndex
        """

        # check for different editor environments if needed and provide a default:
#        if isinstance(editor, QtGui.QTextEdit):
#            model.setData(index, editor.toPlainText())
#        elif isinstance(editor, QComboBox):
#            model.setData(index, editor.currentText())
#        else:
#            super(ItemDelegate, self).setModelData(editor, model, index)
        if self.parent.myQ.frmt == 'float':
            data = safe_eval(qstr(editor.text()),
                             self.parent.ba[index.column()][index.row()], return_type='auto') # raw data without fixpoint formatting
        else:
            data = self.parent.myQ.frmt2float(qstr(editor.text()),
                                    self.parent.myQ.frmt) # transform back to float

        model.setData(index, data)                          # store in QTableWidget
        # if the entry is complex, convert ba (list of arrays) to complex type
        if isinstance(data, complex):
            self.parent.ba[0] = self.parent.ba[0].astype(complex)
            self.parent.ba[1] = self.parent.ba[1].astype(complex)
        self.parent.ba[index.column()][index.row()] = data  # store in self.ba
        qstyle_widget(self.parent.ui.butSave, 'changed')
        self.parent._refresh_table_item(index.row(), index.column()) # refresh table entry
Пример #6
0
    def setModelData(self, editor, model, index):
        """
        When editor has finished, read the updated data from the editor,
        convert it to complex format and store it in both the model
        (= QTableWidget) and in `zpk`. Finally, refresh the table item to
        display it in the selected format (via `to be defined`) and normalize
        the gain.

        editor: instance of e.g. QLineEdit
        model:  instance of QAbstractTableModel
        index:  instance of QModelIndex
        """

        # check for different editor environments if needed and provide a default:
        #        if isinstance(editor, QtGui.QTextEdit):
        #            model.setData(index, editor.toPlainText())
        #        elif isinstance(editor, QComboBox):
        #            model.setData(index, editor.currentText())
        #        else:
        #            super(ItemDelegate, self).setModelData(editor, model, index)

        # convert entered string to complex, pass the old value as default
        data = self.parent.frmt2cmplx(
            qstr(editor.text()), self.parent.zpk[index.column()][index.row()])
        model.setData(index, data)  # store in QTableWidget
        self.parent.zpk[index.column()][index.row()] = data  # and in self.ba
        qstyle_widget(self.parent.ui.butSave, 'changed')
        self.parent._refresh_table_item(index.row(),
                                        index.column())  # refresh table entry
        self.parent._normalize_gain()  # recalculate gain
Пример #7
0
    def exportHDL(self):
        """
        Synthesize HDL description of filter
        """
        dlg = QFD(self)  # instantiate file dialog object

        file_types = "Verilog (*.v)"
        # needed for overwrite confirmation when name is entered without suffix:
        dlg.setDefaultSuffix('v')
        dlg.setWindowTitle('Export Verilog')
        dlg.setNameFilter(file_types)
        dlg.setDirectory(dirs.save_dir)
        # set mode "save file" instead "open file":
        dlg.setAcceptMode(QFD.AcceptSave)
        dlg.setOption(QFD.DontConfirmOverwrite, False)
        if dlg.exec_() == QFD.Accepted:
            hdl_file = qstr(dlg.selectedFiles()[0])
            # hdl_type = extract_file_ext(qstr(dlg.selectedNameFilter()))[0]

# =============================================================================
#       # static method getSaveFileName_() is simple but unflexible
#         hdl_file, hdl_filter = dlg.getSaveFileName_(
#                 caption="Save Verilog netlist as (this also defines the module name)",
#                 directory=dirs.save_dir, filter=file_types)
#         hdl_file = qstr(hdl_file)
#         if hdl_file != "": # "operation cancelled" returns an empty string
#             # return '.v' or '.vhd' depending on filetype selection:
#             # hdl_type = extract_file_ext(qstr(hdl_filter))[0]
#             # sanitized dir + filename + suffix. The filename suffix is replaced
#             # by `v` later.
#             hdl_file = os.path.normpath(hdl_file) # complete path + file name
# =============================================================================
            hdl_dir_name = os.path.dirname(hdl_file)  # extract the directory path
            if not os.path.isdir(hdl_dir_name):  # create directory if it doesn't exist
                os.mkdir(hdl_dir_name)
            dirs.save_dir = hdl_dir_name  # make this directory the new default / base dir
            hdl_file_name = os.path.splitext(os.path.basename(hdl_file))[0]
            hdl_full_name = os.path.join(hdl_dir_name, hdl_file_name + ".v")
            # remove all non-alphanumeric chars:
            vlog_mod_name = re.sub(r'\W+', '', hdl_file_name).lower()

            logger.info('Creating hdl_file "{0}"\n\twith top level module "{1}"'
                        .format(hdl_full_name, vlog_mod_name))
            try:
                self.update_fxqc_dict()
                self.fx_filt_ui.construct_fixp_filter()
                code = self.fx_filt_ui.to_hdl(name=vlog_mod_name)
                # logger.info(str(code)) # print verilog code to console
                with io.open(hdl_full_name, 'w', encoding="utf8") as f:
                    f.write(str(code))

                logger.info("HDL conversion finished!")
            except (IOError, TypeError) as e:
                logger.warning(e)
Пример #8
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))
Пример #9
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
Пример #10
0
    def exportHDL(self):
        """
        Synthesize HDL description of filter
        """
        if not hasattr(self.fx_wdg_inst, 'construct_fixp_filter'):
            logger.warning('Fixpoint widget has no method "construct_fixp_filter", aborting.')
            return
        
        dlg = QFD(self) # instantiate file dialog object

        file_types = "Verilog (*.v)"

        hdl_file, hdl_filter = dlg.getSaveFileName_(
                caption="Save HDL as", directory=dirs.save_dir,
                filter=file_types)
        hdl_file = qstr(hdl_file)

        if hdl_file != "": # "operation cancelled" returns an empty string
            # return '.v' or '.vhd' depending on filetype selection:
            # hdl_type = extract_file_ext(qstr(hdl_filter))[0]
            # sanitized dir + filename + suffix. The filename suffix is replaced
            # by `v` later.
            hdl_file = os.path.normpath(hdl_file)
            hdl_dir_name = os.path.dirname(hdl_file) # extract the directory path
            if not os.path.isdir(hdl_dir_name): # create directory if it doesn't exist
                os.mkdir(hdl_dir_name)
            dirs.save_dir = hdl_dir_name # make this directory the new default / base dir
            hdl_file_name = os.path.join(hdl_dir_name, 
                                os.path.splitext(os.path.basename(hdl_file))[0]+ ".v")


            logger.info('Creating hdl_file "{0}"'.format(
                        os.path.join(hdl_dir_name, hdl_file_name)))
            try:
                self.update_fxqc_dict()
                self.fx_wdg_inst.construct_fixp_filter()
                code = self.fx_wdg_inst.to_verilog()
                
                logger.info(str(code))
                
                with io.open(hdl_file_name, 'w', encoding="utf8") as f:
                    f.write(str(code))

                logger.info("HDL conversion finished!")
            except (IOError, TypeError) as e:
                logger.warning(e)
Пример #11
0
    def setEditorData(self, editor, index):
        """
        Pass the data to be edited to the editor:
        - retrieve data with full accuracy from self.ba (in float format)
        - requantize data according to settings in fixpoint object
        - represent it in the selected format (int, hex, ...)

        editor: instance of e.g. QLineEdit
        index:  instance of QModelIndex
        """
#        data = qstr(index.data()) # get data from QTableWidget
        data_str = qstr(safe_eval(self.parent.ba[index.column()][index.row()], return_type="auto"))

        if self.parent.myQ.frmt == 'float':
            # floating point format: pass data with full resolution
            editor.setText(data_str)
        else:
            # fixpoint format with base: pass requantized data with required number of places
            editor.setText("{0:>{1}}".format(self.parent.myQ.float2frmt(data_str),
                                               self.parent.myQ.places))
Пример #12
0
    def displayText(self, text, locale):
        """
        Display `text` with selected fixpoint base and number of places

        text:   string / QVariant from QTableWidget to be rendered
        locale: locale for the text

        The instance parameter myQ.ovr_flag is set to +1 or -1 for positive /
        negative overflows, else it is 0.
        """
        data_str = qstr(text) # convert to "normal" string

        if self.parent.myQ.frmt == 'float':
            data = safe_eval(data_str, return_type='auto') # convert to float
            return "{0:.{1}g}".format(data, params['FMT_ba'])

        elif self.parent.myQ.frmt == 'dec' and self.parent.myQ.WF > 0:
            # decimal fixpoint representation with fractional part
            return "{0:.{1}g}".format(self.parent.myQ.float2frmt(data_str),
                                        params['FMT_ba'])
        else:
            return "{0:>{1}}".format(self.parent.myQ.float2frmt(data_str),
                                        self.parent.myQ.places)
Пример #13
0
    def _construct_UI(self, **kwargs):
        """
        Construct widget from quantization dict, individual settings and
        the default dict below """

        # default settings
        dict_ui = {
            'wdg_name': 'ui_w',
            'label': 'WI.WF',
            'lbl_sep': '.',
            'max_led_width': 30,
            'WI': 0,
            'WI_len': 2,
            'tip_WI': 'Number of integer bits',
            'WF': 15,
            'WF_len': 2,
            'tip_WF': 'Number of fractional bits',
            'enabled': True,
            'visible': True,
            'fractional': True,
            'combo_visible': False,
            'combo_items': ['auto', 'full', 'man'],
            'tip_combo': 'Calculate Acc. width.',
            'lock_visible': False,
            'tip_lock': 'Lock input/output quantization.'
        }  #: default values

        if self.q_dict:
            dict_ui.update(self.q_dict)

        for k, v in kwargs.items():
            if k not in dict_ui:
                logger.warning("Unknown key {0}".format(k))
            else:
                dict_ui.update({k: v})

        self.wdg_name = dict_ui['wdg_name']

        if not dict_ui['fractional']:
            dict_ui['WF'] = 0
        self.WI = dict_ui['WI']
        self.WF = dict_ui['WF']
        self.W = int(self.WI + self.WF + 1)
        if self.q_dict:
            self.q_dict.update({'WI': self.WI, 'WF': self.WF, 'W': self.W})
        else:
            self.q_dict = {'WI': self.WI, 'WF': self.WF, 'W': self.W}

        lblW = QLabel(to_html(dict_ui['label'], frmt='bi'), self)

        self.cmbW = QComboBox(self)
        self.cmbW.addItems(dict_ui['combo_items'])
        self.cmbW.setVisible(dict_ui['combo_visible'])
        self.cmbW.setToolTip(dict_ui['tip_combo'])
        self.cmbW.setObjectName("cmbW")

        self.butLock = QPushButton(self)
        self.butLock.setCheckable(True)
        self.butLock.setChecked(False)
        self.butLock.setVisible(dict_ui['lock_visible'])
        self.butLock.setToolTip(dict_ui['tip_lock'])

        self.ledWI = QLineEdit(self)
        self.ledWI.setToolTip(dict_ui['tip_WI'])
        self.ledWI.setMaxLength(dict_ui['WI_len'])  # maximum of 2 digits
        self.ledWI.setFixedWidth(
            dict_ui['max_led_width'])  # width of lineedit in points
        self.ledWI.setObjectName("WI")

        lblDot = QLabel(dict_ui['lbl_sep'], self)
        lblDot.setVisible(dict_ui['fractional'])

        self.ledWF = QLineEdit(self)
        self.ledWF.setToolTip(dict_ui['tip_WF'])
        self.ledWF.setMaxLength(dict_ui['WI_len'])  # maximum of 2 digits
        self.ledWF.setFixedWidth(
            dict_ui['max_led_width'])  # width of lineedit in points
        self.ledWF.setVisible(dict_ui['fractional'])
        self.ledWF.setObjectName("WF")

        layH = QHBoxLayout()
        layH.addWidget(lblW)
        layH.addStretch()
        layH.addWidget(self.cmbW)
        layH.addWidget(self.butLock)
        layH.addWidget(self.ledWI)
        layH.addWidget(lblDot)
        layH.addWidget(self.ledWF)
        layH.setContentsMargins(0, 0, 0, 0)

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

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

        self.setLayout(layVMain)

        # ----------------------------------------------------------------------
        # INITIAL SETTINGS
        # ----------------------------------------------------------------------
        self.ledWI.setText(qstr(dict_ui['WI']))
        self.ledWF.setText(qstr(dict_ui['WF']))

        frmMain.setEnabled(dict_ui['enabled'])
        frmMain.setVisible(dict_ui['visible'])

        # ----------------------------------------------------------------------
        # LOCAL SIGNALS & SLOTs
        # ----------------------------------------------------------------------
        self.ledWI.editingFinished.connect(self.ui2dict)
        self.ledWF.editingFinished.connect(self.ui2dict)
        self.butLock.clicked.connect(self.butLock_clicked)
        self.cmbW.currentIndexChanged.connect(self.ui2dict)

        # initialize button icon
        self.butLock_clicked(self.butLock.isChecked())
Пример #14
0
 def text(self, item):
     """
     Return item text as string transformed by self.displayText()
     """
     # return qstr(item.text()) # convert to "normal" string
     return  qstr(self.displayText(item.text(), QtCore.QLocale()))
Пример #15
0
 def displayText(self, text, locale):
     return "{:.{n_digits}g}".format(safe_eval(qstr(text),
                                               return_type='cmplx'),
                                     n_digits=params['FMT_pz'])