def FuT(roots_in, *kwargs): """Select Function-Under-Test (FuT)""" return unique_roots(roots_in, tol=1e-3, magsort=magsort, rtype=rtype, rdist=rdist)
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()
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
#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)