[docs]defdelay_thiran_raw(delay_s,order=1):""" Create a thiran filter to simulate a delay line with maximally flat group delay for a given filter order. This is done by generating the poles of a Bessel filter - which has maximally flat group delay for a low-pass filter. The poles are then rescaled to generate the desired delay and then right-plane zeroes are created to mirror the poles. This generates an all-pass filter with constant gain and constant delay. This implementation can create filters to very high order of 100 poles or more. """# take the poles of this normalized bessel filter (delay=1s)z,p,k=scipy.signal.besselap(order,norm="delay")# now rescale for desired delayroots=p/delay_s*2iforder%2==0:k=1else:k=-1returnzpk(-roots.conjugate(),roots,k)
[docs]defroot_factored_quadrature_sum(*filts):""" Return a square root filter TODO, make this numerically better behaved and test its output. This should actually be implemented using spectral factorization using and ARE. But debugging that sounds hard. This should be similarly robust but is not a general method. """ss_sq=Noneforfiltinfilts:filt=filt.asSSifss_sqisNone:ss_sq=filt.conjugate()*filtelse:ss_sq=ss_sq+filt.conjugate()*filt# convert back to ZPKss_sqZPK=ss_sq.asZPKdefstable_root_extract(roots):# This is not a fully robust way to do this!roots=np.asarray(roots)lhp=roots[roots.real<0]eq0=roots[roots.real==0]rhp=roots[roots.real>0]assert(len(lhp)==len(rhp))assert(len(eq0)%2==0)eq0=sorted(eq0,key=lambdar:abs(r.imag))# skip every other real onereturnlist(lhp)+list(eq0[::4])+list(eq0[1::4])p=stable_root_extract(ss_sqZPK.p)z=stable_root_extract(ss_sqZPK.z)k=ss_sqZPK.k**0.5ss_rt=zpk(z,p,k,fiducial_f=[])# TODO, test against the actual magnitudereturnss_rt