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 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 FuT(roots_in, *kwargs): """Select Function-Under-Test (FuT)""" return unique_roots(roots_in, tol=1e-3, magsort = magsort, rtype = rtype, rdist = rdist)
def zplane(self, b=None, a=1, z=None, p=None, k =1, pn_eps=1e-3, analog=False, plt_ax = None, verbose=False, 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() pltLib : string (default: 'matplotlib') Library for plotting the P/Z plane. Currently, only matplotlib is implemented. When pltLib = 'none' or when matplotlib is not available, only pass the poles / zeros and their multiplicity verbose : boolean (default: False) When verbose == True, print poles / zeros and their multiplicity. 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 size, color etc. of markers and 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], 'equal') 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) # t1 = plt.plot(z.real, z.imag, 'go', ms=10, label=label) # plt.setp( t1, markersize=mzs, markeredgewidth=2.0, # markeredgecolor=mzc, markerfacecolor='none') # Plot the poles ax.scatter(p.real, p.imag, s=mps*mps, zorder=2, marker='x', color=mpc, lw=lw, label=plabel) # Print multiplicity of poles / zeros for i in range(len(z)): if verbose == True: print('z', 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 = 'bottom') for i in range(len(p)): if verbose == True: print('p', 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') # 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)) # print(ax.get_xlim(),ax.get_ylim()) return z, p, k
#vals = np.roots(np.convolve(ones(500),ones(100))) # 100 double and 400 single complex roots, #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.clock() for i in range(Navg): roots, mult = dsp.unique_roots(vals, rtype = rtype, rdist='manhattan') t2 = time.clock() T_dsp = (t2 - t1)/Navg print (mult) print('============ signal.unique_roots() =============================') t1 = time.clock() for i in range(Navg): roots, mult = sig.unique_roots(vals, rtype = rtype) t2 = time.clock() 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)