|
"""The suite of window functions.""" |
|
|
|
import operator |
|
import warnings |
|
|
|
import numpy as np |
|
from scipy import linalg, special, fft as sp_fft |
|
|
|
__all__ = ['boxcar', 'triang', 'parzen', 'bohman', 'blackman', 'nuttall', |
|
'blackmanharris', 'flattop', 'bartlett', 'barthann', |
|
'hamming', 'kaiser', 'kaiser_bessel_derived', 'gaussian', |
|
'general_cosine', 'general_gaussian', 'general_hamming', |
|
'chebwin', 'cosine', 'hann', 'exponential', 'tukey', 'taylor', |
|
'dpss', 'get_window', 'lanczos'] |
|
|
|
|
|
def _len_guards(M): |
|
"""Handle small or incorrect window lengths""" |
|
if int(M) != M or M < 0: |
|
raise ValueError('Window length M must be a non-negative integer') |
|
return M <= 1 |
|
|
|
|
|
def _extend(M, sym): |
|
"""Extend window by 1 sample if needed for DFT-even symmetry""" |
|
if not sym: |
|
return M + 1, True |
|
else: |
|
return M, False |
|
|
|
|
|
def _truncate(w, needed): |
|
"""Truncate window by 1 sample if needed for DFT-even symmetry""" |
|
if needed: |
|
return w[:-1] |
|
else: |
|
return w |
|
|
|
|
|
def general_cosine(M, a, sym=True): |
|
r""" |
|
Generic weighted sum of cosine terms window |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window |
|
a : array_like |
|
Sequence of weighting coefficients. This uses the convention of being |
|
centered on the origin, so these will typically all be positive |
|
numbers, not alternating sign. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The array of window values. |
|
|
|
References |
|
---------- |
|
.. [1] A. Nuttall, "Some windows with very good sidelobe behavior," IEEE |
|
Transactions on Acoustics, Speech, and Signal Processing, vol. 29, |
|
no. 1, pp. 84-91, Feb 1981. :doi:`10.1109/TASSP.1981.1163506`. |
|
.. [2] Heinzel G. et al., "Spectrum and spectral density estimation by the |
|
Discrete Fourier transform (DFT), including a comprehensive list of |
|
window functions and some new flat-top windows", February 15, 2002 |
|
https://holometer.fnal.gov/GH_FFT.pdf |
|
|
|
Examples |
|
-------- |
|
Heinzel describes a flat-top window named "HFT90D" with formula: [2]_ |
|
|
|
.. math:: w_j = 1 - 1.942604 \cos(z) + 1.340318 \cos(2z) |
|
- 0.440811 \cos(3z) + 0.043097 \cos(4z) |
|
|
|
where |
|
|
|
.. math:: z = \frac{2 \pi j}{N}, j = 0...N - 1 |
|
|
|
Since this uses the convention of starting at the origin, to reproduce the |
|
window, we need to convert every other coefficient to a positive number: |
|
|
|
>>> HFT90D = [1, 1.942604, 1.340318, 0.440811, 0.043097] |
|
|
|
The paper states that the highest sidelobe is at -90.2 dB. Reproduce |
|
Figure 42 by plotting the window and its frequency response, and confirm |
|
the sidelobe level in red: |
|
|
|
>>> import numpy as np |
|
>>> from scipy.signal.windows import general_cosine |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = general_cosine(1000, HFT90D, sym=False) |
|
>>> plt.plot(window) |
|
>>> plt.title("HFT90D window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 10000) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = np.abs(fftshift(A / abs(A).max())) |
|
>>> response = 20 * np.log10(np.maximum(response, 1e-10)) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-50/1000, 50/1000, -140, 0]) |
|
>>> plt.title("Frequency response of the HFT90D window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
>>> plt.axhline(-90.2, color='red') |
|
>>> plt.show() |
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
fac = np.linspace(-np.pi, np.pi, M) |
|
w = np.zeros(M) |
|
for k in range(len(a)): |
|
w += a[k] * np.cos(k * fac) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def boxcar(M, sym=True): |
|
"""Return a boxcar or rectangular window. |
|
|
|
Also known as a rectangular window or Dirichlet window, this is equivalent |
|
to no window at all. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
Whether the window is symmetric. (Has no effect for boxcar.) |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.boxcar(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Boxcar window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the boxcar window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
w = np.ones(M, float) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def triang(M, sym=True): |
|
"""Return a triangular window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
See Also |
|
-------- |
|
bartlett : A triangular window that touches zero |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.triang(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Triangular window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = np.abs(fftshift(A / abs(A).max())) |
|
>>> response = 20 * np.log10(np.maximum(response, 1e-10)) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the triangular window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(1, (M + 1) // 2 + 1) |
|
if M % 2 == 0: |
|
w = (2 * n - 1.0) / M |
|
w = np.r_[w, w[::-1]] |
|
else: |
|
w = 2 * n / (M + 1.0) |
|
w = np.r_[w, w[-2::-1]] |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def parzen(M, sym=True): |
|
"""Return a Parzen window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
References |
|
---------- |
|
.. [1] E. Parzen, "Mathematical Considerations in the Estimation of |
|
Spectra", Technometrics, Vol. 3, No. 2 (May, 1961), pp. 167-190 |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.parzen(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Parzen window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Parzen window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(-(M - 1) / 2.0, (M - 1) / 2.0 + 0.5, 1.0) |
|
na = np.extract(n < -(M - 1) / 4.0, n) |
|
nb = np.extract(abs(n) <= (M - 1) / 4.0, n) |
|
wa = 2 * (1 - np.abs(na) / (M / 2.0)) ** 3.0 |
|
wb = (1 - 6 * (np.abs(nb) / (M / 2.0)) ** 2.0 + |
|
6 * (np.abs(nb) / (M / 2.0)) ** 3.0) |
|
w = np.r_[wa, wb, wa[::-1]] |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def bohman(M, sym=True): |
|
"""Return a Bohman window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.bohman(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Bohman window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2047) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Bohman window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
fac = np.abs(np.linspace(-1, 1, M)[1:-1]) |
|
w = (1 - fac) * np.cos(np.pi * fac) + 1.0 / np.pi * np.sin(np.pi * fac) |
|
w = np.r_[0, w, 0] |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def blackman(M, sym=True): |
|
r""" |
|
Return a Blackman window. |
|
|
|
The Blackman window is a taper formed by using the first three terms of |
|
a summation of cosines. It was designed to have close to the minimal |
|
leakage possible. It is close to optimal, only slightly worse than a |
|
Kaiser window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Blackman window is defined as |
|
|
|
.. math:: w(n) = 0.42 - 0.5 \cos(2\pi n/M) + 0.08 \cos(4\pi n/M) |
|
|
|
The "exact Blackman" window was designed to null out the third and fourth |
|
sidelobes, but has discontinuities at the boundaries, resulting in a |
|
6 dB/oct fall-off. This window is an approximation of the "exact" window, |
|
which does not null the sidelobes as well, but is smooth at the edges, |
|
improving the fall-off rate to 18 dB/oct. [3]_ |
|
|
|
Most references to the Blackman window come from the signal processing |
|
literature, where it is used as one of many windowing functions for |
|
smoothing values. It is also known as an apodization (which means |
|
"removing the foot", i.e. smoothing discontinuities at the beginning |
|
and end of the sampled signal) or tapering function. It is known as a |
|
"near optimal" tapering function, almost as good (by some measures) |
|
as the Kaiser window. |
|
|
|
References |
|
---------- |
|
.. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power |
|
spectra, Dover Publications, New York. |
|
.. [2] Oppenheim, A.V., and R.W. Schafer. Discrete-Time Signal Processing. |
|
Upper Saddle River, NJ: Prentice-Hall, 1999, pp. 468-471. |
|
.. [3] Harris, Fredric J. (Jan 1978). "On the use of Windows for Harmonic |
|
Analysis with the Discrete Fourier Transform". Proceedings of the |
|
IEEE 66 (1): 51-83. :doi:`10.1109/PROC.1978.10837`. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.blackman(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Blackman window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = np.abs(fftshift(A / abs(A).max())) |
|
>>> response = 20 * np.log10(np.maximum(response, 1e-10)) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Blackman window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
|
|
return general_cosine(M, [0.42, 0.50, 0.08], sym) |
|
|
|
|
|
def nuttall(M, sym=True): |
|
"""Return a minimum 4-term Blackman-Harris window according to Nuttall. |
|
|
|
This variation is called "Nuttall4c" by Heinzel. [2]_ |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
References |
|
---------- |
|
.. [1] A. Nuttall, "Some windows with very good sidelobe behavior," IEEE |
|
Transactions on Acoustics, Speech, and Signal Processing, vol. 29, |
|
no. 1, pp. 84-91, Feb 1981. :doi:`10.1109/TASSP.1981.1163506`. |
|
.. [2] Heinzel G. et al., "Spectrum and spectral density estimation by the |
|
Discrete Fourier transform (DFT), including a comprehensive list of |
|
window functions and some new flat-top windows", February 15, 2002 |
|
https://holometer.fnal.gov/GH_FFT.pdf |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.nuttall(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Nuttall window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Nuttall window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
return general_cosine(M, [0.3635819, 0.4891775, 0.1365995, 0.0106411], sym) |
|
|
|
|
|
def blackmanharris(M, sym=True): |
|
"""Return a minimum 4-term Blackman-Harris window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.blackmanharris(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Blackman-Harris window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Blackman-Harris window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
return general_cosine(M, [0.35875, 0.48829, 0.14128, 0.01168], sym) |
|
|
|
|
|
def flattop(M, sym=True): |
|
"""Return a flat top window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
Flat top windows are used for taking accurate measurements of signal |
|
amplitude in the frequency domain, with minimal scalloping error from the |
|
center of a frequency bin to its edges, compared to others. This is a |
|
5th-order cosine window, with the 5 terms optimized to make the main lobe |
|
maximally flat. [1]_ |
|
|
|
References |
|
---------- |
|
.. [1] D'Antona, Gabriele, and A. Ferrero, "Digital Signal Processing for |
|
Measurement Systems", Springer Media, 2006, p. 70 |
|
:doi:`10.1007/0-387-28666-7`. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.flattop(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Flat top window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the flat top window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
a = [0.21557895, 0.41663158, 0.277263158, 0.083578947, 0.006947368] |
|
return general_cosine(M, a, sym) |
|
|
|
|
|
def bartlett(M, sym=True): |
|
r""" |
|
Return a Bartlett window. |
|
|
|
The Bartlett window is very similar to a triangular window, except |
|
that the end points are at zero. It is often used in signal |
|
processing for tapering a signal, without generating too much |
|
ripple in the frequency domain. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The triangular window, with the first and last samples equal to zero |
|
and the maximum value normalized to 1 (though the value 1 does not |
|
appear if `M` is even and `sym` is True). |
|
|
|
See Also |
|
-------- |
|
triang : A triangular window that does not touch zero at the ends |
|
|
|
Notes |
|
----- |
|
The Bartlett window is defined as |
|
|
|
.. math:: w(n) = \frac{2}{M-1} \left( |
|
\frac{M-1}{2} - \left|n - \frac{M-1}{2}\right| |
|
\right) |
|
|
|
Most references to the Bartlett window come from the signal |
|
processing literature, where it is used as one of many windowing |
|
functions for smoothing values. Note that convolution with this |
|
window produces linear interpolation. It is also known as an |
|
apodization (which means"removing the foot", i.e. smoothing |
|
discontinuities at the beginning and end of the sampled signal) or |
|
tapering function. The Fourier transform of the Bartlett is the product |
|
of two sinc functions. |
|
Note the excellent discussion in Kanasewich. [2]_ |
|
|
|
References |
|
---------- |
|
.. [1] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra", |
|
Biometrika 37, 1-16, 1950. |
|
.. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", |
|
The University of Alberta Press, 1975, pp. 109-110. |
|
.. [3] A.V. Oppenheim and R.W. Schafer, "Discrete-Time Signal |
|
Processing", Prentice-Hall, 1999, pp. 468-471. |
|
.. [4] Wikipedia, "Window function", |
|
https://en.wikipedia.org/wiki/Window_function |
|
.. [5] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, |
|
"Numerical Recipes", Cambridge University Press, 1986, page 429. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.bartlett(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Bartlett window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Bartlett window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
|
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(0, M) |
|
w = np.where(np.less_equal(n, (M - 1) / 2.0), |
|
2.0 * n / (M - 1), 2.0 - 2.0 * n / (M - 1)) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def hann(M, sym=True): |
|
r""" |
|
Return a Hann window. |
|
|
|
The Hann window is a taper formed by using a raised cosine or sine-squared |
|
with ends that touch zero. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Hann window is defined as |
|
|
|
.. math:: w(n) = 0.5 - 0.5 \cos\left(\frac{2\pi{n}}{M-1}\right) |
|
\qquad 0 \leq n \leq M-1 |
|
|
|
The window was named for Julius von Hann, an Austrian meteorologist. It is |
|
also known as the Cosine Bell. It is sometimes erroneously referred to as |
|
the "Hanning" window, from the use of "hann" as a verb in the original |
|
paper and confusion with the very similar Hamming window. |
|
|
|
Most references to the Hann window come from the signal processing |
|
literature, where it is used as one of many windowing functions for |
|
smoothing values. It is also known as an apodization (which means |
|
"removing the foot", i.e. smoothing discontinuities at the beginning |
|
and end of the sampled signal) or tapering function. |
|
|
|
References |
|
---------- |
|
.. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power |
|
spectra, Dover Publications, New York. |
|
.. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", |
|
The University of Alberta Press, 1975, pp. 106-108. |
|
.. [3] Wikipedia, "Window function", |
|
https://en.wikipedia.org/wiki/Window_function |
|
.. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, |
|
"Numerical Recipes", Cambridge University Press, 1986, page 425. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.hann(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Hann window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = np.abs(fftshift(A / abs(A).max())) |
|
>>> response = 20 * np.log10(np.maximum(response, 1e-10)) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Hann window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
|
|
return general_hamming(M, 0.5, sym) |
|
|
|
|
|
def tukey(M, alpha=0.5, sym=True): |
|
r"""Return a Tukey window, also known as a tapered cosine window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
alpha : float, optional |
|
Shape parameter of the Tukey window, representing the fraction of the |
|
window inside the cosine tapered region. |
|
If zero, the Tukey window is equivalent to a rectangular window. |
|
If one, the Tukey window is equivalent to a Hann window. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
References |
|
---------- |
|
.. [1] Harris, Fredric J. (Jan 1978). "On the use of Windows for Harmonic |
|
Analysis with the Discrete Fourier Transform". Proceedings of the |
|
IEEE 66 (1): 51-83. :doi:`10.1109/PROC.1978.10837` |
|
.. [2] Wikipedia, "Window function", |
|
https://en.wikipedia.org/wiki/Window_function#Tukey_window |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.tukey(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Tukey window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
>>> plt.ylim([0, 1.1]) |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Tukey window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
|
|
if alpha <= 0: |
|
return np.ones(M, 'd') |
|
elif alpha >= 1.0: |
|
return hann(M, sym=sym) |
|
|
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(0, M) |
|
width = int(np.floor(alpha*(M-1)/2.0)) |
|
n1 = n[0:width+1] |
|
n2 = n[width+1:M-width-1] |
|
n3 = n[M-width-1:] |
|
|
|
w1 = 0.5 * (1 + np.cos(np.pi * (-1 + 2.0*n1/alpha/(M-1)))) |
|
w2 = np.ones(n2.shape) |
|
w3 = 0.5 * (1 + np.cos(np.pi * (-2.0/alpha + 1 + 2.0*n3/alpha/(M-1)))) |
|
|
|
w = np.concatenate((w1, w2, w3)) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def barthann(M, sym=True): |
|
"""Return a modified Bartlett-Hann window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.barthann(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Bartlett-Hann window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Bartlett-Hann window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(0, M) |
|
fac = np.abs(n / (M - 1.0) - 0.5) |
|
w = 0.62 - 0.48 * fac + 0.38 * np.cos(2 * np.pi * fac) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def general_hamming(M, alpha, sym=True): |
|
r"""Return a generalized Hamming window. |
|
|
|
The generalized Hamming window is constructed by multiplying a rectangular |
|
window by one period of a cosine function [1]_. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
alpha : float |
|
The window coefficient, :math:`\alpha` |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
See Also |
|
-------- |
|
hamming, hann |
|
|
|
Notes |
|
----- |
|
The generalized Hamming window is defined as |
|
|
|
.. math:: w(n) = \alpha - \left(1 - \alpha\right) |
|
\cos\left(\frac{2\pi{n}}{M-1}\right) \qquad 0 \leq n \leq M-1 |
|
|
|
Both the common Hamming window and Hann window are special cases of the |
|
generalized Hamming window with :math:`\alpha` = 0.54 and :math:`\alpha` = |
|
0.5, respectively [2]_. |
|
|
|
References |
|
---------- |
|
.. [1] DSPRelated, "Generalized Hamming Window Family", |
|
https://www.dsprelated.com/freebooks/sasp/Generalized_Hamming_Window_Family.html |
|
.. [2] Wikipedia, "Window function", |
|
https://en.wikipedia.org/wiki/Window_function |
|
.. [3] Riccardo Piantanida ESA, "Sentinel-1 Level 1 Detailed Algorithm |
|
Definition", |
|
https://sentinel.esa.int/documents/247904/1877131/Sentinel-1-Level-1-Detailed-Algorithm-Definition |
|
.. [4] Matthieu Bourbigot ESA, "Sentinel-1 Product Definition", |
|
https://sentinel.esa.int/documents/247904/1877131/Sentinel-1-Product-Definition |
|
|
|
Examples |
|
-------- |
|
The Sentinel-1A/B Instrument Processing Facility uses generalized Hamming |
|
windows in the processing of spaceborne Synthetic Aperture Radar (SAR) |
|
data [3]_. The facility uses various values for the :math:`\alpha` |
|
parameter based on operating mode of the SAR instrument. Some common |
|
:math:`\alpha` values include 0.75, 0.7 and 0.52 [4]_. As an example, we |
|
plot these different windows. |
|
|
|
>>> import numpy as np |
|
>>> from scipy.signal.windows import general_hamming |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> fig1, spatial_plot = plt.subplots() |
|
>>> spatial_plot.set_title("Generalized Hamming Windows") |
|
>>> spatial_plot.set_ylabel("Amplitude") |
|
>>> spatial_plot.set_xlabel("Sample") |
|
|
|
>>> fig2, freq_plot = plt.subplots() |
|
>>> freq_plot.set_title("Frequency Responses") |
|
>>> freq_plot.set_ylabel("Normalized magnitude [dB]") |
|
>>> freq_plot.set_xlabel("Normalized frequency [cycles per sample]") |
|
|
|
>>> for alpha in [0.75, 0.7, 0.52]: |
|
... window = general_hamming(41, alpha) |
|
... spatial_plot.plot(window, label="{:.2f}".format(alpha)) |
|
... A = fft(window, 2048) / (len(window)/2.0) |
|
... freq = np.linspace(-0.5, 0.5, len(A)) |
|
... response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
... freq_plot.plot(freq, response, label="{:.2f}".format(alpha)) |
|
>>> freq_plot.legend(loc="upper right") |
|
>>> spatial_plot.legend(loc="upper right") |
|
|
|
""" |
|
return general_cosine(M, [alpha, 1. - alpha], sym) |
|
|
|
|
|
def hamming(M, sym=True): |
|
r"""Return a Hamming window. |
|
|
|
The Hamming window is a taper formed by using a raised cosine with |
|
non-zero endpoints, optimized to minimize the nearest side lobe. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Hamming window is defined as |
|
|
|
.. math:: w(n) = 0.54 - 0.46 \cos\left(\frac{2\pi{n}}{M-1}\right) |
|
\qquad 0 \leq n \leq M-1 |
|
|
|
The Hamming was named for R. W. Hamming, an associate of J. W. Tukey and |
|
is described in Blackman and Tukey. It was recommended for smoothing the |
|
truncated autocovariance function in the time domain. |
|
Most references to the Hamming window come from the signal processing |
|
literature, where it is used as one of many windowing functions for |
|
smoothing values. It is also known as an apodization (which means |
|
"removing the foot", i.e. smoothing discontinuities at the beginning |
|
and end of the sampled signal) or tapering function. |
|
|
|
References |
|
---------- |
|
.. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power |
|
spectra, Dover Publications, New York. |
|
.. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The |
|
University of Alberta Press, 1975, pp. 109-110. |
|
.. [3] Wikipedia, "Window function", |
|
https://en.wikipedia.org/wiki/Window_function |
|
.. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, |
|
"Numerical Recipes", Cambridge University Press, 1986, page 425. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.hamming(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Hamming window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Hamming window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
|
|
return general_hamming(M, 0.54, sym) |
|
|
|
|
|
def kaiser(M, beta, sym=True): |
|
r"""Return a Kaiser window. |
|
|
|
The Kaiser window is a taper formed by using a Bessel function. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
beta : float |
|
Shape parameter, determines trade-off between main-lobe width and |
|
side lobe level. As beta gets large, the window narrows. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Kaiser window is defined as |
|
|
|
.. math:: w(n) = I_0\left( \beta \sqrt{1-\frac{4n^2}{(M-1)^2}} |
|
\right)/I_0(\beta) |
|
|
|
with |
|
|
|
.. math:: \quad -\frac{M-1}{2} \leq n \leq \frac{M-1}{2}, |
|
|
|
where :math:`I_0` is the modified zeroth-order Bessel function. |
|
|
|
The Kaiser was named for Jim Kaiser, who discovered a simple approximation |
|
to the DPSS window based on Bessel functions. |
|
The Kaiser window is a very good approximation to the discrete prolate |
|
spheroidal sequence, or Slepian window, which is the transform which |
|
maximizes the energy in the main lobe of the window relative to total |
|
energy. |
|
|
|
The Kaiser can approximate other windows by varying the beta parameter. |
|
(Some literature uses alpha = beta/pi.) [4]_ |
|
|
|
==== ======================= |
|
beta Window shape |
|
==== ======================= |
|
0 Rectangular |
|
5 Similar to a Hamming |
|
6 Similar to a Hann |
|
8.6 Similar to a Blackman |
|
==== ======================= |
|
|
|
A beta value of 14 is probably a good starting point. Note that as beta |
|
gets large, the window narrows, and so the number of samples needs to be |
|
large enough to sample the increasingly narrow spike, otherwise NaNs will |
|
be returned. |
|
|
|
Most references to the Kaiser window come from the signal processing |
|
literature, where it is used as one of many windowing functions for |
|
smoothing values. It is also known as an apodization (which means |
|
"removing the foot", i.e. smoothing discontinuities at the beginning |
|
and end of the sampled signal) or tapering function. |
|
|
|
References |
|
---------- |
|
.. [1] J. F. Kaiser, "Digital Filters" - Ch 7 in "Systems analysis by |
|
digital computer", Editors: F.F. Kuo and J.F. Kaiser, p 218-285. |
|
John Wiley and Sons, New York, (1966). |
|
.. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The |
|
University of Alberta Press, 1975, pp. 177-178. |
|
.. [3] Wikipedia, "Window function", |
|
https://en.wikipedia.org/wiki/Window_function |
|
.. [4] F. J. Harris, "On the use of windows for harmonic analysis with the |
|
discrete Fourier transform," Proceedings of the IEEE, vol. 66, |
|
no. 1, pp. 51-83, Jan. 1978. :doi:`10.1109/PROC.1978.10837`. |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.kaiser(51, beta=14) |
|
>>> plt.plot(window) |
|
>>> plt.title(r"Kaiser window ($\beta$=14)") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title(r"Frequency response of the Kaiser window ($\beta$=14)") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
|
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(0, M) |
|
alpha = (M - 1) / 2.0 |
|
w = (special.i0(beta * np.sqrt(1 - ((n - alpha) / alpha) ** 2.0)) / |
|
special.i0(beta)) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def kaiser_bessel_derived(M, beta, *, sym=True): |
|
"""Return a Kaiser-Bessel derived window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
Note that this window is only defined for an even |
|
number of points. |
|
beta : float |
|
Kaiser window shape parameter. |
|
sym : bool, optional |
|
This parameter only exists to comply with the interface offered by |
|
the other window functions and to be callable by `get_window`. |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, normalized to fulfil the Princen-Bradley condition. |
|
|
|
See Also |
|
-------- |
|
kaiser |
|
|
|
Notes |
|
----- |
|
It is designed to be suitable for use with the modified discrete cosine |
|
transform (MDCT) and is mainly used in audio signal processing and |
|
audio coding. |
|
|
|
.. versionadded:: 1.9.0 |
|
|
|
References |
|
---------- |
|
.. [1] Bosi, Marina, and Richard E. Goldberg. Introduction to Digital |
|
Audio Coding and Standards. Dordrecht: Kluwer, 2003. |
|
.. [2] Wikipedia, "Kaiser window", |
|
https://en.wikipedia.org/wiki/Kaiser_window |
|
|
|
Examples |
|
-------- |
|
Plot the Kaiser-Bessel derived window based on the wikipedia |
|
reference [2]_: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> import matplotlib.pyplot as plt |
|
>>> fig, ax = plt.subplots() |
|
>>> N = 50 |
|
>>> for alpha in [0.64, 2.55, 7.64, 31.83]: |
|
... ax.plot(signal.windows.kaiser_bessel_derived(2*N, np.pi*alpha), |
|
... label=f"{alpha=}") |
|
>>> ax.grid(True) |
|
>>> ax.set_title("Kaiser-Bessel derived window") |
|
>>> ax.set_ylabel("Amplitude") |
|
>>> ax.set_xlabel("Sample") |
|
>>> ax.set_xticks([0, N, 2*N-1]) |
|
>>> ax.set_xticklabels(["0", "N", "2N+1"]) # doctest: +SKIP |
|
>>> ax.set_yticks([0.0, 0.2, 0.4, 0.6, 0.707, 0.8, 1.0]) |
|
>>> fig.legend(loc="center") |
|
>>> fig.tight_layout() |
|
>>> fig.show() |
|
""" |
|
if not sym: |
|
raise ValueError( |
|
"Kaiser-Bessel Derived windows are only defined for symmetric " |
|
"shapes" |
|
) |
|
elif M < 1: |
|
return np.array([]) |
|
elif M % 2: |
|
raise ValueError( |
|
"Kaiser-Bessel Derived windows are only defined for even number " |
|
"of points" |
|
) |
|
|
|
kaiser_window = kaiser(M // 2 + 1, beta) |
|
csum = np.cumsum(kaiser_window) |
|
half_window = np.sqrt(csum[:-1] / csum[-1]) |
|
w = np.concatenate((half_window, half_window[::-1]), axis=0) |
|
return w |
|
|
|
|
|
def gaussian(M, std, sym=True): |
|
r"""Return a Gaussian window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
std : float |
|
The standard deviation, sigma. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Gaussian window is defined as |
|
|
|
.. math:: w(n) = e^{ -\frac{1}{2}\left(\frac{n}{\sigma}\right)^2 } |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.gaussian(51, std=7) |
|
>>> plt.plot(window) |
|
>>> plt.title(r"Gaussian window ($\sigma$=7)") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title(r"Frequency response of the Gaussian window ($\sigma$=7)") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(0, M) - (M - 1.0) / 2.0 |
|
sig2 = 2 * std * std |
|
w = np.exp(-n ** 2 / sig2) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def general_gaussian(M, p, sig, sym=True): |
|
r"""Return a window with a generalized Gaussian shape. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
p : float |
|
Shape parameter. p = 1 is identical to `gaussian`, p = 0.5 is |
|
the same shape as the Laplace distribution. |
|
sig : float |
|
The standard deviation, sigma. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The generalized Gaussian window is defined as |
|
|
|
.. math:: w(n) = e^{ -\frac{1}{2}\left|\frac{n}{\sigma}\right|^{2p} } |
|
|
|
the half-power point is at |
|
|
|
.. math:: (2 \log(2))^{1/(2 p)} \sigma |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.general_gaussian(51, p=1.5, sig=7) |
|
>>> plt.plot(window) |
|
>>> plt.title(r"Generalized Gaussian window (p=1.5, $\sigma$=7)") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title(r"Freq. resp. of the gen. Gaussian " |
|
... r"window (p=1.5, $\sigma$=7)") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
n = np.arange(0, M) - (M - 1.0) / 2.0 |
|
w = np.exp(-0.5 * np.abs(n / sig) ** (2 * p)) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
|
|
def chebwin(M, at, sym=True): |
|
r"""Return a Dolph-Chebyshev window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
at : float |
|
Attenuation (in dB). |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value always normalized to 1 |
|
|
|
Notes |
|
----- |
|
This window optimizes for the narrowest main lobe width for a given order |
|
`M` and sidelobe equiripple attenuation `at`, using Chebyshev |
|
polynomials. It was originally developed by Dolph to optimize the |
|
directionality of radio antenna arrays. |
|
|
|
Unlike most windows, the Dolph-Chebyshev is defined in terms of its |
|
frequency response: |
|
|
|
.. math:: W(k) = \frac |
|
{\cos\{M \cos^{-1}[\beta \cos(\frac{\pi k}{M})]\}} |
|
{\cosh[M \cosh^{-1}(\beta)]} |
|
|
|
where |
|
|
|
.. math:: \beta = \cosh \left [\frac{1}{M} |
|
\cosh^{-1}(10^\frac{A}{20}) \right ] |
|
|
|
and 0 <= abs(k) <= M-1. A is the attenuation in decibels (`at`). |
|
|
|
The time domain window is then generated using the IFFT, so |
|
power-of-two `M` are the fastest to generate, and prime number `M` are |
|
the slowest. |
|
|
|
The equiripple condition in the frequency domain creates impulses in the |
|
time domain, which appear at the ends of the window. |
|
|
|
References |
|
---------- |
|
.. [1] C. Dolph, "A current distribution for broadside arrays which |
|
optimizes the relationship between beam width and side-lobe level", |
|
Proceedings of the IEEE, Vol. 34, Issue 6 |
|
.. [2] Peter Lynch, "The Dolph-Chebyshev Window: A Simple Optimal Filter", |
|
American Meteorological Society (April 1997) |
|
http://mathsci.ucd.ie/~plynch/Publications/Dolph.pdf |
|
.. [3] F. J. Harris, "On the use of windows for harmonic analysis with the |
|
discrete Fourier transforms", Proceedings of the IEEE, Vol. 66, |
|
No. 1, January 1978 |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.chebwin(51, at=100) |
|
>>> plt.plot(window) |
|
>>> plt.title("Dolph-Chebyshev window (100 dB)") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Dolph-Chebyshev window (100 dB)") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if np.abs(at) < 45: |
|
warnings.warn("This window is not suitable for spectral analysis " |
|
"for attenuation values lower than about 45dB because " |
|
"the equivalent noise bandwidth of a Chebyshev window " |
|
"does not grow monotonically with increasing sidelobe " |
|
"attenuation when the attenuation is smaller than " |
|
"about 45 dB.", |
|
stacklevel=2) |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
|
|
order = M - 1.0 |
|
beta = np.cosh(1.0 / order * np.arccosh(10 ** (np.abs(at) / 20.))) |
|
k = np.r_[0:M] * 1.0 |
|
x = beta * np.cos(np.pi * k / M) |
|
|
|
|
|
|
|
p = np.zeros(x.shape) |
|
p[x > 1] = np.cosh(order * np.arccosh(x[x > 1])) |
|
p[x < -1] = (2 * (M % 2) - 1) * np.cosh(order * np.arccosh(-x[x < -1])) |
|
p[np.abs(x) <= 1] = np.cos(order * np.arccos(x[np.abs(x) <= 1])) |
|
|
|
|
|
|
|
if M % 2: |
|
w = np.real(sp_fft.fft(p)) |
|
n = (M + 1) // 2 |
|
w = w[:n] |
|
w = np.concatenate((w[n - 1:0:-1], w)) |
|
else: |
|
p = p * np.exp(1.j * np.pi / M * np.r_[0:M]) |
|
w = np.real(sp_fft.fft(p)) |
|
n = M // 2 + 1 |
|
w = np.concatenate((w[n - 1:0:-1], w[1:n])) |
|
w = w / max(w) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def cosine(M, sym=True): |
|
"""Return a window with a simple cosine shape. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
|
|
.. versionadded:: 0.13.0 |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.cosine(51) |
|
>>> plt.plot(window) |
|
>>> plt.title("Cosine window") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2047) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the cosine window") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
>>> plt.show() |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
w = np.sin(np.pi / M * (np.arange(0, M) + .5)) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def exponential(M, center=None, tau=1., sym=True): |
|
r"""Return an exponential (or Poisson) window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
center : float, optional |
|
Parameter defining the center location of the window function. |
|
The default value if not given is ``center = (M-1) / 2``. This |
|
parameter must take its default value for symmetric windows. |
|
tau : float, optional |
|
Parameter defining the decay. For ``center = 0`` use |
|
``tau = -(M-1) / ln(x)`` if ``x`` is the fraction of the window |
|
remaining at the end. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Exponential window is defined as |
|
|
|
.. math:: w(n) = e^{-|n-center| / \tau} |
|
|
|
References |
|
---------- |
|
.. [1] S. Gade and H. Herlufsen, "Windows to FFT analysis (Part I)", |
|
Technical Review 3, Bruel & Kjaer, 1987. |
|
|
|
Examples |
|
-------- |
|
Plot the symmetric window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> M = 51 |
|
>>> tau = 3.0 |
|
>>> window = signal.windows.exponential(M, tau=tau) |
|
>>> plt.plot(window) |
|
>>> plt.title("Exponential Window (tau=3.0)") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -35, 0]) |
|
>>> plt.title("Frequency response of the Exponential window (tau=3.0)") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
This function can also generate non-symmetric windows: |
|
|
|
>>> tau2 = -(M-1) / np.log(0.01) |
|
>>> window2 = signal.windows.exponential(M, 0, tau2, False) |
|
>>> plt.figure() |
|
>>> plt.plot(window2) |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
""" |
|
if sym and center is not None: |
|
raise ValueError("If sym==True, center must be None.") |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
if center is None: |
|
center = (M-1) / 2 |
|
|
|
n = np.arange(0, M) |
|
w = np.exp(-np.abs(n-center) / tau) |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def taylor(M, nbar=4, sll=30, norm=True, sym=True): |
|
""" |
|
Return a Taylor window. |
|
|
|
The Taylor window taper function approximates the Dolph-Chebyshev window's |
|
constant sidelobe level for a parameterized number of near-in sidelobes, |
|
but then allows a taper beyond [2]_. |
|
|
|
The SAR (synthetic aperture radar) community commonly uses Taylor |
|
weighting for image formation processing because it provides strong, |
|
selectable sidelobe suppression with minimum broadening of the |
|
mainlobe [1]_. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
nbar : int, optional |
|
Number of nearly constant level sidelobes adjacent to the mainlobe. |
|
sll : float, optional |
|
Desired suppression of sidelobe level in decibels (dB) relative to the |
|
DC gain of the mainlobe. This should be a positive number. |
|
norm : bool, optional |
|
When True (default), divides the window by the largest (middle) value |
|
for odd-length windows or the value that would occur between the two |
|
repeated middle values for even-length windows such that all values |
|
are less than or equal to 1. When False the DC gain will remain at 1 |
|
(0 dB) and the sidelobes will be `sll` dB down. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
out : array |
|
The window. When `norm` is True (default), the maximum value is |
|
normalized to 1 (though the value 1 does not appear if `M` is |
|
even and `sym` is True). |
|
|
|
See Also |
|
-------- |
|
chebwin, kaiser, bartlett, blackman, hamming, hann |
|
|
|
References |
|
---------- |
|
.. [1] W. Carrara, R. Goodman, and R. Majewski, "Spotlight Synthetic |
|
Aperture Radar: Signal Processing Algorithms" Pages 512-513, |
|
July 1995. |
|
.. [2] Armin Doerry, "Catalog of Window Taper Functions for |
|
Sidelobe Control", 2017. |
|
https://www.researchgate.net/profile/Armin_Doerry/publication/316281181_Catalog_of_Window_Taper_Functions_for_Sidelobe_Control/links/58f92cb2a6fdccb121c9d54d/Catalog-of-Window-Taper-Functions-for-Sidelobe-Control.pdf |
|
|
|
Examples |
|
-------- |
|
Plot the window and its frequency response: |
|
|
|
>>> import numpy as np |
|
>>> from scipy import signal |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
|
|
>>> window = signal.windows.taylor(51, nbar=20, sll=100, norm=False) |
|
>>> plt.plot(window) |
|
>>> plt.title("Taylor window (100 dB)") |
|
>>> plt.ylabel("Amplitude") |
|
>>> plt.xlabel("Sample") |
|
|
|
>>> plt.figure() |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> plt.plot(freq, response) |
|
>>> plt.axis([-0.5, 0.5, -120, 0]) |
|
>>> plt.title("Frequency response of the Taylor window (100 dB)") |
|
>>> plt.ylabel("Normalized magnitude [dB]") |
|
>>> plt.xlabel("Normalized frequency [cycles per sample]") |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
|
|
|
|
|
|
B = 10**(sll / 20) |
|
A = np.arccosh(B) / np.pi |
|
s2 = nbar**2 / (A**2 + (nbar - 0.5)**2) |
|
ma = np.arange(1, nbar) |
|
|
|
Fm = np.empty(nbar-1) |
|
signs = np.empty_like(ma) |
|
signs[::2] = 1 |
|
signs[1::2] = -1 |
|
m2 = ma*ma |
|
for mi, m in enumerate(ma): |
|
numer = signs[mi] * np.prod(1 - m2[mi]/s2/(A**2 + (ma - 0.5)**2)) |
|
denom = 2 * np.prod(1 - m2[mi]/m2[:mi]) * np.prod(1 - m2[mi]/m2[mi+1:]) |
|
Fm[mi] = numer / denom |
|
|
|
def W(n): |
|
return 1 + 2*np.dot(Fm, np.cos( |
|
2*np.pi*ma[:, np.newaxis]*(n-M/2.+0.5)/M)) |
|
|
|
w = W(np.arange(M)) |
|
|
|
|
|
if norm: |
|
scale = 1.0 / W((M - 1) / 2) |
|
w *= scale |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def dpss(M, NW, Kmax=None, sym=True, norm=None, return_ratios=False): |
|
""" |
|
Compute the Discrete Prolate Spheroidal Sequences (DPSS). |
|
|
|
DPSS (or Slepian sequences) are often used in multitaper power spectral |
|
density estimation (see [1]_). The first window in the sequence can be |
|
used to maximize the energy concentration in the main lobe, and is also |
|
called the Slepian window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Window length. |
|
NW : float |
|
Standardized half bandwidth corresponding to ``2*NW = BW/f0 = BW*M*dt`` |
|
where ``dt`` is taken as 1. |
|
Kmax : int | None, optional |
|
Number of DPSS windows to return (orders ``0`` through ``Kmax-1``). |
|
If None (default), return only a single window of shape ``(M,)`` |
|
instead of an array of windows of shape ``(Kmax, M)``. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
norm : {2, 'approximate', 'subsample'} | None, optional |
|
If 'approximate' or 'subsample', then the windows are normalized by the |
|
maximum, and a correction scale-factor for even-length windows |
|
is applied either using ``M**2/(M**2+NW)`` ("approximate") or |
|
a FFT-based subsample shift ("subsample"), see Notes for details. |
|
If None, then "approximate" is used when ``Kmax=None`` and 2 otherwise |
|
(which uses the l2 norm). |
|
return_ratios : bool, optional |
|
If True, also return the concentration ratios in addition to the |
|
windows. |
|
|
|
Returns |
|
------- |
|
v : ndarray, shape (Kmax, M) or (M,) |
|
The DPSS windows. Will be 1D if `Kmax` is None. |
|
r : ndarray, shape (Kmax,) or float, optional |
|
The concentration ratios for the windows. Only returned if |
|
`return_ratios` evaluates to True. Will be 0D if `Kmax` is None. |
|
|
|
Notes |
|
----- |
|
This computation uses the tridiagonal eigenvector formulation given |
|
in [2]_. |
|
|
|
The default normalization for ``Kmax=None``, i.e. window-generation mode, |
|
simply using the l-infinity norm would create a window with two unity |
|
values, which creates slight normalization differences between even and odd |
|
orders. The approximate correction of ``M**2/float(M**2+NW)`` for even |
|
sample numbers is used to counteract this effect (see Examples below). |
|
|
|
For very long signals (e.g., 1e6 elements), it can be useful to compute |
|
windows orders of magnitude shorter and use interpolation (e.g., |
|
`scipy.interpolate.interp1d`) to obtain tapers of length `M`, |
|
but this in general will not preserve orthogonality between the tapers. |
|
|
|
.. versionadded:: 1.1 |
|
|
|
References |
|
---------- |
|
.. [1] Percival DB, Walden WT. Spectral Analysis for Physical Applications: |
|
Multitaper and Conventional Univariate Techniques. |
|
Cambridge University Press; 1993. |
|
.. [2] Slepian, D. Prolate spheroidal wave functions, Fourier analysis, and |
|
uncertainty V: The discrete case. Bell System Technical Journal, |
|
Volume 57 (1978), 1371430. |
|
.. [3] Kaiser, JF, Schafer RW. On the Use of the I0-Sinh Window for |
|
Spectrum Analysis. IEEE Transactions on Acoustics, Speech and |
|
Signal Processing. ASSP-28 (1): 105-107; 1980. |
|
|
|
Examples |
|
-------- |
|
We can compare the window to `kaiser`, which was invented as an alternative |
|
that was easier to calculate [3]_ (example adapted from |
|
`here <https://ccrma.stanford.edu/~jos/sasp/Kaiser_DPSS_Windows_Compared.html>`_): |
|
|
|
>>> import numpy as np |
|
>>> import matplotlib.pyplot as plt |
|
>>> from scipy.signal import windows, freqz |
|
>>> M = 51 |
|
>>> fig, axes = plt.subplots(3, 2, figsize=(5, 7)) |
|
>>> for ai, alpha in enumerate((1, 3, 5)): |
|
... win_dpss = windows.dpss(M, alpha) |
|
... beta = alpha*np.pi |
|
... win_kaiser = windows.kaiser(M, beta) |
|
... for win, c in ((win_dpss, 'k'), (win_kaiser, 'r')): |
|
... win /= win.sum() |
|
... axes[ai, 0].plot(win, color=c, lw=1.) |
|
... axes[ai, 0].set(xlim=[0, M-1], title=r'$\\alpha$ = %s' % alpha, |
|
... ylabel='Amplitude') |
|
... w, h = freqz(win) |
|
... axes[ai, 1].plot(w, 20 * np.log10(np.abs(h)), color=c, lw=1.) |
|
... axes[ai, 1].set(xlim=[0, np.pi], |
|
... title=r'$\\beta$ = %0.2f' % beta, |
|
... ylabel='Magnitude (dB)') |
|
>>> for ax in axes.ravel(): |
|
... ax.grid(True) |
|
>>> axes[2, 1].legend(['DPSS', 'Kaiser']) |
|
>>> fig.tight_layout() |
|
>>> plt.show() |
|
|
|
And here are examples of the first four windows, along with their |
|
concentration ratios: |
|
|
|
>>> M = 512 |
|
>>> NW = 2.5 |
|
>>> win, eigvals = windows.dpss(M, NW, 4, return_ratios=True) |
|
>>> fig, ax = plt.subplots(1) |
|
>>> ax.plot(win.T, linewidth=1.) |
|
>>> ax.set(xlim=[0, M-1], ylim=[-0.1, 0.1], xlabel='Samples', |
|
... title='DPSS, M=%d, NW=%0.1f' % (M, NW)) |
|
>>> ax.legend(['win[%d] (%0.4f)' % (ii, ratio) |
|
... for ii, ratio in enumerate(eigvals)]) |
|
>>> fig.tight_layout() |
|
>>> plt.show() |
|
|
|
Using a standard :math:`l_{\\infty}` norm would produce two unity values |
|
for even `M`, but only one unity value for odd `M`. This produces uneven |
|
window power that can be counteracted by the approximate correction |
|
``M**2/float(M**2+NW)``, which can be selected by using |
|
``norm='approximate'`` (which is the same as ``norm=None`` when |
|
``Kmax=None``, as is the case here). Alternatively, the slower |
|
``norm='subsample'`` can be used, which uses subsample shifting in the |
|
frequency domain (FFT) to compute the correction: |
|
|
|
>>> Ms = np.arange(1, 41) |
|
>>> factors = (50, 20, 10, 5, 2.0001) |
|
>>> energy = np.empty((3, len(Ms), len(factors))) |
|
>>> for mi, M in enumerate(Ms): |
|
... for fi, factor in enumerate(factors): |
|
... NW = M / float(factor) |
|
... # Corrected using empirical approximation (default) |
|
... win = windows.dpss(M, NW) |
|
... energy[0, mi, fi] = np.sum(win ** 2) / np.sqrt(M) |
|
... # Corrected using subsample shifting |
|
... win = windows.dpss(M, NW, norm='subsample') |
|
... energy[1, mi, fi] = np.sum(win ** 2) / np.sqrt(M) |
|
... # Uncorrected (using l-infinity norm) |
|
... win /= win.max() |
|
... energy[2, mi, fi] = np.sum(win ** 2) / np.sqrt(M) |
|
>>> fig, ax = plt.subplots(1) |
|
>>> hs = ax.plot(Ms, energy[2], '-o', markersize=4, |
|
... markeredgecolor='none') |
|
>>> leg = [hs[-1]] |
|
>>> for hi, hh in enumerate(hs): |
|
... h1 = ax.plot(Ms, energy[0, :, hi], '-o', markersize=4, |
|
... color=hh.get_color(), markeredgecolor='none', |
|
... alpha=0.66) |
|
... h2 = ax.plot(Ms, energy[1, :, hi], '-o', markersize=4, |
|
... color=hh.get_color(), markeredgecolor='none', |
|
... alpha=0.33) |
|
... if hi == len(hs) - 1: |
|
... leg.insert(0, h1[0]) |
|
... leg.insert(0, h2[0]) |
|
>>> ax.set(xlabel='M (samples)', ylabel=r'Power / $\\sqrt{M}$') |
|
>>> ax.legend(leg, ['Uncorrected', r'Corrected: $\\frac{M^2}{M^2+NW}$', |
|
... 'Corrected (subsample)']) |
|
>>> fig.tight_layout() |
|
|
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
if norm is None: |
|
norm = 'approximate' if Kmax is None else 2 |
|
known_norms = (2, 'approximate', 'subsample') |
|
if norm not in known_norms: |
|
raise ValueError(f'norm must be one of {known_norms}, got {norm}') |
|
if Kmax is None: |
|
singleton = True |
|
Kmax = 1 |
|
else: |
|
singleton = False |
|
Kmax = operator.index(Kmax) |
|
if not 0 < Kmax <= M: |
|
raise ValueError('Kmax must be greater than 0 and less than M') |
|
if NW >= M/2.: |
|
raise ValueError('NW must be less than M/2.') |
|
if NW <= 0: |
|
raise ValueError('NW must be positive') |
|
M, needs_trunc = _extend(M, sym) |
|
W = float(NW) / M |
|
nidx = np.arange(M) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
d = ((M - 1 - 2 * nidx) / 2.) ** 2 * np.cos(2 * np.pi * W) |
|
e = nidx[1:] * (M - nidx[1:]) / 2. |
|
|
|
|
|
w, windows = linalg.eigh_tridiagonal( |
|
d, e, select='i', select_range=(M - Kmax, M - 1)) |
|
w = w[::-1] |
|
windows = windows[:, ::-1].T |
|
|
|
|
|
|
|
fix_even = (windows[::2].sum(axis=1) < 0) |
|
for i, f in enumerate(fix_even): |
|
if f: |
|
windows[2 * i] *= -1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
thresh = max(1e-7, 1. / M) |
|
for i, w in enumerate(windows[1::2]): |
|
if w[w * w > thresh][0] < 0: |
|
windows[2 * i + 1] *= -1 |
|
|
|
|
|
|
|
if return_ratios: |
|
dpss_rxx = _fftautocorr(windows) |
|
r = 4 * W * np.sinc(2 * W * nidx) |
|
r[0] = 2 * W |
|
ratios = np.dot(dpss_rxx, r) |
|
if singleton: |
|
ratios = ratios[0] |
|
|
|
if norm != 2: |
|
windows /= windows.max() |
|
if M % 2 == 0: |
|
if norm == 'approximate': |
|
correction = M**2 / float(M**2 + NW) |
|
else: |
|
s = sp_fft.rfft(windows[0]) |
|
shift = -(1 - 1./M) * np.arange(1, M//2 + 1) |
|
s[1:] *= 2 * np.exp(-1j * np.pi * shift) |
|
correction = M / s.real.sum() |
|
windows *= correction |
|
|
|
if needs_trunc: |
|
windows = windows[:, :-1] |
|
if singleton: |
|
windows = windows[0] |
|
return (windows, ratios) if return_ratios else windows |
|
|
|
|
|
def lanczos(M, *, sym=True): |
|
r"""Return a Lanczos window also known as a sinc window. |
|
|
|
Parameters |
|
---------- |
|
M : int |
|
Number of points in the output window. If zero, an empty array |
|
is returned. An exception is thrown when it is negative. |
|
sym : bool, optional |
|
When True (default), generates a symmetric window, for use in filter |
|
design. |
|
When False, generates a periodic window, for use in spectral analysis. |
|
|
|
Returns |
|
------- |
|
w : ndarray |
|
The window, with the maximum value normalized to 1 (though the value 1 |
|
does not appear if `M` is even and `sym` is True). |
|
|
|
Notes |
|
----- |
|
The Lanczos window is defined as |
|
|
|
.. math:: w(n) = sinc \left( \frac{2n}{M - 1} - 1 \right) |
|
|
|
where |
|
|
|
.. math:: sinc(x) = \frac{\sin(\pi x)}{\pi x} |
|
|
|
The Lanczos window has reduced Gibbs oscillations and is widely used for |
|
filtering climate timeseries with good properties in the physical and |
|
spectral domains. |
|
|
|
.. versionadded:: 1.10 |
|
|
|
References |
|
---------- |
|
.. [1] Lanczos, C., and Teichmann, T. (1957). Applied analysis. |
|
Physics Today, 10, 44. |
|
.. [2] Duchon C. E. (1979) Lanczos Filtering in One and Two Dimensions. |
|
Journal of Applied Meteorology, Vol 18, pp 1016-1022. |
|
.. [3] Thomson, R. E. and Emery, W. J. (2014) Data Analysis Methods in |
|
Physical Oceanography (Third Edition), Elsevier, pp 593-637. |
|
.. [4] Wikipedia, "Window function", |
|
http://en.wikipedia.org/wiki/Window_function |
|
|
|
Examples |
|
-------- |
|
Plot the window |
|
|
|
>>> import numpy as np |
|
>>> from scipy.signal.windows import lanczos |
|
>>> from scipy.fft import fft, fftshift |
|
>>> import matplotlib.pyplot as plt |
|
>>> fig, ax = plt.subplots(1) |
|
>>> window = lanczos(51) |
|
>>> ax.plot(window) |
|
>>> ax.set_title("Lanczos window") |
|
>>> ax.set_ylabel("Amplitude") |
|
>>> ax.set_xlabel("Sample") |
|
>>> fig.tight_layout() |
|
>>> plt.show() |
|
|
|
and its frequency response: |
|
|
|
>>> fig, ax = plt.subplots(1) |
|
>>> A = fft(window, 2048) / (len(window)/2.0) |
|
>>> freq = np.linspace(-0.5, 0.5, len(A)) |
|
>>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max()))) |
|
>>> ax.plot(freq, response) |
|
>>> ax.set_xlim(-0.5, 0.5) |
|
>>> ax.set_ylim(-120, 0) |
|
>>> ax.set_title("Frequency response of the lanczos window") |
|
>>> ax.set_ylabel("Normalized magnitude [dB]") |
|
>>> ax.set_xlabel("Normalized frequency [cycles per sample]") |
|
>>> fig.tight_layout() |
|
>>> plt.show() |
|
""" |
|
if _len_guards(M): |
|
return np.ones(M) |
|
M, needs_trunc = _extend(M, sym) |
|
|
|
|
|
|
|
|
|
def _calc_right_side_lanczos(n, m): |
|
return np.sinc(2. * np.arange(n, m) / (m - 1) - 1.0) |
|
|
|
if M % 2 == 0: |
|
wh = _calc_right_side_lanczos(M/2, M) |
|
w = np.r_[np.flip(wh), wh] |
|
else: |
|
wh = _calc_right_side_lanczos((M+1)/2, M) |
|
w = np.r_[np.flip(wh), 1.0, wh] |
|
|
|
return _truncate(w, needs_trunc) |
|
|
|
|
|
def _fftautocorr(x): |
|
"""Compute the autocorrelation of a real array and crop the result.""" |
|
N = x.shape[-1] |
|
use_N = sp_fft.next_fast_len(2*N-1) |
|
x_fft = sp_fft.rfft(x, use_N, axis=-1) |
|
cxy = sp_fft.irfft(x_fft * x_fft.conj(), n=use_N)[:, :N] |
|
|
|
|
|
|
|
return cxy |
|
|
|
|
|
_win_equiv_raw = { |
|
('barthann', 'brthan', 'bth'): (barthann, False), |
|
('bartlett', 'bart', 'brt'): (bartlett, False), |
|
('blackman', 'black', 'blk'): (blackman, False), |
|
('blackmanharris', 'blackharr', 'bkh'): (blackmanharris, False), |
|
('bohman', 'bman', 'bmn'): (bohman, False), |
|
('boxcar', 'box', 'ones', |
|
'rect', 'rectangular'): (boxcar, False), |
|
('chebwin', 'cheb'): (chebwin, True), |
|
('cosine', 'halfcosine'): (cosine, False), |
|
('dpss',): (dpss, True), |
|
('exponential', 'poisson'): (exponential, False), |
|
('flattop', 'flat', 'flt'): (flattop, False), |
|
('gaussian', 'gauss', 'gss'): (gaussian, True), |
|
('general cosine', 'general_cosine'): (general_cosine, True), |
|
('general gaussian', 'general_gaussian', |
|
'general gauss', 'general_gauss', 'ggs'): (general_gaussian, True), |
|
('general hamming', 'general_hamming'): (general_hamming, True), |
|
('hamming', 'hamm', 'ham'): (hamming, False), |
|
('hann', 'han'): (hann, False), |
|
('kaiser', 'ksr'): (kaiser, True), |
|
('kaiser bessel derived', 'kbd'): (kaiser_bessel_derived, True), |
|
('lanczos', 'sinc'): (lanczos, False), |
|
('nuttall', 'nutl', 'nut'): (nuttall, False), |
|
('parzen', 'parz', 'par'): (parzen, False), |
|
('taylor', 'taylorwin'): (taylor, False), |
|
('triangle', 'triang', 'tri'): (triang, False), |
|
('tukey', 'tuk'): (tukey, False), |
|
} |
|
|
|
|
|
_win_equiv = {} |
|
for k, v in _win_equiv_raw.items(): |
|
for key in k: |
|
_win_equiv[key] = v[0] |
|
|
|
|
|
_needs_param = set() |
|
for k, v in _win_equiv_raw.items(): |
|
if v[1]: |
|
_needs_param.update(k) |
|
|
|
|
|
def get_window(window, Nx, fftbins=True): |
|
""" |
|
Return a window of a given length and type. |
|
|
|
Parameters |
|
---------- |
|
window : string, float, or tuple |
|
The type of window to create. See below for more details. |
|
Nx : int |
|
The number of samples in the window. |
|
fftbins : bool, optional |
|
If True (default), create a "periodic" window, ready to use with |
|
`ifftshift` and be multiplied by the result of an FFT (see also |
|
:func:`~scipy.fft.fftfreq`). |
|
If False, create a "symmetric" window, for use in filter design. |
|
|
|
Returns |
|
------- |
|
get_window : ndarray |
|
Returns a window of length `Nx` and type `window` |
|
|
|
Notes |
|
----- |
|
Window types: |
|
|
|
- `~scipy.signal.windows.boxcar` |
|
- `~scipy.signal.windows.triang` |
|
- `~scipy.signal.windows.blackman` |
|
- `~scipy.signal.windows.hamming` |
|
- `~scipy.signal.windows.hann` |
|
- `~scipy.signal.windows.bartlett` |
|
- `~scipy.signal.windows.flattop` |
|
- `~scipy.signal.windows.parzen` |
|
- `~scipy.signal.windows.bohman` |
|
- `~scipy.signal.windows.blackmanharris` |
|
- `~scipy.signal.windows.nuttall` |
|
- `~scipy.signal.windows.barthann` |
|
- `~scipy.signal.windows.cosine` |
|
- `~scipy.signal.windows.exponential` |
|
- `~scipy.signal.windows.tukey` |
|
- `~scipy.signal.windows.taylor` |
|
- `~scipy.signal.windows.lanczos` |
|
- `~scipy.signal.windows.kaiser` (needs beta) |
|
- `~scipy.signal.windows.kaiser_bessel_derived` (needs beta) |
|
- `~scipy.signal.windows.gaussian` (needs standard deviation) |
|
- `~scipy.signal.windows.general_cosine` (needs weighting coefficients) |
|
- `~scipy.signal.windows.general_gaussian` (needs power, width) |
|
- `~scipy.signal.windows.general_hamming` (needs window coefficient) |
|
- `~scipy.signal.windows.dpss` (needs normalized half-bandwidth) |
|
- `~scipy.signal.windows.chebwin` (needs attenuation) |
|
|
|
|
|
If the window requires no parameters, then `window` can be a string. |
|
|
|
If the window requires parameters, then `window` must be a tuple |
|
with the first argument the string name of the window, and the next |
|
arguments the needed parameters. |
|
|
|
If `window` is a floating point number, it is interpreted as the beta |
|
parameter of the `~scipy.signal.windows.kaiser` window. |
|
|
|
Each of the window types listed above is also the name of |
|
a function that can be called directly to create a window of |
|
that type. |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import signal |
|
>>> signal.get_window('triang', 7) |
|
array([ 0.125, 0.375, 0.625, 0.875, 0.875, 0.625, 0.375]) |
|
>>> signal.get_window(('kaiser', 4.0), 9) |
|
array([ 0.08848053, 0.29425961, 0.56437221, 0.82160913, 0.97885093, |
|
0.97885093, 0.82160913, 0.56437221, 0.29425961]) |
|
>>> signal.get_window(('exponential', None, 1.), 9) |
|
array([ 0.011109 , 0.03019738, 0.082085 , 0.22313016, 0.60653066, |
|
0.60653066, 0.22313016, 0.082085 , 0.03019738]) |
|
>>> signal.get_window(4.0, 9) |
|
array([ 0.08848053, 0.29425961, 0.56437221, 0.82160913, 0.97885093, |
|
0.97885093, 0.82160913, 0.56437221, 0.29425961]) |
|
|
|
""" |
|
sym = not fftbins |
|
try: |
|
beta = float(window) |
|
except (TypeError, ValueError) as e: |
|
args = () |
|
if isinstance(window, tuple): |
|
winstr = window[0] |
|
if len(window) > 1: |
|
args = window[1:] |
|
elif isinstance(window, str): |
|
if window in _needs_param: |
|
raise ValueError("The '" + window + "' window needs one or " |
|
"more parameters -- pass a tuple.") from e |
|
else: |
|
winstr = window |
|
else: |
|
raise ValueError( |
|
f"{str(type(window))} as window type is not supported.") from e |
|
|
|
try: |
|
winfunc = _win_equiv[winstr] |
|
except KeyError as e: |
|
raise ValueError("Unknown window type.") from e |
|
|
|
if winfunc is dpss: |
|
params = (Nx,) + args + (None,) |
|
else: |
|
params = (Nx,) + args |
|
else: |
|
winfunc = kaiser |
|
params = (Nx, beta) |
|
|
|
return winfunc(*params, sym=sym) |
|
|