scipy.signal.

buttap#

scipy.signal.buttap(N, *, xp=None, device=None)[source]#

Return (z, p, k) for analog prototype of Nth-order Butterworth filter.

The filter will have an angular (e.g., rad/s) cutoff frequency of 1.

Parameters:
Nint

The order of the filter

xparray_namespace, optional

Optional array namespace. Should be compatible with the array API standard, or supported by array-api-compat. Default: numpy

deviceany

optional device specification for output. Should match one of the supported device specification in xp.

Returns:
zndarray[float64]

Zeros of the transfer function. Is always an empty array.

pndarray[complex128]

Poles of the transfer function as an (N,) array.

kfloat

Gain of the transfer function, which is always one.

See also

butter

Filter design function using this prototype

Notes

Here, a cutoff frequency of \(\omega_c = 1\,\)rad/s and a gain of \(k=1\) are assumed. The transfer function can be expressed as [1]

\[H(s) = k \prod_{l=0}^{N-1} \frac{\omega_c}{s - p_l} \quad\text{with poles}\quad p_l = -\omega_c\exp\!\left\{ j\pi\frac{2l+1-N}{2N} \right\} \,.\]

Hence, this function returns no zeros and N poles. Note that the poles are ordered in a way so that the l-th and (N-1-l)-th pole are complex conjugates of each other. Furthermore, if N is odd, then the (N//2)-th pole is always \(-1\).

Array API Standard Support

buttap has support for Python Array API Standard compatible backends in addition to NumPy. The following combinations of backend and device (or other capability) are supported.

Library

CPU

GPU

NumPy

✅

n/a

CuPy

n/a

✅

PyTorch

✅

✅

JAX

✅

✅

Dask

✅

n/a

See Support for the array API standard for more information.

References

[1]

“Butterworth filter”, Wikipedia, https://en.wikipedia.org/wiki/Butterworth_filter

Examples

Compute the zeros, poles, and gain of a 2nd-order Butterworth analog lowpass prototype with a cutoff frequency of 1 rad/s:

>>> from scipy.signal import buttap
>>> z, p, k = buttap(2)
>>> z
array([], dtype=float64)
>>> p
array([-0.70710678+0.70710678j, -0.70710678-0.70710678j])
>>> k
1.0

Bode plot of the frequency response of a 3rd-order prototype:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from scipy.signal import freqs_zpk, buttap
...
>>> z, p, k = buttap(3)
>>> f = np.geomspace(1e-1, 1e1, 200)
>>> _, h = freqs_zpk(z, p, k, worN=f)
>>> h_db, h_ph = 20 * np.log10(np.abs(h)), np.unwrap(np.angle(h))
...
>>> _, (ax0, ax1) = plt.subplots(2, 1, sharex='all', constrained_layout=True)
>>> ax0.set(title='3rd-order Butterworth prototype', ylabel='Magnitude in dB',
...         yticks=[-60, -40, -20, 0])
>>> ax0.semilogx(f, h_db, 'C0', label='Magnitude')
>>> ax1.set(ylabel="Phase in radians", xlabel="Frequency in rad/s",
...         yticks=np.pi*np.arange(-1.5, 0.5, 0.5), ylim=(-1.5*np.pi, 0),
...         yticklabels=[r'-3$\pi$/2', r'-$\pi$', r'-$\pi$/2', '0'],
...         xlim=(f[0], f[-1]))
>>> ax1.semilogx(f, h_ph, 'C1', label='Phase')
>>> for ax_ in (ax0, ax1):
...     ax_.axvline(1.0, color='C2', ls='--', alpha=.5, label='Cutoff frequency')
...     ax_.grid(True, which='both')
...     ax_.legend()
>>> plt.show()
../../_images/scipy-signal-buttap-1.png