Esempio n. 1
0
def FuT(roots_in, *kwargs):
    """Select Function-Under-Test (FuT)"""
    return unique_roots(roots_in,
                        tol=1e-3,
                        magsort=magsort,
                        rtype=rtype,
                        rdist=rdist)
Esempio n. 2
0
    def sort_dict_freqs(self):
        """
        - Sort visible filter dict frequency spec entries with ascending frequency if
             the sort button is activated
        - Update the visible QLineEdit frequency widgets

        The method is called when:
        - update_UI has been called after changing the filter design algorithm
          that the response type has been changed
          eg. from LP -> HP, requiring a different order of frequency entries
        - a frequency spec field has been edited
        - the sort button has been clicked (from filter_specs.py)
        """

        f_specs = [
            fb.fil[0][str(self.qlineedit[i].objectName())]
            for i in range(self.n_cur_labels)
        ]
        if fb.fil[0]['freq_specs_sort']:
            f_specs.sort()

        # Make sure normalized freqs are in the range ]0, 0.5[ and are different
        # by at least MIN_FREQ_STEP
        for i in range(self.n_cur_labels):
            if f_specs[i] <= MIN_FREQ:
                logger.warning("Frequencies must be > 0, changed {0} from {1:.4g} to {2:.4g}."\
                               .format(str(self.qlineedit[i].objectName()),f_specs[i]*fb.fil[0]['f_S'],
                                       (MIN_FREQ + MIN_FREQ_STEP)*fb.fil[0]['f_S']))
                f_specs[i] = MIN_FREQ + MIN_FREQ_STEP
            if f_specs[i] >= MAX_FREQ:
                logger.warning("Frequencies must be < f_S /2, changed {0} from {1:.4g} to {2:.4g}."\
                               .format(str(self.qlineedit[i].objectName()),f_specs[i]*fb.fil[0]['f_S'],
                                       (MAX_FREQ - MIN_FREQ_STEP)*fb.fil[0]['f_S']))
                f_specs[i] = MAX_FREQ - MIN_FREQ_STEP

            fb.fil[0][str(self.qlineedit[i].objectName())] = f_specs[i]

        # check for (nearly) identical elements:
        _, mult = unique_roots(f_specs, tol=MIN_FREQ_STEP)
        ident = [x for x in mult if x > 1]
        if ident:
            logger.warning(
                "Frequencies must differ by at least {0:.4g}".format(
                    MIN_FREQ_STEP * fb.fil[0]['f_S']))

        self.load_dict()
Esempio n. 3
0
    def zplane(self,
               b=None,
               a=1,
               z=None,
               p=None,
               k=1,
               pn_eps=1e-3,
               analog=False,
               plt_ax=None,
               plt_poles=True,
               style='square',
               anaCircleRad=0,
               lw=2,
               mps=10,
               mzs=10,
               mpc='r',
               mzc='b',
               plabel='',
               zlabel=''):
        """
        Plot the poles and zeros in the complex z-plane either from the
        coefficients (`b,`a) of a discrete transfer function `H`(`z`) (zpk = False)
        or directly from the zeros and poles (z,p) (zpk = True).

        When only b is given, an FIR filter with all poles at the origin is assumed.

        Parameters
        ----------
        b :  array_like
             Numerator coefficients (transversal part of filter)
             When b is not None, poles and zeros are determined from the coefficients
             b and a

        a :  array_like (optional, default = 1 for FIR-filter)
             Denominator coefficients (recursive part of filter)

        z :  array_like, default = None
             Zeros
             When b is None, poles and zeros are taken directly from z and p

        p :  array_like, default = None
             Poles

        analog : boolean (default: False)
            When True, create a P/Z plot suitable for the s-plane, i.e. suppress
            the unit circle (unless anaCircleRad > 0) and scale the plot for
            a good display of all poles and zeros.

        pn_eps : float (default : 1e-2)
             Tolerance for separating close poles or zeros

        plt_ax : handle to axes for plotting (default: None)
            When no axes is specified, the current axes is determined via plt.gca()

        plt_poles : Boolean (default : True)
            Plot poles. This can be used to suppress poles for FIR systems
            where all poles are at the origin.

        style : string (default: 'square')
            Style of the plot, for style == 'square' make scale of x- and y-
            axis equal.

        mps : integer  (default: 10)
            Size for pole marker

        mzs : integer (default: 10)
            Size for zero marker

        mpc : char (default: 'r')
            Pole marker colour

        mzc : char (default: 'b')
            Zero marker colour

        lw : integer (default:  2)
            Linewidth for unit circle

        plabel, zlabel : string (default: '')
            This string is passed to the plot command for poles and zeros and
            can be displayed by legend()


        Returns
        -------
        z, p, k : ndarray


        Notes
        -----
        """
        # TODO:
        # - polar option
        # - add keywords for color of circle -> **kwargs
        # - add option for multi-dimensional arrays and zpk data

        # make sure that all inputs are arrays
        b = np.atleast_1d(b)
        a = np.atleast_1d(a)
        z = np.atleast_1d(z)  # make sure that p, z  are arrays
        p = np.atleast_1d(p)

        if b.any():  # coefficients were specified
            if len(b) < 2 and len(a) < 2:
                logger.error(
                    'No proper filter coefficients: both b and a are scalars!')
                return z, p, k

            # The coefficients are less than 1, normalize the coefficients
            if np.max(b) > 1:
                kn = np.max(b)
                b = b / float(kn)
            else:
                kn = 1.

            if np.max(a) > 1:
                kd = np.max(a)
                a = a / abs(kd)
            else:
                kd = 1.

            # Calculate the poles, zeros and scaling factor
            p = np.roots(a)
            z = np.roots(b)
            k = kn / kd
        elif not (len(p) or len(z)):  # P/Z were specified
            logger.error('Either b,a or z,p must be specified!')
            return z, p, k

        # find multiple poles and zeros and their multiplicities
        if len(p) < 2:  # single pole, [None] or [0]
            if not p or p == 0:  # only zeros, create equal number of poles at origin
                p = np.array(0, ndmin=1)  #
                num_p = np.atleast_1d(len(z))
            else:
                num_p = [1.]  # single pole != 0
        else:
            #p, num_p = sig.signaltools.unique_roots(p, tol = pn_eps, rtype='avg')
            p, num_p = unique_roots(p, tol=pn_eps, rtype='avg')
    #        p = np.array(p); num_p = np.ones(len(p))
        if len(z) > 0:
            z, num_z = unique_roots(z, tol=pn_eps, rtype='avg')

    #        z = np.array(z); num_z = np.ones(len(z))
    #z, num_z = sig.signaltools.unique_roots(z, tol = pn_eps, rtype='avg')
        else:
            num_z = []

        ax = plt_ax  #.subplot(111)
        if analog == False:
            # create the unit circle for the z-plane
            uc = patches.Circle((0, 0),
                                radius=1,
                                fill=False,
                                color='grey',
                                ls='solid',
                                zorder=1)
            ax.add_patch(uc)
            if style == 'square':
                #r = 1.1
                #ax.axis([-r, r, -r, r]) # overridden by next option
                ax.axis('equal')
        #    ax.spines['left'].set_position('center')
        #    ax.spines['bottom'].set_position('center')
        #    ax.spines['right'].set_visible(True)
        #    ax.spines['top'].set_visible(True)

        else:  # s-plane
            if anaCircleRad > 0:
                # plot a circle with radius = anaCircleRad
                uc = patches.Circle((0, 0),
                                    radius=anaCircleRad,
                                    fill=False,
                                    color='grey',
                                    ls='solid',
                                    zorder=1)
                ax.add_patch(uc)
            # plot real and imaginary axis
            ax.axhline(lw=2, color='k', zorder=1)
            ax.axvline(lw=2, color='k', zorder=1)

        # Plot the zeros
        ax.scatter(z.real,
                   z.imag,
                   s=mzs * mzs,
                   zorder=2,
                   marker='o',
                   facecolor='none',
                   edgecolor=mzc,
                   lw=lw,
                   label=zlabel)
        # and print their multiplicity
        for i in range(len(z)):
            logger.debug('z: {0} | {1} | {2}'.format(i, z[i], num_z[i]))
            if num_z[i] > 1:
                ax.text(np.real(z[i]),
                        np.imag(z[i]),
                        '  (' + str(num_z[i]) + ')',
                        va='top',
                        color=mzc)
        if plt_poles:
            # Plot the poles
            ax.scatter(p.real,
                       p.imag,
                       s=mps * mps,
                       zorder=2,
                       marker='x',
                       color=mpc,
                       lw=lw,
                       label=plabel)
            # and print their multiplicity
            for i in range(len(p)):
                logger.debug('p:{0} | {1} | {2}'.format(i, p[i], num_p[i]))
                if num_p[i] > 1:
                    ax.text(np.real(p[i]),
                            np.imag(p[i]),
                            '  (' + str(num_p[i]) + ')',
                            va='bottom',
                            color=mpc)

# =============================================================================
#            # increase distance between ticks and labels
#            # to give some room for poles and zeros
#         for tick in ax.get_xaxis().get_major_ticks():
#             tick.set_pad(12.)
#             tick.label1 = tick._get_text1()
#         for tick in ax.get_yaxis().get_major_ticks():
#             tick.set_pad(12.)
#             tick.label1 = tick._get_text1()
#
# =============================================================================
        xl = ax.get_xlim()
        Dx = max(abs(xl[1] - xl[0]), 0.05)
        yl = ax.get_ylim()
        Dy = max(abs(yl[1] - yl[0]), 0.05)
        ax.set_xlim((xl[0] - Dx * 0.05, max(xl[1] + Dx * 0.05, 0)))
        ax.set_ylim((yl[0] - Dy * 0.05, yl[1] + Dy * 0.05))

        return z, p, k
Esempio n. 4
0
#vals = np.roots(np.convolve(ones(500),ones(500))) # 500 double complex roots

#vals = np.roots(np.convolve(ones(5),ones(7))) # 4 double complex roots

## tests with nans
#vals = list(ones(5) * 1j); vals.append(np.nan); vals.append(np.nan)
#vals = []
#print(vals)

Navg = 1000
rtype = 'min'
print('============ dsp.unique_roots() ================================')
t1 = time.process_time()

for i in range(Navg):
    roots, mult = dsp.unique_roots(vals, rtype=rtype, rdist='manhattan')
t2 = time.process_time()
T_dsp = (t2 - t1) / Navg
print(mult)

print('============ signal.unique_roots() =============================')
t1 = time.process_time()
for i in range(Navg):
    roots, mult = sig.unique_roots(vals, rtype=rtype)
t2 = time.process_time()
T_sig = (t2 - t1) / Navg
print(mult)

print("T_dsp = ", T_dsp, " s")
print("T_sig = ", T_sig, " s")
print("T_dsp / T_sig = ", T_dsp / T_sig)