def besselord(omega_p, omega_s, alfa_max, alfa_min, omega_d, max_pc_delay): min_order = 0 # partimos de suponer que al menos será el orden de un Butter o más grande start_order, _ = sig.buttord(omega_p, omega_s, alfa_max, alfa_min + alfa_max, analog=True) # de forma iterativa, intentamos cumplimentar la plantilla for ii in range(start_order, 20): z, p, k = sig.besselap(ii, norm='delay') this_lti = sig.ZerosPolesGain(z, p, k).to_tf() _, mm, pp = sig.bode(this_lti, w=[0, 0.0001, omega_p, omega_d, omega_d + 0.0001]) # attenuation in omega_p, i.e. bandpass end this_ripple = -mm[2] # relative delay this_delay = 1 - np.abs((pp[4] - pp[3]) / (pp[1] - pp[0])) if this_ripple <= alfa_max and this_delay <= max_pc_delay: break min_order = ii return min_order
def __init__(self,f,order,f0,Z0=50.): """Constructor @param f list of float frequencies @param order int filter order @param f0 float cutoff frequency (Hz) @note This device requires installation of the scipy package """ try: from scipy.signal import besselap except: raise SignalIntegrityExceptionSParameters('Bessel filters require the scipy package to be installed') # pragma: no cover (z,p,k)=besselap(order,'mag') # this filter has -3 dB point at w=1 radian ClassicalFilter.__init__(self,f,z,p,k,f0,Z0)
def plane_25d(x0, r0, npw, fs, max_order=None, c=None, s2z=matchedz_zpk): r"""Virtual plane wave by 2.5-dimensional NFC-HOA. .. math:: D(\phi_0, s) = 2\e{\frac{s}{c}r_0} \sum_{m=-M}^{M} (-1)^m \Big(\frac{s}{s-\frac{c}{r_0}\sigma_0}\Big)^\mu \prod_{l=1}^{\nu} \frac{s^2}{(s-\frac{c}{r_0}\sigma_l)^2+(\frac{c}{r_0}\omega_l)^2} \e{\i m(\phi_0 - \phi_\text{pw})} The driving function is represented in the Laplace domain, from which the recursive filters are designed. :math:`\sigma_l + \i\omega_l` denotes the complex roots of the reverse Bessel polynomial. The number of second-order sections is :math:`\nu = \big\lfloor\tfrac{|m|}{2}\big\rfloor`, whereas the number of first-order section :math:`\mu` is either 0 or 1 for even and odd :math:`|m|`, respectively. Parameters ---------- x0 : (N, 3) array_like Sequence of secondary source positions. r0 : float Radius of the circular secondary source distribution. npw : (3,) array_like Unit vector (propagation direction) of plane wave. fs : int Sampling frequency in Hertz. max_order : int, optional Ambisonics order. c : float, optional Speed of sound in m/s. s2z : callable, optional Function transforming s-domain poles and zeros into z-domain, e.g. :func:`matchedz_zpk`, :func:`scipy.signal.bilinear_zpk`. Returns ------- delay : float Overall delay in seconds. weight : float Overall weight. sos : list of numpy.ndarray Second-order section filters :func:`scipy.signal.sosfilt`. phaseshift : (N,) numpy.ndarray Phase shift in radians. selection : (N,) numpy.ndarray Boolean array containing only ``True`` indicating that all secondary source are "active" for NFC-HOA. secondary_source_function : callable A function that can be used to create the sound field of a single secondary source. See `sfs.td.synthesize()`. Examples -------- .. plot:: :context: close-figs delay, weight, sos, phaseshift, selection, secondary_source = \ sfs.td.nfchoa.plane_25d(array.x, R, npw, fs) d = sfs.td.nfchoa.driving_signals_25d( delay, weight, sos, phaseshift, signal) plot(d, selection, secondary_source) """ if max_order is None: max_order = _util.max_order_circular_harmonics(len(x0)) if c is None: c = _default.c x0 = _util.asarray_of_rows(x0) npw = _util.asarray_1d(npw) phi0, _, _ = _util.cart2sph(*x0.T) phipw, _, _ = _util.cart2sph(*npw) phaseshift = phi0 - phipw + _np.pi delay = -r0 / c weight = 2 sos = [] for m in range(max_order + 1): _, p, _ = _sig.besselap(m, norm='delay') s_zeros = _np.zeros(m) s_poles = c / r0 * p s_gain = 1 z_zeros, z_poles, z_gain = s2z(s_zeros, s_poles, s_gain, fs) sos.append(_sig.zpk2sos(z_zeros, z_poles, z_gain, pairing='nearest')) selection = _util.source_selection_all(len(x0)) return (delay, weight, sos, phaseshift, selection, _secondary_source_point(c))
def point_3d(x0, r0, xs, fs, max_order=None, c=None, s2z=matchedz_zpk): r"""Virtual point source by 3-dimensional NFC-HOA. .. math:: D(\phi_0, s) = \frac{\e{\frac{s}{c}(r_0-r_\text{s})}}{4 \pi r_0 r_\text{s}} \sum_{n=0}^{N} (2n+1) P_{n}(\cos\Theta) \Big(\frac{s-\frac{c}{r_\text{s}}\sigma_0}{s-\frac{c}{r_0}\sigma_0}\Big)^\mu \prod_{l=1}^{\nu} \frac{(s-\frac{c}{r_\text{s}}\sigma_l)^2-(\frac{c}{r_\text{s}}\omega_l)^2} {(s-\frac{c}{r_0}\sigma_l)^2+(\frac{c}{r_0}\omega_l)^2} The driving function is represented in the Laplace domain, from which the recursive filters are designed. :math:`\sigma_l + \i\omega_l` denotes the complex roots of the reverse Bessel polynomial. The number of second-order sections is :math:`\nu = \big\lfloor\tfrac{|m|}{2}\big\rfloor`, whereas the number of first-order section :math:`\mu` is either 0 or 1 for even and odd :math:`|m|`, respectively. :math:`P_{n}(\cdot)` denotes the Legendre polynomial of degree :math:`n`, and :math:`\Theta` the angle between :math:`(\theta, \phi)` and :math:`(\theta_\text{s}, \phi_\text{s})`. Parameters ---------- x0 : (N, 3) array_like Sequence of secondary source positions. r0 : float Radius of the spherial secondary source distribution. xs : (3,) array_like Virtual source position. fs : int Sampling frequency in Hertz. max_order : int, optional Ambisonics order. c : float, optional Speed of sound in m/s. s2z : callable, optional Function transforming s-domain poles and zeros into z-domain, e.g. :func:`matchedz_zpk`, :func:`scipy.signal.bilinear_zpk`. Returns ------- delay : float Overall delay in seconds. weight : float Overall weight. sos : list of numpy.ndarray Second-order section filters :func:`scipy.signal.sosfilt`. phaseshift : (N,) numpy.ndarray Phase shift in radians. selection : (N,) numpy.ndarray Boolean array containing only ``True`` indicating that all secondary source are "active" for NFC-HOA. secondary_source_function : callable A function that can be used to create the sound field of a single secondary source. See `sfs.td.synthesize()`. """ if max_order is None: max_order = _util.max_order_spherical_harmonics(len(x0)) if c is None: c = _default.c x0 = _util.asarray_of_rows(x0) xs = _util.asarray_1d(xs) phi0, theta0, _ = _util.cart2sph(*x0.T) phis, thetas, rs = _util.cart2sph(*xs) phaseshift = _np.arccos(_np.dot(x0 / r0, xs / rs)) delay = (rs - r0) / c weight = 1 / r0 / rs sos = [] for m in range(max_order + 1): _, p, _ = _sig.besselap(m, norm='delay') s_zeros = c / rs * p s_poles = c / r0 * p s_gain = 1 z_zeros, z_poles, z_gain = s2z(s_zeros, s_poles, s_gain, fs) sos.append(_sig.zpk2sos(z_zeros, z_poles, z_gain, pairing='nearest')) selection = _util.source_selection_all(len(x0)) return (delay, weight, sos, phaseshift, selection, _secondary_source_point(c))
z, p, k = sig.buttap(this_order) eps = np.sqrt(10**(this_ripple / 10) - 1) num, den = sig.zpk2tf(z, p, k) num, den = sig.lp2lp(num, den, eps**(-1 / this_order)) z, p, k = sig.tf2zpk(num, den) elif aprox_name == 'Chebyshev1': z, p, k = sig.cheb1ap(this_order, this_ripple) elif aprox_name == 'Chebyshev2': z, p, k = sig.cheb2ap(this_order, this_ripple) elif aprox_name == 'Bessel': z, p, k = sig.besselap(this_order, norm='mag') elif aprox_name == 'Cauer': z, p, k = sig.ellipap(this_order, this_ripple, this_att) num, den = sig.zpk2tf(z, p, k) all_sys.append(sig.TransferFunction(num, den)) filter_names.append(aprox_name + '_ord_' + str(this_order) + '_rip_' + str(this_ripple) + '_att_' + str(this_att)) analyze_sys(all_sys, filter_names)
if aprox_name == 'Butterworth': z, p, k = sig.buttap(order2analyze) elif aprox_name == 'Chebyshev1': z, p, k = sig.cheb1ap(order2analyze, ripple) elif aprox_name == 'Chebyshev2': z, p, k = sig.cheb2ap(order2analyze, ripple) elif aprox_name == 'Bessel': z, p, k = sig.besselap(order2analyze, norm='mag') elif aprox_name == 'Cauer': z, p, k = sig.ellipap(order2analyze, ripple, attenuation) num, den = sig.zpk2tf(z, p, k) # Desnormalizamos para wc num, den = sig.lp2lp(num, den, 2 * np.pi * fc) my_analog_filter = sig.TransferFunction(num, den) my_analog_filter_desc = aprox_name + '_ord_' + str(order2analyze) + '_analog' all_sys.append(my_analog_filter) filter_names.append(my_analog_filter_desc)
# Obtengo la Transferencia Normalizada my_tf_ch = tfunction(NUM2, DEN2) print('\n', my_tf_ch) #pretty_print_lti(my_tf_ch) print('\nDenominador Factorizado de la Transferencia Normalizada:', convert2SOS(my_tf_ch)) # ---------------------------------------------------------------------------- print('\n\nFiltro Bessel de Orden 5:\n') # Filtro de Bessel: Uso de los Métodos dentro del Paquete signal de Scipy. z3, p3, k3 = sig.besselap(orden_filtro, 'mag') # Obtengo los Coeficientes de mi Transferencia. NUM3, DEN3 = sig.zpk2tf(z3, p3, k3) # Obtengo la Transferencia Normalizada my_tf_be = tfunction(NUM3, DEN3) print('\n', my_tf_be) #pretty_print_lti(my_tf_be) print('Denominador Factorizado de la Transferencia Normalizada:', convert2SOS(my_tf_be)) # ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
@author: mariano """ import scipy.signal as sig import numpy as np from splane import tfadd, tfcascade, analyze_sys, pzmap, grpDelay, bodePlot, pretty_print_lti nn = 2 # orden ripple = 3 # dB eps = np.sqrt(10**(ripple/10)-1) z,p,k = sig.besselap(nn, norm='delay') # z,p,k = sig.buttap(nn) num_lp, den_lp = sig.zpk2tf(z,p,k) num_lp, den_lp = sig.lp2lp(num_lp, den_lp, eps**(-1/nn) ) num_hp, den_hp = sig.lp2hp(num_lp,den_lp) lp_sys = sig.TransferFunction(num_lp,den_lp) hp_sys = sig.TransferFunction(num_hp,den_hp) xover = tfadd(lp_sys, hp_sys) bandpass = tfcascade(lp_sys, hp_sys) pretty_print_lti(lp_sys) pretty_print_lti(hp_sys) pretty_print_lti(xover)
if aprox_name == 'Butterworth': z,p,k = sig.buttap(ii) elif aprox_name == 'Chebyshev1': z,p,k = sig.cheb1ap(ii, ripple) elif aprox_name == 'Chebyshev2': z,p,k = sig.cheb2ap(ii, ripple) elif aprox_name == 'Bessel': z,p,k = sig.besselap(ii, norm='mag') elif aprox_name == 'Cauer': z,p,k = sig.ellipap(ii, ripple, attenuation) num, den = sig.zpk2tf(z,p,k) all_sys.append(sig.TransferFunction(num,den)) filter_names.append(aprox_name + '_ord_' + str(ii)) analyze_sys( all_sys, filter_names )
#!/usr/bin/env python3 from scipy.signal import besselap for n in range(1, 26): [zs, ps, k] = besselap(n) if (n == 1): print(" if (n == 1)\n {") else: print(" }\n else if (n == %d)\n {" % (n)) i = 0 for p in ps: #print " p[%d] = %.16f + %.16f*_Complex_I;"%(i,p.real,p.imag) print(" poles[%d] = std::complex<double> (%.18f, %.18f);" % (i, p.real, p.imag)) i = i + 1 print(" }")
filter_names.append('Butter_ord_' + str(order2analyze)) # Chebyshev z, p, k = sig.cheb1ap(order2analyze, ripple) num, den = sig.zpk2tf(z, p, k) all_sys.append(sig.TransferFunction(num, den)) filter_names.append('Cheby_ord_' + str(order2analyze)) # Bessel z, p, k = sig.besselap(order2analyze, norm='delay') num, den = sig.zpk2tf(z, p, k) all_sys.append(sig.TransferFunction(num, den)) filter_names.append('Bessel_ord_' + str(order2analyze)) # Cauer # z,p,k = sig.ellipap(order2analyze, ripple, attenuation) # num, den = sig.zpk2tf(z,p,k) # all_sys.append(sig.TransferFunction(num,den))
NUM, DEN = sig.zpk2tf (z, p, k) NUM, DEN = sig.lp2lp ( NUM, DEN, wb ) z, p, k = sig.tf2zpk ( NUM, DEN ) elif (aprox_name == 'Chebyshev1'): # Le paso Orden y ripple (alfa_máx) z, p, k = sig.cheb1ap (order_filter, ripple_) elif (aprox_name == 'Bessel'): z, p, k = sig.besselap ( order_filter, norm = 'mag' ) else: sys.exit(' No Existe esa Aproximación o Está mal Escrita.') # Genero el Numerador y Denominador de mi Transferencia NUM, DEN = sig.zpk2tf(z, p, k) my_tf = tf ( NUM, DEN ) # Genero mi Transferencia # Ploteo. bodePlot (my_tf)
if force_order <= 0: print_console_alert(aprox_name + ': parameters calculated') print( 'Chebyshev type 2 (equiripple in band-stop) order n = {:d}'.format( this_order)) elif aprox_name == 'Bessel': if force_order > 0: this_order = force_order else: this_order = besselord(omega_p, omega_s, alfa_max, alfa_min, omega_d, max_pc_delay) z, p, k = sig.besselap(this_order, norm='delay') if force_order <= 0: print_console_alert(aprox_name + ': parameters calculated') print('order n = {:d}'.format(this_order)) elif aprox_name == 'Cauer': if force_order > 0: this_order = force_order else: this_order, _ = sig.ellipord(omega_p, omega_s, alfa_max, alfa_min, analog=True)