Пример #1
0
    def _get_params(self, fil_dict):
        """
        Translate parameters from the passed dictionary to instance
        parameters, scaling / transforming them if needed.
        """

        #in scipy, Bessel filter order is limited to 25
        self.N = fil_dict['N'] if fil_dict['N'] < 26 else 25
        if (fil_dict['N'] != self.N):
            logger.warn("Bessel orders limited to 25 in scipy")
            fil_dict['N'] = self.N

        self.F_PB = fil_dict['F_PB'] * 2  # Frequencies are normalized to f_Nyq
        self.F_SB = fil_dict['F_SB'] * 2
        self.F_PB2 = fil_dict['F_PB2'] * 2
        self.F_SB2 = fil_dict['F_SB2'] * 2
        self.F_PBC = None
        self.F_C = fil_dict['F_C'] * 2
        self.F_C2 = fil_dict['F_C2'] * 2

        self.A_PB = lin2unit(fil_dict['A_PB'], 'IIR', 'A_PB', unit='dB')
        self.A_SB = lin2unit(fil_dict['A_SB'], 'IIR', 'A_SB', unit='dB')

        # bessel filter routines support only one amplitude spec for
        # pass- and stop band each
        if str(fil_dict['rt']) == 'BS':
            fil_dict['A_PB2'] = fil_dict['A_PB']
        elif str(fil_dict['rt']) == 'BP':
            fil_dict['A_SB2'] = fil_dict['A_SB']
Пример #2
0
    def _get_params(self,fil_dict):
        """
        Translate parameters from the passed dictionary to instance
        parameters, scaling / transforming them if needed.
        """
        self.analog = False # set to True for analog filters
        self.N     = fil_dict['N']
        # Frequencies are normalized to f_Nyq = f_S/2, ripple specs are in dB
        self.F_PB  = fil_dict['F_PB'] * 2
        self.F_SB  = fil_dict['F_SB'] * 2
        self.F_C   = fil_dict['F_C'] * 2
        self.F_PB2 = fil_dict['F_PB2'] * 2
        self.F_SB2 = fil_dict['F_SB2'] * 2
        self.F_C2   = fil_dict['F_C2'] * 2
        self.F_PBC = None

        self.A_PB = lin2unit(fil_dict['A_PB'], 'IIR', 'A_PB', unit='dB')
        self.A_SB = lin2unit(fil_dict['A_SB'], 'IIR', 'A_SB', unit='dB')

        # butter filter routines support only one amplitude spec for
        # pass- and stop band each
        if str(fil_dict['rt']) == 'BS':
            fil_dict['A_PB2'] = fil_dict['A_PB']
        elif str(fil_dict['rt']) == 'BP':
            fil_dict['A_SB2'] = fil_dict['A_SB']
Пример #3
0
    def _get_params(self,fil_dict):
        """
        Translate parameters from the passed dictionary to instance
        parameters, scaling / transforming them if needed.
        """
        self.analog = False # set to True for analog filters
        self.N     = fil_dict['N']
        # Frequencies are normalized to f_Nyq = f_S/2, ripple specs are in dB
        self.F_PB  = fil_dict['F_PB'] * 2
        self.F_SB  = fil_dict['F_SB'] * 2
        self.F_C   = fil_dict['F_C'] * 2
        self.F_PB2 = fil_dict['F_PB2'] * 2
        self.F_SB2 = fil_dict['F_SB2'] * 2
        self.F_C2   = fil_dict['F_C2'] * 2
        self.F_PBC = None

        self.A_PB = lin2unit(fil_dict['A_PB'], 'IIR', 'A_PB', unit='dB')
        self.A_SB = lin2unit(fil_dict['A_SB'], 'IIR', 'A_SB', unit='dB')

        # butter filter routines support only one amplitude spec for
        # pass- and stop band each
        if str(fil_dict['rt']) == 'BS':
            fil_dict['A_PB2'] = fil_dict['A_PB']
        elif str(fil_dict['rt']) == 'BP':
            fil_dict['A_SB2'] = fil_dict['A_SB']
Пример #4
0
    def _get_params(self, fil_dict):
        """
        Translate parameters from the passed dictionary to instance
        parameters, scaling / transforming them if needed.
        """

        #in scipy, Bessel filter order is limited to 25
        self.N = fil_dict['N'] if fil_dict['N'] < 26 else 25
        if (fil_dict['N'] != self.N):
            logger.warn("Bessel orders limited to 25 in scipy")
            fil_dict['N'] = self.N

        self.F_PB  = fil_dict['F_PB'] * 2 # Frequencies are normalized to f_Nyq
        self.F_SB  = fil_dict['F_SB'] * 2
        self.F_PB2 = fil_dict['F_PB2'] * 2
        self.F_SB2 = fil_dict['F_SB2'] * 2
        self.F_PBC = None
        self.F_C   = fil_dict['F_C'] * 2
        self.F_C2  = fil_dict['F_C2'] * 2

        self.A_PB = lin2unit(fil_dict['A_PB'], 'IIR', 'A_PB', unit='dB')
        self.A_SB = lin2unit(fil_dict['A_SB'], 'IIR', 'A_SB', unit='dB')

        # bessel filter routines support only one amplitude spec for
        # pass- and stop band each
        if str(fil_dict['rt']) == 'BS':
            fil_dict['A_PB2'] = fil_dict['A_PB']
        elif str(fil_dict['rt']) == 'BP':
            fil_dict['A_SB2'] = fil_dict['A_SB']
Пример #5
0
    def load_dict(self):
        """
        Reload and reformat the amplitude textfields from filter dict when a new filter
        design algorithm is selected or when the user has changed the unit  (V / W / dB):

        - Reload amplitude entries from filter dictionary and convert to selected to reflect changed settings
          unit.
        - Update the lineedit fields, rounded to specified format.
        """
        unit = fb.fil[0]['amp_specs_unit']

        filt_type = fb.fil[0]['ft']

        for i in range(len(self.qlineedit)):
            amp_label = str(self.qlineedit[i].objectName())
            amp_value = lin2unit(fb.fil[0][amp_label],
                                 filt_type,
                                 amp_label,
                                 unit=unit)

            if not self.qlineedit[i].hasFocus():
                # widget has no focus, round the display
                self.qlineedit[i].setText(params['FMT'].format(amp_value))
            else:
                # widget has focus, show full precision
                self.qlineedit[i].setText(str(amp_value))
Пример #6
0
    def _get_params(self, fil_dict):
        """
        Translate parameters from the passed dictionary to instance
        parameters, scaling / transforming them if needed.
        For zero phase filter, we take square root of amplitude specs
        since we later square filter.  Define design around smallest amp spec
        """
        # Frequencies are normalized to f_Nyq = f_S/2, ripple specs are in dB
        self.analog = False  # set to True for analog filters
        self.manual = False  # default is normal design
        self.N = int(fil_dict['N'])

        # force N to be even
        if (self.N % 2) == 1:
            self.N += 1
        self.F_PB = fil_dict['F_PB'] * 2
        self.F_SB = fil_dict['F_SB'] * 2
        self.F_PB2 = fil_dict['F_PB2'] * 2
        self.F_SB2 = fil_dict['F_SB2'] * 2
        self.F_PBC = None

        # find smallest spec'd linear value and rewrite dictionary
        ampPB = fil_dict['A_PB']
        ampSB = fil_dict['A_SB']

        # take square roots of amp specs so resulting squared
        # filter will meet specifications
        if (ampPB < ampSB):
            ampSB = np.sqrt(ampPB)
            ampPB = np.sqrt(1 + ampPB) - 1
        else:
            ampPB = np.sqrt(1 + ampSB) - 1
            ampSB = np.sqrt(ampSB)
        self.A_PB = lin2unit(ampPB, 'IIR', 'A_PB', unit='dB')
        self.A_SB = lin2unit(ampSB, 'IIR', 'A_SB', unit='dB')
        #logger.warning("design with "+str(self.A_PB)+","+str(self.A_SB))

        # ellip filter routines support only one amplitude spec for
        # pass- and stop band each
        if str(fil_dict['rt']) == 'BS':
            fil_dict['A_PB2'] = self.A_PB
        elif str(fil_dict['rt']) == 'BP':
            fil_dict['A_SB2'] = self.A_SB
Пример #7
0
    def _get_params(self, fil_dict):
        """
        Translate parameters from the passed dictionary to instance
        parameters, scaling / transforming them if needed.
        For zero phase filter, we take square root of amplitude specs
        since we later square filter.  Define design around smallest amp spec
        """
        # Frequencies are normalized to f_Nyq = f_S/2, ripple specs are in dB
        self.analog = False # set to True for analog filters
        self.manual = False # default is normal design
        self.N     = int(fil_dict['N'])

        # force N to be even
        if (self.N % 2) == 1:
            self.N += 1
        self.F_PB  = fil_dict['F_PB'] * 2
        self.F_SB  = fil_dict['F_SB'] * 2
        self.F_PB2 = fil_dict['F_PB2'] * 2
        self.F_SB2 = fil_dict['F_SB2'] * 2
        self.F_PBC = None

        # find smallest spec'd linear value and rewrite dictionary
        ampPB = fil_dict['A_PB']
        ampSB = fil_dict['A_SB']

        # take square roots of amp specs so resulting squared
        # filter will meet specifications
        if (ampPB < ampSB):
            ampSB = np.sqrt(ampPB)
            ampPB = np.sqrt(1+ampPB)-1
        else:
            ampPB = np.sqrt(1+ampSB)-1
            ampSB = np.sqrt(ampSB)
        self.A_PB = lin2unit(ampPB, 'IIR', 'A_PB', unit='dB')
        self.A_SB = lin2unit(ampSB, 'IIR', 'A_SB', unit='dB')
        #logger.warning("design with "+str(self.A_PB)+","+str(self.A_SB))

        # ellip filter routines support only one amplitude spec for
        # pass- and stop band each
        if str(fil_dict['rt']) == 'BS':
            fil_dict['A_PB2'] = self.A_PB
        elif str(fil_dict['rt']) == 'BP':
            fil_dict['A_SB2'] = self.A_SB
Пример #8
0
    def load_dict(self):
        """
        Reload and reformat the amplitude textfields from filter dict when a new filter
        design algorithm is selected or when the user has changed the unit  (V / W / dB):

        - Reload amplitude entries from filter dictionary and convert to selected to reflect changed settings
          unit.
        - Update the lineedit fields, rounded to specified format.
        """
        unit = fb.fil[0]['amp_specs_unit']

        filt_type = fb.fil[0]['ft']

        for i in range(len(self.qlineedit)):
            amp_label = str(self.qlineedit[i].objectName())
            amp_value = lin2unit(fb.fil[0][amp_label], filt_type, amp_label, unit = unit)

            if not self.qlineedit[i].hasFocus():
                # widget has no focus, round the display
                self.qlineedit[i].setText(params['FMT'].format(amp_value))
            else:
                # widget has focus, show full precision
                self.qlineedit[i].setText(str(amp_value))
Пример #9
0
    def _show_filt_perf(self):
        """
        Print filter properties in a table at frequencies of interest. When
        specs are violated, colour the table entry in red.
        """
        
        antiC = False

        def _find_min_max(self, f_start, f_stop, unit = 'dB'):
            """
            Find minimum and maximum magnitude and the corresponding frequencies
            for the filter defined in the filter dict in a given frequency band
            [f_start, f_stop].
            """
            w = np.linspace(f_start, f_stop, params['N_FFT'])*2*np.pi
            [w, H] = sig.freqz(bb, aa, worN = w)

            # add antiCausals if we have them
            if (antiC):
               #
               # Evaluate transfer function of anticausal half on the same freq grid.
               #
               wa, ha = sig.freqz(bbA, aaA, worN = w)
               ha = ha.conjugate()
               #
               # Total transfer function is the product
               #
               H = H*ha

            f = w / (2.0 * pi) # frequency normalized to f_S
            H_abs = abs(H)
            H_max = max(H_abs)
            H_min = min(H_abs)
            F_max = f[np.argmax(H_abs)] # find the frequency where H_abs 
            F_min = f[np.argmin(H_abs)] # becomes max resp. min
            if unit == 'dB':
                H_max = 20*log10(H_max)
                H_min = 20*log10(H_min)
            return F_min, H_min, F_max, H_max
        #------------------------------------------------------------------

        self.tblFiltPerf.setVisible(self.chkFiltPerf.isChecked())
        if self.chkFiltPerf.isChecked():

            bb = fb.fil[0]['ba'][0]
            aa = fb.fil[0]['ba'][1]

            #'rpk' means nonCausal filter
            if 'rpk' in fb.fil[0]:
                antiC = True
                bbA = fb.fil[0]['baA'][0]
                aaA = fb.fil[0]['baA'][1]
                bbA = bbA.conjugate()
                aaA = aaA.conjugate()

            f_S  = fb.fil[0]['f_S']
    
            f_lbls = []
            f_vals = []
            a_lbls = []
            a_targs = []
            a_targs_dB = []
            a_test = []
            ft = fb.fil[0]['ft'] # get filter type ('IIR', 'FIR')
            unit = fb.fil[0]['amp_specs_unit']
            unit = 'dB' # fix this for the moment
            # construct pairs of corner frequency and corresponding amplitude
            # labels in ascending frequency for each response type        
            if fb.fil[0]['rt'] in {'LP', 'HP', 'BP', 'BS', 'HIL'}:
                if fb.fil[0]['rt'] == 'LP':
                    f_lbls = ['F_PB', 'F_SB'] 
                    a_lbls = ['A_PB', 'A_SB']
                elif fb.fil[0]['rt'] == 'HP':
                    f_lbls = ['F_SB', 'F_PB']
                    a_lbls = ['A_SB', 'A_PB']
                elif fb.fil[0]['rt'] == 'BP':
                    f_lbls = ['F_SB', 'F_PB', 'F_PB2', 'F_SB2']
                    a_lbls = ['A_SB', 'A_PB', 'A_PB', 'A_SB2']
                elif fb.fil[0]['rt'] == 'BS':
                    f_lbls = ['F_PB', 'F_SB', 'F_SB2', 'F_PB2']
                    a_lbls = ['A_PB', 'A_SB', 'A_SB', 'A_PB2']
                elif fb.fil[0]['rt'] == 'HIL':
                    f_lbls = ['F_PB', 'F_PB2']
                    a_lbls = ['A_PB', 'A_PB']


            # Try to get lists of frequency / amplitude specs from the filter dict
            # that correspond to the f_lbls / a_lbls pairs defined above
            # When one of the labels doesn't exist in the filter dict, delete 
            # all corresponding amplitude and frequency entries.
                err = [False] * len(f_lbls) # initialize error list  
                f_vals = []
                a_targs = []
                for i in range(len(f_lbls)):
                    try:
                        f = fb.fil[0][f_lbls[i]]
                        f_vals.append(f)
                    except KeyError as e:
                        f_vals.append('')
                        err[i] = True
                        logger.debug(e)
                    try:
                        a = fb.fil[0][a_lbls[i]]
                        a_dB = lin2unit(fb.fil[0][a_lbls[i]], ft, a_lbls[i], unit)
                        a_targs.append(a)
                        a_targs_dB.append(a_dB)
                    except KeyError as e:
                        a_targs.append('')
                        a_targs_dB.append('')
                        err[i] = True
                        logger.debug(e)

                for i in range(len(f_lbls)):
                    if err[i]:
                        del f_lbls[i]
                        del f_vals[i]
                        del a_lbls[i]
                        del a_targs[i]
                        del a_targs_dB[i]
    
                f_vals = np.asarray(f_vals) # convert to numpy array
    
                logger.debug("F_test_labels = %s" %f_lbls)
                               
                # Calculate frequency response at test frequencies
                [w_test, a_test] = sig.freqz(bb, aa, 2.0 * pi * f_vals.astype(np.float))
                # add antiCausals if we have them
                if (antiC):
                   wa, ha = sig.freqz(bbA, aaA, 2.0 * pi * f_vals.astype(np.float))
                   ha = ha.conjugate()
                   a_test = a_test*ha


            (F_min, H_min, F_max, H_max) = _find_min_max(self, 0, 1, unit = 'V')    
            # append frequencies and values for min. and max. filter reponse to 
            # test vector
            
            f_lbls += ['Min.','Max.']
            # QTableView does not support direct formatting, use QLabel

            f_vals = np.append(f_vals, [F_min, F_max])
            a_targs = np.append(a_targs, [np.nan, np.nan])
            a_targs_dB = np.append(a_targs_dB, [np.nan, np.nan])
            a_test = np.append(a_test, [H_min, H_max])
            # calculate response of test frequencies in dB
            a_test_dB = -20*log10(abs(a_test))
            
            ft = fb.fil[0]['ft'] # get filter type ('IIR', 'FIR') for dB <-> lin conversion
#            unit = fb.fil[0]['amp_specs_unit']
            unit = 'dB' # make this fixed for the moment
    
            # build a list with the corresponding target specs:
            a_targs_pass = []
            eps = 1e-3
            for i in range(len(f_lbls)):
                if 'PB' in f_lbls[i]:
                    a_targs_pass.append((a_test_dB[i] - a_targs_dB[i])< eps)
                    a_test[i] = 1 - abs(a_test[i])
                elif 'SB' in f_lbls[i]:
                    a_targs_pass.append(a_test_dB[i] >= a_targs_dB[i]) 
                else:
                    a_targs_pass.append(True)
    
            self.targs_spec_passed = np.all(a_targs_pass)
            
            logger.debug("H_targ = {0}\n" 
                "H_test = {1}\n"
                "H_test_dB = {2}\n"
                "F_test = {3}\n" 
                "H_targ_pass = {4}\n" 
                "passed: {5}\n".format(a_targs,  a_test,  a_test_dB, f_vals, a_targs_pass,self.targs_spec_passed)) 
    
            self.tblFiltPerf.setRowCount(len(a_test)) # number of table rows
            self.tblFiltPerf.setColumnCount(5) # number of table columns
    
            self.tblFiltPerf.setHorizontalHeaderLabels([
            'f/{0:s}'.format(fb.fil[0]['freq_specs_unit']),'Spec\n(dB)', '|H(f)|\n(dB)', 'Spec', '|H(f)|'] )
            self.tblFiltPerf.setVerticalHeaderLabels(f_lbls)
            for row in range(len(a_test)):
                self.tblFiltPerf.setItem(row,0,QTableWidgetItem(str('{0:.4g}'.format(f_vals[row]*f_S))))
                self.tblFiltPerf.setItem(row,1,QTableWidgetItem(str('%2.3g'%(-a_targs_dB[row]))))
                self.tblFiltPerf.setItem(row,2,QTableWidgetItem(str('%2.3f'%(-a_test_dB[row]))))
                if a_targs[row] < 0.01:
                    self.tblFiltPerf.setItem(row,3,QTableWidgetItem(str('%.3e'%(a_targs[row]))))
                else:
                    self.tblFiltPerf.setItem(row,3,QTableWidgetItem(str('%2.4f'%(a_targs[row]))))
                if a_test[row] < 0.01:    
                    self.tblFiltPerf.setItem(row,4,QTableWidgetItem(str('%.3e'%(abs(a_test[row])))))
                else:
                    self.tblFiltPerf.setItem(row,4,QTableWidgetItem(str('%.4f'%(abs(a_test[row])))))
                    
                if not a_targs_pass[row]:
                    self.tblFiltPerf.item(row,1).setBackground(QtGui.QColor('red'))
                    self.tblFiltPerf.item(row,3).setBackground(QtGui.QColor('red'))
    
            self.tblFiltPerf.resizeColumnsToContents()
            self.tblFiltPerf.resizeRowsToContents()
Пример #10
0
    def _show_filt_perf(self):
        """
        Print filter properties in a table at frequencies of interest. When
        specs are violated, colour the table entry in red.
        """

        antiC = False

        def _find_min_max(self, f_start, f_stop, unit='dB'):
            """
            Find minimum and maximum magnitude and the corresponding frequencies
            for the filter defined in the filter dict in a given frequency band
            [f_start, f_stop].
            """
            w = np.linspace(f_start, f_stop, params['N_FFT']) * 2 * np.pi
            [w, H] = sig.freqz(bb, aa, worN=w)

            # add antiCausals if we have them
            if (antiC):
                #
                # Evaluate transfer function of anticausal half on the same freq grid.
                #
                wa, ha = sig.freqz(bbA, aaA, worN=w)
                ha = ha.conjugate()
                #
                # Total transfer function is the product
                #
                H = H * ha

            f = w / (2.0 * pi)  # frequency normalized to f_S
            H_abs = abs(H)
            H_max = max(H_abs)
            H_min = min(H_abs)
            F_max = f[np.argmax(H_abs)]  # find the frequency where H_abs
            F_min = f[np.argmin(H_abs)]  # becomes max resp. min
            if unit == 'dB':
                H_max = 20 * log10(H_max)
                H_min = 20 * log10(H_min)
            return F_min, H_min, F_max, H_max

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

        self.tblFiltPerf.setVisible(self.chkFiltPerf.isChecked())
        if self.chkFiltPerf.isChecked():

            bb = fb.fil[0]['ba'][0]
            aa = fb.fil[0]['ba'][1]

            #'rpk' means nonCausal filter
            if 'rpk' in fb.fil[0]:
                antiC = True
                bbA = fb.fil[0]['baA'][0]
                aaA = fb.fil[0]['baA'][1]
                bbA = bbA.conjugate()
                aaA = aaA.conjugate()

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

            f_lbls = []
            f_vals = []
            a_lbls = []
            a_targs = []
            a_targs_dB = []
            a_test = []
            ft = fb.fil[0]['ft']  # get filter type ('IIR', 'FIR')
            unit = fb.fil[0]['amp_specs_unit']
            unit = 'dB'  # fix this for the moment
            # construct pairs of corner frequency and corresponding amplitude
            # labels in ascending frequency for each response type
            if fb.fil[0]['rt'] in {'LP', 'HP', 'BP', 'BS', 'HIL'}:
                if fb.fil[0]['rt'] == 'LP':
                    f_lbls = ['F_PB', 'F_SB']
                    a_lbls = ['A_PB', 'A_SB']
                elif fb.fil[0]['rt'] == 'HP':
                    f_lbls = ['F_SB', 'F_PB']
                    a_lbls = ['A_SB', 'A_PB']
                elif fb.fil[0]['rt'] == 'BP':
                    f_lbls = ['F_SB', 'F_PB', 'F_PB2', 'F_SB2']
                    a_lbls = ['A_SB', 'A_PB', 'A_PB', 'A_SB2']
                elif fb.fil[0]['rt'] == 'BS':
                    f_lbls = ['F_PB', 'F_SB', 'F_SB2', 'F_PB2']
                    a_lbls = ['A_PB', 'A_SB', 'A_SB', 'A_PB2']
                elif fb.fil[0]['rt'] == 'HIL':
                    f_lbls = ['F_PB', 'F_PB2']
                    a_lbls = ['A_PB', 'A_PB']

            # Try to get lists of frequency / amplitude specs from the filter dict
            # that correspond to the f_lbls / a_lbls pairs defined above
            # When one of the labels doesn't exist in the filter dict, delete
            # all corresponding amplitude and frequency entries.
                err = [False] * len(f_lbls)  # initialize error list
                f_vals = []
                a_targs = []
                for i in range(len(f_lbls)):
                    try:
                        f = fb.fil[0][f_lbls[i]]
                        f_vals.append(f)
                    except KeyError as e:
                        f_vals.append('')
                        err[i] = True
                        logger.debug(e)
                    try:
                        a = fb.fil[0][a_lbls[i]]
                        a_dB = lin2unit(fb.fil[0][a_lbls[i]], ft, a_lbls[i],
                                        unit)
                        a_targs.append(a)
                        a_targs_dB.append(a_dB)
                    except KeyError as e:
                        a_targs.append('')
                        a_targs_dB.append('')
                        err[i] = True
                        logger.debug(e)

                for i in range(len(f_lbls)):
                    if err[i]:
                        del f_lbls[i]
                        del f_vals[i]
                        del a_lbls[i]
                        del a_targs[i]
                        del a_targs_dB[i]

                f_vals = np.asarray(f_vals)  # convert to numpy array

                logger.debug("F_test_labels = %s" % f_lbls)

                # Calculate frequency response at test frequencies
                [w_test,
                 a_test] = sig.freqz(bb, aa,
                                     2.0 * pi * f_vals.astype(np.float))
                # add antiCausals if we have them
                if (antiC):
                    wa, ha = sig.freqz(bbA, aaA,
                                       2.0 * pi * f_vals.astype(np.float))
                    ha = ha.conjugate()
                    a_test = a_test * ha

            (F_min, H_min, F_max, H_max) = _find_min_max(self, 0, 1, unit='V')
            # append frequencies and values for min. and max. filter reponse to
            # test vector

            f_lbls += ['Min.', 'Max.']
            # QTableView does not support direct formatting, use QLabel

            f_vals = np.append(f_vals, [F_min, F_max])
            a_targs = np.append(a_targs, [np.nan, np.nan])
            a_targs_dB = np.append(a_targs_dB, [np.nan, np.nan])
            a_test = np.append(a_test, [H_min, H_max])
            # calculate response of test frequencies in dB
            a_test_dB = -20 * log10(abs(a_test))

            ft = fb.fil[0][
                'ft']  # get filter type ('IIR', 'FIR') for dB <-> lin conversion
            #            unit = fb.fil[0]['amp_specs_unit']
            unit = 'dB'  # make this fixed for the moment

            # build a list with the corresponding target specs:
            a_targs_pass = []
            eps = 1e-3
            for i in range(len(f_lbls)):
                if 'PB' in f_lbls[i]:
                    a_targs_pass.append((a_test_dB[i] - a_targs_dB[i]) < eps)
                    a_test[i] = 1 - abs(a_test[i])
                elif 'SB' in f_lbls[i]:
                    a_targs_pass.append(a_test_dB[i] >= a_targs_dB[i])
                else:
                    a_targs_pass.append(True)

            self.targs_spec_passed = np.all(a_targs_pass)

            logger.debug("H_targ = {0}\n"
                         "H_test = {1}\n"
                         "H_test_dB = {2}\n"
                         "F_test = {3}\n"
                         "H_targ_pass = {4}\n"
                         "passed: {5}\n".format(a_targs, a_test, a_test_dB,
                                                f_vals, a_targs_pass,
                                                self.targs_spec_passed))

            self.tblFiltPerf.setRowCount(len(a_test))  # number of table rows
            self.tblFiltPerf.setColumnCount(5)  # number of table columns

            self.tblFiltPerf.setHorizontalHeaderLabels([
                'f/{0:s}'.format(fb.fil[0]['freq_specs_unit']), 'Spec\n(dB)',
                '|H(f)|\n(dB)', 'Spec', '|H(f)|'
            ])
            self.tblFiltPerf.setVerticalHeaderLabels(f_lbls)
            for row in range(len(a_test)):
                self.tblFiltPerf.setItem(
                    row, 0,
                    QTableWidgetItem(str('{0:.4g}'.format(f_vals[row] * f_S))))
                self.tblFiltPerf.setItem(
                    row, 1,
                    QTableWidgetItem(str('%2.3g' % (-a_targs_dB[row]))))
                self.tblFiltPerf.setItem(
                    row, 2, QTableWidgetItem(str('%2.3f' % (-a_test_dB[row]))))
                if a_targs[row] < 0.01:
                    self.tblFiltPerf.setItem(
                        row, 3, QTableWidgetItem(str('%.3e' % (a_targs[row]))))
                else:
                    self.tblFiltPerf.setItem(
                        row, 3,
                        QTableWidgetItem(str('%2.4f' % (a_targs[row]))))
                if a_test[row] < 0.01:
                    self.tblFiltPerf.setItem(
                        row, 4,
                        QTableWidgetItem(str('%.3e' % (abs(a_test[row])))))
                else:
                    self.tblFiltPerf.setItem(
                        row, 4,
                        QTableWidgetItem(str('%.4f' % (abs(a_test[row])))))

                if not a_targs_pass[row]:
                    self.tblFiltPerf.item(row,
                                          1).setBackground(QtGui.QColor('red'))
                    self.tblFiltPerf.item(row,
                                          3).setBackground(QtGui.QColor('red'))

            self.tblFiltPerf.resizeColumnsToContents()
            self.tblFiltPerf.resizeRowsToContents()
Пример #11
0
    def _show_filt_perf(self):
        """
        Print filter properties in a table at frequencies of interest. When
        specs are violated, colour the table entry in red.
        """
        self.tblFiltPerf.setVisible(self.chkFiltPerf.isChecked())

        bb = fb.fil[0]['ba'][0]
        aa = fb.fil[0]['ba'][1]

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

        # Build a list with all frequency related labels:
        #--------------------------------------------------------------------
        # First, extract the dicts for min / man filter order of the selected
        # design method from filter tree:
        fil_dict = fb.fil_tree[fb.fil[0]['rt']][fb.fil[0]['ft']][fb.fil[0]
                                                                 ['fc']]
        # Now, extract the parameter lists (key 'par'), yielding a nested list:
        fil_list = [fil_dict[k]['par'] for k in fil_dict.keys()]
        # Finally, flatten the list of lists and convert it into a set to
        # eliminate double entries:
        fil_set = set([item for sublist in fil_list for item in sublist])
        # extract all labels starting with 'F':
        F_test_lbls = [lbl for lbl in fil_set if lbl[0] == 'F']
        # construct a list of lists [frequency, label], sorted by frequency:
        F_test = sorted([[fb.fil[0][lbl] * f_S, lbl] for lbl in F_test_lbls])

        # construct a list of lists consisting of [label, frequency]:
        # F_test = [[lbl, fb.fil[0][lbl]*f_S] for lbl in F_test_lbls]
        ## sort list of tuples using the LAST element of the tuple (= frequency)
        # F_test = sorted(F_test, key=lambda t: t[::-1])

        logger.debug("input_info.showFiltPerf\nF_test = %s" % F_test)

        # Vector with test frequencies of the labels above
        F_test_vals = np.array([item[0] for item in F_test]) / f_S
        F_test_lbls = [item[1] for item in F_test]

        # Calculate frequency response at test frequencies and over the whole range:
        [w_test, H_test] = sig.freqz(bb, aa, F_test_vals * 2.0 * pi)
        [w, H] = sig.freqz(bb, aa)

        f = w / (2.0 * pi)  # frequency normalized to f_S
        H_abs = abs(H)
        H_max = max(H_abs)
        H_max_dB = 20 * log10(H_max)
        F_max = f[np.argmax(H_abs)]
        #
        H_min = min(H_abs)
        H_min_dB = 20 * log10(H_min)
        F_min = f[np.argmin(H_abs)]

        #        f = f * f_S

        F_test_lbls += ['Min.', 'Max.']
        F_test_vals = np.append(F_test_vals, [F_min, F_max])
        H_test = np.append(H_test, [H_min, H_max])
        # calculate response of test frequencies and round to 5 digits to
        # suppress fails due to numerical inaccuracies:
        H_test_dB = np.round(-20 * log10(abs(H_test)), 5)

        # build a list with the corresponding target specs:
        H_targ = []
        H_targ_pass = []

        ft = fb.fil[0]['ft']
        unit = fb.fil[0]['amp_specs_unit']
        unit = 'dB'  # fix this for the moment
        for i in range(len(F_test_lbls)):
            lbl = F_test_lbls[i]
            if lbl == 'F_PB':
                H_targ.append(lin2unit(fb.fil[0]['A_PB'], ft, 'A_PB', unit))
                H_targ_pass.append(H_test_dB[i] <= H_targ[i])
            elif lbl == 'F_SB':
                H_targ.append(lin2unit(fb.fil[0]['A_SB'], ft, 'A_SB', unit))
                H_targ_pass.append(H_test_dB[i] >= H_targ[i])
            elif lbl == 'F_PB2':
                H_targ.append(lin2unit(fb.fil[0]['A_PB2'], ft, 'A_PB2', unit))
                H_targ_pass.append(H_test_dB[i] <= H_targ[i])
            elif lbl == 'F_SB2':
                H_targ.append(lin2unit(fb.fil[0]['A_SB2'], ft, 'A_SB2', unit))
                H_targ_pass.append(H_test_dB[i] >= H_targ[i])
            else:
                H_targ.append(np.nan)
                H_targ_pass.append(True)

        self.targ_spec_passed = np.all(H_targ_pass)
        #
        logger.debug("H_targ = %s\n", H_targ, "H_test = %s\n", H_test,
                     "H_test_dB = %s\n", H_test_dB, "F_test = %s\n",
                     F_test_vals, "H_targ_pass = %s\n", H_targ_pass,
                     "passed: %s\n", self.targ_spec_passed)

        #        min_dB = np.floor(max(PLT_min_dB, H_min_dB) / 10) * 10

        self.tblFiltPerf.setRowCount(len(H_test))
        self.target_spec_passed = False

        self.tblFiltPerf.setHorizontalHeaderLabels([
            'f/{0:s}'.format(fb.fil[0]['freq_specs_unit']), '|H(f)|',
            '|H(f)| (dB)', 'Spec'
        ])
        self.tblFiltPerf.setVerticalHeaderLabels(F_test_lbls)
        for row in range(len(H_test)):
            #            self.tblFiltPerf.setItem(row,0,QtGui.QTableWidgetItem(F_test_lbls[row]))
            self.tblFiltPerf.setItem(
                row, 0,
                QtGui.QTableWidgetItem(
                    str('{0:.4g}'.format(F_test_vals[row] * f_S))))
            self.tblFiltPerf.setItem(
                row, 1,
                QtGui.QTableWidgetItem(str('%.4g' % (abs(H_test[row])))))
            self.tblFiltPerf.setItem(
                row, 2,
                QtGui.QTableWidgetItem(str('%2.3f' % (H_test_dB[row]))))
            if not H_targ_pass[row]:
                self.tblFiltPerf.item(row,
                                      1).setBackgroundColor(Qt.QColor('red'))
                self.tblFiltPerf.item(row,
                                      2).setBackgroundColor(Qt.QColor('red'))
            self.tblFiltPerf.setItem(
                row, 3, QtGui.QTableWidgetItem(str('%2.3f' % (H_targ[row]))))

    #    self.tblFiltPerf.item(1,1).setBackgroundColor(Qt.QColor('red'))
        self.tblFiltPerf.resizeColumnsToContents()
        self.tblFiltPerf.resizeRowsToContents()
Пример #12
0
    def _show_filt_perf(self):
        """
        Print filter properties in a table at frequencies of interest. When
        specs are violated, colour the table entry in red.
        """
        def _find_min_max(self, f_start, f_stop, unit='dB'):
            """
            Find minimum and maximum magnitude and the corresponding frequencies
            for the filter defined in the filter dict in a given frequency band
            [f_start, f_stop].
            """
            w = np.linspace(f_start, f_stop, 2048) * 2 * np.pi
            [w, H] = sig.freqz(bb, aa)
            f = w / (2.0 * pi)  # frequency normalized to f_S
            H_abs = abs(H)
            H_max = max(H_abs)
            H_min = min(H_abs)
            F_max = f[np.argmax(H_abs)]  # find the frequency where H_abs
            F_min = f[np.argmin(H_abs)]  # becomes max resp. min
            if unit == 'dB':
                H_max = 20 * log10(H_max)
                H_min = 20 * log10(H_min)
            return F_min, H_min, F_max, H_max

        self.tblFiltPerf.setVisible(self.chkFiltPerf.isChecked())
        if self.chkFiltPerf.isChecked():

            bb = fb.fil[0]['ba'][0]
            aa = fb.fil[0]['ba'][1]

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

            # Build a list with all frequency related labels:
            #--------------------------------------------------------------------
            # First, extract the dicts for min / man filter order of the selected
            # filter class from filter tree:
            fil_dict = fb.fil_tree[fb.fil[0]['rt']][fb.fil[0]['ft']][fb.fil[0]
                                                                     ['fc']]
            # Now, extract the parameter lists (key 'par'), yielding a nested list:
            fil_list = [fil_dict[k]['par'] for k in fil_dict.keys()]
            # Finally, flatten the list of lists and convert it into a set to
            # eliminate double entries:
            fil_set = set([item for sublist in fil_list for item in sublist])
            # extract all labels starting with 'F':
            #        F_test_lbls = [lbl for lbl in fil_set if lbl[0] == 'F']
            # construct a list of lists [frequency, label], sorted by frequency:
            #        F_test = sorted([[fb.fil[0][lbl]*f_S, lbl] for lbl in F_test_lbls])

            # construct a list of lists consisting of [label, frequency]:
            # F_test = [[lbl, fb.fil[0][lbl]*f_S] for lbl in F_test_lbls]
            ## sort list of tuples using the LAST element of the tuple (= frequency)
            # F_test = sorted(F_test, key=lambda t: t[::-1])

            f_lbls = []
            f_vals = []
            a_lbls = []
            a_targs = []
            a_targs_dB = []
            ft = fb.fil[0]['ft']  # get filter type ('IIR', 'FIR')
            unit = fb.fil[0]['amp_specs_unit']
            unit = 'dB'  # fix this for the moment
            # read specifications from filter dict and sort them depending on the response type
            if fb.fil[0]['rt'] in {'LP', 'HP', 'BP', 'BS'}:
                if fb.fil[0]['rt'] == 'LP':
                    f_lbls = ['F_PB', 'F_SB']
                    a_lbls = ['A_PB', 'A_SB']
                elif fb.fil[0]['rt'] == 'HP':
                    f_lbls = ['F_SB', 'F_PB']
                    a_lbls = ['A_SB', 'A_PB']
                elif fb.fil[0]['rt'] == 'BP':
                    f_lbls = ['F_SB', 'F_PB', 'F_PB2', 'F_SB2']
                    a_lbls = ['A_SB', 'A_PB', 'A_PB', 'A_SB2']
                elif fb.fil[0]['rt'] == 'BS':
                    f_lbls = ['F_PB', 'F_SB', 'F_SB2', 'F_PB2']
                    a_lbls = ['A_PB', 'A_SB', 'A_SB', 'A_PB2']

            # Try to construct lists of frequency / amplitude labels and specs
            # When one of the labels doesn't exist in the filter dict, delete
            # all corresponding amplitude and frequency entries
                err = [False] * len(f_lbls)  # initialize error list
                f_vals = []
                a_targs = []
                for i in range(len(f_lbls)):
                    try:
                        f = fb.fil[0][f_lbls[i]]
                        f_vals.append(f)
                    except KeyError as e:
                        f_vals.append('')
                        err[i] = True
                        logger.debug(e)
                    try:
                        a = fb.fil[0][a_lbls[i]]
                        a_dB = lin2unit(fb.fil[0][a_lbls[i]], ft, a_lbls[i],
                                        unit)
                        a_targs.append(a)
                        a_targs_dB.append(a_dB)
                    except KeyError as e:
                        a_targs.append('')
                        a_targs_dB.append('')
                        err[i] = True
                        logger.debug(e)

                for i in range(len(f_lbls)):
                    if err[i]:
                        del f_lbls[i]
                        del f_vals[i]
                        del a_lbls[i]
                        del a_targs[i]
                        del a_targs_dB[i]

                f_vals = np.asarray(f_vals)  # convert to numpy array

                logger.debug("input_info.showFiltPerf\nF_test = %s" % f_lbls)

                # Calculate frequency response at test frequencies
                [w_test,
                 a_test] = sig.freqz(bb, aa,
                                     2.0 * pi * f_vals.astype(np.float))

            (F_min, H_min, F_max, H_max) = _find_min_max(self, 0, 1, unit='V')
            # append frequencies and values for min. and max. filter reponse to
            # test vector
            f_lbls += ['Min.', 'Max.']
            # QTableView does not support direct formatting, use QLabel
            # f_lbls = [rt_label(l) for l in f_lbls]
            f_vals = np.append(f_vals, [F_min, F_max])
            a_targs = np.append(a_targs, [np.nan, np.nan])
            a_targs_dB = np.append(a_targs_dB, [np.nan, np.nan])
            a_test = np.append(a_test, [H_min, H_max])
            # calculate response of test frequencies in dB
            a_test_dB = -20 * log10(abs(a_test))

            ft = fb.fil[0][
                'ft']  # get filter type ('IIR', 'FIR') for dB <-> lin conversion
            #            unit = fb.fil[0]['amp_specs_unit']
            unit = 'dB'  # fix this for the moment

            # build a list with the corresponding target specs:
            a_targs_pass = []
            eps = 1e-3
            for i in range(len(f_lbls)):
                if 'PB' in f_lbls[i]:
                    a_targs_pass.append((a_test_dB[i] - a_targs_dB[i]) < eps)
                elif 'SB' in f_lbls[i]:
                    a_targs_pass.append(a_test_dB[i] >= a_targs_dB[i])
                else:
                    a_targs_pass.append(True)

            self.targs_spec_passed = np.all(a_targs_pass)

            logger.debug("H_targ = %s\n", a_targs, "H_test = %s\n", a_test,
                         "H_test_dB = %s\n", a_test_dB, "F_test = %s\n",
                         f_vals, "H_targ_pass = %s\n", a_targs_pass,
                         "passed: %s\n", self.targs_spec_passed)

            self.tblFiltPerf.setRowCount(len(a_test))  # number of table rows
            self.tblFiltPerf.setColumnCount(5)  # number of table columns

            self.tblFiltPerf.setHorizontalHeaderLabels([
                'f/{0:s}'.format(fb.fil[0]['freq_specs_unit']), '|H(f)| (dB)',
                'Spec (dB)', '|H(f)|', 'Spec'
            ])
            self.tblFiltPerf.setVerticalHeaderLabels(f_lbls)
            for row in range(len(a_test)):
                self.tblFiltPerf.setItem(
                    row, 0,
                    QTableWidgetItem(str('{0:.4g}'.format(f_vals[row] * f_S))))
                self.tblFiltPerf.setItem(
                    row, 1, QTableWidgetItem(str('%2.3f' % (a_test_dB[row]))))
                self.tblFiltPerf.setItem(
                    row, 2, QTableWidgetItem(str('%2.3g' % (a_targs_dB[row]))))
                self.tblFiltPerf.setItem(
                    row, 3, QTableWidgetItem(str('%.3g' % (abs(a_test[row])))))
                self.tblFiltPerf.setItem(
                    row, 4, QTableWidgetItem(str('%2.3f' % (a_targs[row]))))
                if not a_targs_pass[row]:
                    self.tblFiltPerf.item(row,
                                          1).setBackground(QtGui.QColor('red'))
                    self.tblFiltPerf.item(row,
                                          3).setBackground(QtGui.QColor('red'))

            self.tblFiltPerf.resizeColumnsToContents()
            self.tblFiltPerf.resizeRowsToContents()