_windows.py 93 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608
  1. """The suite of window functions."""
  2. import math
  3. import numbers
  4. import operator
  5. import warnings
  6. from scipy._lib import doccer
  7. from scipy import linalg, special, fft as sp_fft
  8. from scipy._lib.array_api_compat import numpy as np_compat
  9. from scipy._lib._array_api import array_namespace, xp_device
  10. from scipy._lib._array_api import xp_capabilities
  11. from scipy._lib import array_api_extra as xpx
  12. __all__ = ['boxcar', 'triang', 'parzen', 'bohman', 'blackman', 'nuttall',
  13. 'blackmanharris', 'flattop', 'bartlett', 'barthann',
  14. 'hamming', 'kaiser', 'kaiser_bessel_derived', 'gaussian',
  15. 'general_cosine', 'general_gaussian', 'general_hamming',
  16. 'chebwin', 'cosine', 'hann', 'exponential', 'tukey', 'taylor',
  17. 'dpss', 'get_window', 'lanczos']
  18. def _len_guards(M):
  19. """Handle small or incorrect window lengths"""
  20. if int(M) != M or M < 0:
  21. raise ValueError('Window length M must be a non-negative integer')
  22. return M <= 1
  23. def _extend(M, sym):
  24. """Extend window by 1 sample if needed for DFT-even symmetry"""
  25. if not sym:
  26. return M + 1, True
  27. else:
  28. return M, False
  29. def _truncate(w, needed):
  30. """Truncate window by 1 sample if needed for DFT-even symmetry"""
  31. if needed:
  32. return w[:-1]
  33. else:
  34. return w
  35. def _namespace(xp):
  36. """A shim for the `device` arg of `np.asarray(x, device=device)` and acos/arccos.
  37. Will be able to replace with `np_compat if xp is None else xp` when we drop
  38. support for numpy 1.x and cupy 13.x
  39. """
  40. return np_compat if xp is None else array_namespace(xp.empty(0))
  41. def _general_cosine_impl(M, a, xp, device, sym=True):
  42. if _len_guards(M):
  43. return xp.ones(M, dtype=xp.float64, device=device)
  44. M, needs_trunc = _extend(M, sym)
  45. fac = xp.linspace(-xp.pi, xp.pi, M, dtype=xp.float64, device=device)
  46. w = xp.zeros(M, dtype=xp.float64, device=device)
  47. for k in range(a.shape[0]):
  48. w += a[k] * xp.cos(k * fac)
  49. return _truncate(w, needs_trunc)
  50. @xp_capabilities()
  51. def general_cosine(M, a, sym=True):
  52. r"""
  53. Generic weighted sum of cosine terms window
  54. Parameters
  55. ----------
  56. M : int
  57. Number of points in the output window
  58. a : array_like
  59. Sequence of weighting coefficients. This uses the convention of being
  60. centered on the origin, so these will typically all be positive
  61. numbers, not alternating sign.
  62. sym : bool, optional
  63. When True (default), generates a symmetric window, for use in filter
  64. design.
  65. When False, generates a periodic window, for use in spectral analysis.
  66. Returns
  67. -------
  68. w : ndarray
  69. The array of window values.
  70. References
  71. ----------
  72. .. [1] A. Nuttall, "Some windows with very good sidelobe behavior," IEEE
  73. Transactions on Acoustics, Speech, and Signal Processing, vol. 29,
  74. no. 1, pp. 84-91, Feb 1981. :doi:`10.1109/TASSP.1981.1163506`.
  75. .. [2] Heinzel G. et al., "Spectrum and spectral density estimation by the
  76. Discrete Fourier transform (DFT), including a comprehensive list of
  77. window functions and some new flat-top windows", February 15, 2002
  78. https://holometer.fnal.gov/GH_FFT.pdf
  79. Examples
  80. --------
  81. Heinzel describes a flat-top window named "HFT90D" with formula: [2]_
  82. .. math:: w_j = 1 - 1.942604 \cos(z) + 1.340318 \cos(2z)
  83. - 0.440811 \cos(3z) + 0.043097 \cos(4z)
  84. where
  85. .. math:: z = \frac{2 \pi j}{N}, j = 0...N - 1
  86. Since this uses the convention of starting at the origin, to reproduce the
  87. window, we need to convert every other coefficient to a positive number:
  88. >>> HFT90D = [1, 1.942604, 1.340318, 0.440811, 0.043097]
  89. The paper states that the highest sidelobe is at -90.2 dB. Reproduce
  90. Figure 42 by plotting the window and its frequency response, and confirm
  91. the sidelobe level in red:
  92. >>> import numpy as np
  93. >>> from scipy.signal.windows import general_cosine
  94. >>> from scipy.fft import fft, fftshift
  95. >>> import matplotlib.pyplot as plt
  96. >>> window = general_cosine(1000, HFT90D, sym=False)
  97. >>> plt.plot(window)
  98. >>> plt.title("HFT90D window")
  99. >>> plt.ylabel("Amplitude")
  100. >>> plt.xlabel("Sample")
  101. >>> plt.figure()
  102. >>> A = fft(window, 10000) / (len(window)/2.0)
  103. >>> freq = np.linspace(-0.5, 0.5, len(A))
  104. >>> response = np.abs(fftshift(A / abs(A).max()))
  105. >>> response = 20 * np.log10(np.maximum(response, 1e-10))
  106. >>> plt.plot(freq, response)
  107. >>> plt.axis([-50/1000, 50/1000, -140, 0])
  108. >>> plt.title("Frequency response of the HFT90D window")
  109. >>> plt.ylabel("Normalized magnitude [dB]")
  110. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  111. >>> plt.axhline(-90.2, color='red')
  112. >>> plt.show()
  113. """
  114. xp = array_namespace(a)
  115. a = xp.asarray(a)
  116. device = xp_device(a)
  117. return _general_cosine_impl(M, a, xp, device, sym=sym)
  118. @xp_capabilities()
  119. def boxcar(M, sym=True, *, xp=None, device=None):
  120. """Return a boxcar or rectangular window.
  121. Also known as a rectangular window or Dirichlet window, this is equivalent
  122. to no window at all.
  123. Parameters
  124. ----------
  125. M : int
  126. Number of points in the output window. If zero, an empty array
  127. is returned. An exception is thrown when it is negative.
  128. sym : bool, optional
  129. Whether the window is symmetric. (Has no effect for boxcar.)
  130. %(xp_device_snippet)s
  131. Returns
  132. -------
  133. w : ndarray
  134. The window, with the maximum value normalized to 1.
  135. Examples
  136. --------
  137. Plot the window and its frequency response:
  138. >>> import numpy as np
  139. >>> from scipy import signal
  140. >>> from scipy.fft import fft, fftshift
  141. >>> import matplotlib.pyplot as plt
  142. >>> window = signal.windows.boxcar(51)
  143. >>> plt.plot(window)
  144. >>> plt.title("Boxcar window")
  145. >>> plt.ylabel("Amplitude")
  146. >>> plt.xlabel("Sample")
  147. >>> plt.figure()
  148. >>> A = fft(window, 2048) / (len(window)/2.0)
  149. >>> freq = np.linspace(-0.5, 0.5, len(A))
  150. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  151. >>> plt.plot(freq, response)
  152. >>> plt.axis([-0.5, 0.5, -120, 0])
  153. >>> plt.title("Frequency response of the boxcar window")
  154. >>> plt.ylabel("Normalized magnitude [dB]")
  155. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  156. """
  157. xp = _namespace(xp)
  158. if _len_guards(M):
  159. return xp.ones(M, dtype=xp.float64, device=device)
  160. M, needs_trunc = _extend(M, sym)
  161. w = xp.ones(M, dtype=xp.float64, device=device)
  162. return _truncate(w, needs_trunc)
  163. @xp_capabilities()
  164. def triang(M, sym=True, *, xp=None, device=None):
  165. """Return a triangular window.
  166. Parameters
  167. ----------
  168. M : int
  169. Number of points in the output window. If zero, an empty array
  170. is returned. An exception is thrown when it is negative.
  171. sym : bool, optional
  172. When True (default), generates a symmetric window, for use in filter
  173. design.
  174. When False, generates a periodic window, for use in spectral analysis.
  175. %(xp_device_snippet)s
  176. Returns
  177. -------
  178. w : ndarray
  179. The window, with the maximum value normalized to 1 (though the value 1
  180. does not appear if `M` is even and `sym` is True).
  181. See Also
  182. --------
  183. bartlett : A triangular window that touches zero
  184. Examples
  185. --------
  186. Plot the window and its frequency response:
  187. >>> import numpy as np
  188. >>> from scipy import signal
  189. >>> from scipy.fft import fft, fftshift
  190. >>> import matplotlib.pyplot as plt
  191. >>> window = signal.windows.triang(51)
  192. >>> plt.plot(window)
  193. >>> plt.title("Triangular window")
  194. >>> plt.ylabel("Amplitude")
  195. >>> plt.xlabel("Sample")
  196. >>> plt.figure()
  197. >>> A = fft(window, 2048) / (len(window)/2.0)
  198. >>> freq = np.linspace(-0.5, 0.5, len(A))
  199. >>> response = np.abs(fftshift(A / abs(A).max()))
  200. >>> response = 20 * np.log10(np.maximum(response, 1e-10))
  201. >>> plt.plot(freq, response)
  202. >>> plt.axis([-0.5, 0.5, -120, 0])
  203. >>> plt.title("Frequency response of the triangular window")
  204. >>> plt.ylabel("Normalized magnitude [dB]")
  205. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  206. """
  207. xp = _namespace(xp)
  208. if _len_guards(M):
  209. return xp.ones(M, dtype=xp.float64, device=device)
  210. M, needs_trunc = _extend(M, sym)
  211. n = xp.arange(1, (M + 1) // 2 + 1, dtype=xp.float64, device=device)
  212. if M % 2 == 0:
  213. w = (2 * n - 1.0) / M
  214. w = xp.concat([w, xp.flip(w)])
  215. else:
  216. w = 2 * n / (M + 1.0)
  217. w = xp.concat([w, xp.flip(w[:-1])])
  218. return _truncate(w, needs_trunc)
  219. @xp_capabilities()
  220. def parzen(M, sym=True, *, xp=None, device=None):
  221. """Return a Parzen window.
  222. Parameters
  223. ----------
  224. M : int
  225. Number of points in the output window. If zero, an empty array
  226. is returned. An exception is thrown when it is negative.
  227. sym : bool, optional
  228. When True (default), generates a symmetric window, for use in filter
  229. design.
  230. When False, generates a periodic window, for use in spectral analysis.
  231. %(xp_device_snippet)s
  232. Returns
  233. -------
  234. w : ndarray
  235. The window, with the maximum value normalized to 1 (though the value 1
  236. does not appear if `M` is even and `sym` is True).
  237. References
  238. ----------
  239. .. [1] E. Parzen, "Mathematical Considerations in the Estimation of
  240. Spectra", Technometrics, Vol. 3, No. 2 (May, 1961), pp. 167-190
  241. Examples
  242. --------
  243. Plot the window and its frequency response:
  244. >>> import numpy as np
  245. >>> from scipy import signal
  246. >>> from scipy.fft import fft, fftshift
  247. >>> import matplotlib.pyplot as plt
  248. >>> window = signal.windows.parzen(51)
  249. >>> plt.plot(window)
  250. >>> plt.title("Parzen window")
  251. >>> plt.ylabel("Amplitude")
  252. >>> plt.xlabel("Sample")
  253. >>> plt.figure()
  254. >>> A = fft(window, 2048) / (len(window)/2.0)
  255. >>> freq = np.linspace(-0.5, 0.5, len(A))
  256. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  257. >>> plt.plot(freq, response)
  258. >>> plt.axis([-0.5, 0.5, -120, 0])
  259. >>> plt.title("Frequency response of the Parzen window")
  260. >>> plt.ylabel("Normalized magnitude [dB]")
  261. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  262. """
  263. xp = _namespace(xp)
  264. if _len_guards(M):
  265. return xp.ones(M, dtype=xp.float64, device=device)
  266. M, needs_trunc = _extend(M, sym)
  267. n = xp.arange(-(M - 1) / 2.0, (M - 1) / 2.0 + 0.5, 1.0,
  268. dtype=xp.float64, device=device)
  269. w = xp.where(abs(n) <= (M - 1) / 4.0,
  270. (1 - 6 * (abs(n) / (M / 2.0)) ** 2.0 +
  271. 6 * (abs(n) / (M / 2.0)) ** 3.0),
  272. 2 * (1 - abs(n) / (M / 2.0)) ** 3.0)
  273. return _truncate(w, needs_trunc)
  274. @xp_capabilities()
  275. def bohman(M, sym=True, *, xp=None, device=None):
  276. """Return a Bohman window.
  277. Parameters
  278. ----------
  279. M : int
  280. Number of points in the output window. If zero, an empty array
  281. is returned. An exception is thrown when it is negative.
  282. sym : bool, optional
  283. When True (default), generates a symmetric window, for use in filter
  284. design.
  285. When False, generates a periodic window, for use in spectral analysis.
  286. %(xp_device_snippet)s
  287. Returns
  288. -------
  289. w : ndarray
  290. The window, with the maximum value normalized to 1 (though the value 1
  291. does not appear if `M` is even and `sym` is True).
  292. Examples
  293. --------
  294. Plot the window and its frequency response:
  295. >>> import numpy as np
  296. >>> from scipy import signal
  297. >>> from scipy.fft import fft, fftshift
  298. >>> import matplotlib.pyplot as plt
  299. >>> window = signal.windows.bohman(51)
  300. >>> plt.plot(window)
  301. >>> plt.title("Bohman window")
  302. >>> plt.ylabel("Amplitude")
  303. >>> plt.xlabel("Sample")
  304. >>> plt.figure()
  305. >>> A = fft(window, 2047) / (len(window)/2.0)
  306. >>> freq = np.linspace(-0.5, 0.5, len(A))
  307. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  308. >>> plt.plot(freq, response)
  309. >>> plt.axis([-0.5, 0.5, -120, 0])
  310. >>> plt.title("Frequency response of the Bohman window")
  311. >>> plt.ylabel("Normalized magnitude [dB]")
  312. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  313. """
  314. xp = _namespace(xp)
  315. if _len_guards(M):
  316. return xp.ones(M, dtype=xp.float64, device=device)
  317. M, needs_trunc = _extend(M, sym)
  318. fac = abs(xp.linspace(-1, 1, M, dtype=xp.float64, device=device)[1:-1])
  319. w = (1 - fac) * xp.cos(xp.pi * fac) + 1.0 / xp.pi * xp.sin(xp.pi * fac)
  320. one = xp.zeros(1, dtype=xp.float64, device=device)
  321. w = xp.concat([one, w, one])
  322. return _truncate(w, needs_trunc)
  323. @xp_capabilities()
  324. def blackman(M, sym=True, *, xp=None, device=None):
  325. r"""
  326. Return a Blackman window.
  327. The Blackman window is a taper formed by using the first three terms of
  328. a summation of cosines. It was designed to have close to the minimal
  329. leakage possible. It is close to optimal, only slightly worse than a
  330. Kaiser window.
  331. Parameters
  332. ----------
  333. M : int
  334. Number of points in the output window. If zero, an empty array
  335. is returned. An exception is thrown when it is negative.
  336. sym : bool, optional
  337. When True (default), generates a symmetric window, for use in filter
  338. design.
  339. When False, generates a periodic window, for use in spectral analysis.
  340. %(xp_device_snippet)s
  341. Returns
  342. -------
  343. w : ndarray
  344. The window, with the maximum value normalized to 1 (though the value 1
  345. does not appear if `M` is even and `sym` is True).
  346. Notes
  347. -----
  348. The Blackman window is defined as
  349. .. math:: w(n) = 0.42 - 0.5 \cos(2\pi n/M) + 0.08 \cos(4\pi n/M)
  350. The "exact Blackman" window was designed to null out the third and fourth
  351. sidelobes, but has discontinuities at the boundaries, resulting in a
  352. 6 dB/oct fall-off. This window is an approximation of the "exact" window,
  353. which does not null the sidelobes as well, but is smooth at the edges,
  354. improving the fall-off rate to 18 dB/oct. [3]_
  355. Most references to the Blackman window come from the signal processing
  356. literature, where it is used as one of many windowing functions for
  357. smoothing values. It is also known as an apodization (which means
  358. "removing the foot", i.e. smoothing discontinuities at the beginning
  359. and end of the sampled signal) or tapering function. It is known as a
  360. "near optimal" tapering function, almost as good (by some measures)
  361. as the Kaiser window.
  362. References
  363. ----------
  364. .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power
  365. spectra, Dover Publications, New York.
  366. .. [2] Oppenheim, A.V., and R.W. Schafer. Discrete-Time Signal Processing.
  367. Upper Saddle River, NJ: Prentice-Hall, 1999, pp. 468-471.
  368. .. [3] Harris, Fredric J. (Jan 1978). "On the use of Windows for Harmonic
  369. Analysis with the Discrete Fourier Transform". Proceedings of the
  370. IEEE 66 (1): 51-83. :doi:`10.1109/PROC.1978.10837`.
  371. Examples
  372. --------
  373. Plot the window and its frequency response:
  374. >>> import numpy as np
  375. >>> from scipy import signal
  376. >>> from scipy.fft import fft, fftshift
  377. >>> import matplotlib.pyplot as plt
  378. >>> window = signal.windows.blackman(51)
  379. >>> plt.plot(window)
  380. >>> plt.title("Blackman window")
  381. >>> plt.ylabel("Amplitude")
  382. >>> plt.xlabel("Sample")
  383. >>> plt.figure()
  384. >>> A = fft(window, 2048) / (len(window)/2.0)
  385. >>> freq = np.linspace(-0.5, 0.5, len(A))
  386. >>> response = np.abs(fftshift(A / abs(A).max()))
  387. >>> response = 20 * np.log10(np.maximum(response, 1e-10))
  388. >>> plt.plot(freq, response)
  389. >>> plt.axis([-0.5, 0.5, -120, 0])
  390. >>> plt.title("Frequency response of the Blackman window")
  391. >>> plt.ylabel("Normalized magnitude [dB]")
  392. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  393. """
  394. # Docstring adapted from NumPy's blackman function
  395. xp = _namespace(xp)
  396. a = xp.asarray([0.42, 0.50, 0.08], dtype=xp.float64, device=device)
  397. device = xp_device(a)
  398. return _general_cosine_impl(M, a, xp, device, sym=sym)
  399. @xp_capabilities()
  400. def nuttall(M, sym=True, *, xp=None, device=None):
  401. """Return a minimum 4-term Blackman-Harris window according to Nuttall.
  402. This variation is called "Nuttall4c" by Heinzel. [2]_
  403. Parameters
  404. ----------
  405. M : int
  406. Number of points in the output window. If zero, an empty array
  407. is returned. An exception is thrown when it is negative.
  408. sym : bool, optional
  409. When True (default), generates a symmetric window, for use in filter
  410. design.
  411. When False, generates a periodic window, for use in spectral analysis.
  412. %(xp_device_snippet)s
  413. Returns
  414. -------
  415. w : ndarray
  416. The window, with the maximum value normalized to 1 (though the value 1
  417. does not appear if `M` is even and `sym` is True).
  418. References
  419. ----------
  420. .. [1] A. Nuttall, "Some windows with very good sidelobe behavior," IEEE
  421. Transactions on Acoustics, Speech, and Signal Processing, vol. 29,
  422. no. 1, pp. 84-91, Feb 1981. :doi:`10.1109/TASSP.1981.1163506`.
  423. .. [2] Heinzel G. et al., "Spectrum and spectral density estimation by the
  424. Discrete Fourier transform (DFT), including a comprehensive list of
  425. window functions and some new flat-top windows", February 15, 2002
  426. https://holometer.fnal.gov/GH_FFT.pdf
  427. Examples
  428. --------
  429. Plot the window and its frequency response:
  430. >>> import numpy as np
  431. >>> from scipy import signal
  432. >>> from scipy.fft import fft, fftshift
  433. >>> import matplotlib.pyplot as plt
  434. >>> window = signal.windows.nuttall(51)
  435. >>> plt.plot(window)
  436. >>> plt.title("Nuttall window")
  437. >>> plt.ylabel("Amplitude")
  438. >>> plt.xlabel("Sample")
  439. >>> plt.figure()
  440. >>> A = fft(window, 2048) / (len(window)/2.0)
  441. >>> freq = np.linspace(-0.5, 0.5, len(A))
  442. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  443. >>> plt.plot(freq, response)
  444. >>> plt.axis([-0.5, 0.5, -120, 0])
  445. >>> plt.title("Frequency response of the Nuttall window")
  446. >>> plt.ylabel("Normalized magnitude [dB]")
  447. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  448. """
  449. xp = _namespace(xp)
  450. a = xp.asarray(
  451. [0.3635819, 0.4891775, 0.1365995, 0.0106411], dtype=xp.float64, device=device
  452. )
  453. device = xp_device(a)
  454. return _general_cosine_impl(M, a, xp, device, sym=sym)
  455. @xp_capabilities()
  456. def blackmanharris(M, sym=True, *, xp=None, device=None):
  457. """Return a minimum 4-term Blackman-Harris window.
  458. Parameters
  459. ----------
  460. M : int
  461. Number of points in the output window. If zero, an empty array
  462. is returned. An exception is thrown when it is negative.
  463. sym : bool, optional
  464. When True (default), generates a symmetric window, for use in filter
  465. design.
  466. When False, generates a periodic window, for use in spectral analysis.
  467. %(xp_device_snippet)s
  468. Returns
  469. -------
  470. w : ndarray
  471. The window, with the maximum value normalized to 1 (though the value 1
  472. does not appear if `M` is even and `sym` is True).
  473. Examples
  474. --------
  475. Plot the window and its frequency response:
  476. >>> import numpy as np
  477. >>> from scipy import signal
  478. >>> from scipy.fft import fft, fftshift
  479. >>> import matplotlib.pyplot as plt
  480. >>> window = signal.windows.blackmanharris(51)
  481. >>> plt.plot(window)
  482. >>> plt.title("Blackman-Harris window")
  483. >>> plt.ylabel("Amplitude")
  484. >>> plt.xlabel("Sample")
  485. >>> plt.figure()
  486. >>> A = fft(window, 2048) / (len(window)/2.0)
  487. >>> freq = np.linspace(-0.5, 0.5, len(A))
  488. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  489. >>> plt.plot(freq, response)
  490. >>> plt.axis([-0.5, 0.5, -120, 0])
  491. >>> plt.title("Frequency response of the Blackman-Harris window")
  492. >>> plt.ylabel("Normalized magnitude [dB]")
  493. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  494. """
  495. xp = _namespace(xp)
  496. a = xp.asarray(
  497. [0.35875, 0.48829, 0.14128, 0.01168], dtype=xp.float64, device=device
  498. )
  499. device = xp_device(a)
  500. return _general_cosine_impl(M, a, xp, device, sym=sym)
  501. @xp_capabilities()
  502. def flattop(M, sym=True, *, xp=None, device=None):
  503. """Return a flat top window.
  504. Parameters
  505. ----------
  506. M : int
  507. Number of points in the output window. If zero, an empty array
  508. is returned. An exception is thrown when it is negative.
  509. sym : bool, optional
  510. When True (default), generates a symmetric window, for use in filter
  511. design.
  512. When False, generates a periodic window, for use in spectral analysis.
  513. %(xp_device_snippet)s
  514. Returns
  515. -------
  516. w : ndarray
  517. The window, with the maximum value normalized to 1 (though the value 1
  518. does not appear if `M` is even and `sym` is True).
  519. Notes
  520. -----
  521. Flat top windows are used for taking accurate measurements of signal
  522. amplitude in the frequency domain, with minimal scalloping error from the
  523. center of a frequency bin to its edges, compared to others. This is a
  524. 5th-order cosine window, with the 5 terms optimized to make the main lobe
  525. maximally flat. [1]_
  526. References
  527. ----------
  528. .. [1] D'Antona, Gabriele, and A. Ferrero, "Digital Signal Processing for
  529. Measurement Systems", Springer Media, 2006, p. 70
  530. :doi:`10.1007/0-387-28666-7`.
  531. Examples
  532. --------
  533. Plot the window and its frequency response:
  534. >>> import numpy as np
  535. >>> from scipy import signal
  536. >>> from scipy.fft import fft, fftshift
  537. >>> import matplotlib.pyplot as plt
  538. >>> window = signal.windows.flattop(51)
  539. >>> plt.plot(window)
  540. >>> plt.title("Flat top window")
  541. >>> plt.ylabel("Amplitude")
  542. >>> plt.xlabel("Sample")
  543. >>> plt.figure()
  544. >>> A = fft(window, 2048) / (len(window)/2.0)
  545. >>> freq = np.linspace(-0.5, 0.5, len(A))
  546. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  547. >>> plt.plot(freq, response)
  548. >>> plt.axis([-0.5, 0.5, -120, 0])
  549. >>> plt.title("Frequency response of the flat top window")
  550. >>> plt.ylabel("Normalized magnitude [dB]")
  551. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  552. """
  553. xp = _namespace(xp)
  554. a = xp.asarray(
  555. [0.21557895, 0.41663158, 0.277263158, 0.083578947, 0.006947368],
  556. dtype=xp.float64, device=device
  557. )
  558. device = xp_device(a)
  559. return _general_cosine_impl(M, a, xp, device, sym=sym)
  560. @xp_capabilities()
  561. def bartlett(M, sym=True, *, xp=None, device=None):
  562. r"""
  563. Return a Bartlett window.
  564. The Bartlett window is very similar to a triangular window, except
  565. that the end points are at zero. It is often used in signal
  566. processing for tapering a signal, without generating too much
  567. ripple in the frequency domain.
  568. Parameters
  569. ----------
  570. M : int
  571. Number of points in the output window. If zero, an empty array
  572. is returned. An exception is thrown when it is negative.
  573. sym : bool, optional
  574. When True (default), generates a symmetric window, for use in filter
  575. design.
  576. When False, generates a periodic window, for use in spectral analysis.
  577. %(xp_device_snippet)s
  578. Returns
  579. -------
  580. w : ndarray
  581. The triangular window, with the first and last samples equal to zero
  582. and the maximum value normalized to 1 (though the value 1 does not
  583. appear if `M` is even and `sym` is True).
  584. See Also
  585. --------
  586. triang : A triangular window that does not touch zero at the ends
  587. Notes
  588. -----
  589. The Bartlett window is defined as
  590. .. math:: w(n) = \frac{2}{M-1} \left(
  591. \frac{M-1}{2} - \left|n - \frac{M-1}{2}\right|
  592. \right)
  593. Most references to the Bartlett window come from the signal
  594. processing literature, where it is used as one of many windowing
  595. functions for smoothing values. Note that convolution with this
  596. window produces linear interpolation. It is also known as an
  597. apodization (which means"removing the foot", i.e. smoothing
  598. discontinuities at the beginning and end of the sampled signal) or
  599. tapering function. The Fourier transform of the Bartlett is the product
  600. of two sinc functions.
  601. Note the excellent discussion in Kanasewich. [2]_
  602. References
  603. ----------
  604. .. [1] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
  605. Biometrika 37, 1-16, 1950.
  606. .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics",
  607. The University of Alberta Press, 1975, pp. 109-110.
  608. .. [3] A.V. Oppenheim and R.W. Schafer, "Discrete-Time Signal
  609. Processing", Prentice-Hall, 1999, pp. 468-471.
  610. .. [4] Wikipedia, "Window function",
  611. https://en.wikipedia.org/wiki/Window_function
  612. .. [5] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling,
  613. "Numerical Recipes", Cambridge University Press, 1986, page 429.
  614. Examples
  615. --------
  616. Plot the window and its frequency response:
  617. >>> import numpy as np
  618. >>> from scipy import signal
  619. >>> from scipy.fft import fft, fftshift
  620. >>> import matplotlib.pyplot as plt
  621. >>> window = signal.windows.bartlett(51)
  622. >>> plt.plot(window)
  623. >>> plt.title("Bartlett window")
  624. >>> plt.ylabel("Amplitude")
  625. >>> plt.xlabel("Sample")
  626. >>> plt.figure()
  627. >>> A = fft(window, 2048) / (len(window)/2.0)
  628. >>> freq = np.linspace(-0.5, 0.5, len(A))
  629. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  630. >>> plt.plot(freq, response)
  631. >>> plt.axis([-0.5, 0.5, -120, 0])
  632. >>> plt.title("Frequency response of the Bartlett window")
  633. >>> plt.ylabel("Normalized magnitude [dB]")
  634. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  635. """
  636. # Docstring adapted from NumPy's bartlett function
  637. xp = _namespace(xp)
  638. if _len_guards(M):
  639. return xp.ones(M, dtype=xp.float64, device=device)
  640. M, needs_trunc = _extend(M, sym)
  641. n = xp.arange(0, M, dtype=xp.float64, device=device)
  642. # cf https://github.com/data-apis/array-api-strict/issues/77
  643. w = xp.where(n <= (M - 1) / 2.0,
  644. 2.0 * n / (M - 1), 2.0 - 2.0 * n / (M - 1))
  645. return _truncate(w, needs_trunc)
  646. @xp_capabilities()
  647. def hann(M, sym=True, *, xp=None, device=None):
  648. r"""
  649. Return a Hann window.
  650. The Hann window is a taper formed by using a raised cosine or sine-squared
  651. with ends that touch zero.
  652. Parameters
  653. ----------
  654. M : int
  655. Number of points in the output window. If zero, an empty array
  656. is returned. An exception is thrown when it is negative.
  657. sym : bool, optional
  658. When True (default), generates a symmetric window, for use in filter
  659. design.
  660. When False, generates a periodic window, for use in spectral analysis.
  661. %(xp_device_snippet)s
  662. Returns
  663. -------
  664. w : ndarray
  665. The window, with the maximum value normalized to 1 (though the value 1
  666. does not appear if `M` is even and `sym` is True).
  667. Notes
  668. -----
  669. The Hann window is defined as
  670. .. math:: w(n) = 0.5 - 0.5 \cos\left(\frac{2\pi{n}}{M-1}\right)
  671. \qquad 0 \leq n \leq M-1
  672. The window was named for Julius von Hann, an Austrian meteorologist. It is
  673. also known as the Cosine Bell. It is sometimes erroneously referred to as
  674. the "Hanning" window, from the use of "hann" as a verb in the original
  675. paper and confusion with the very similar Hamming window.
  676. Most references to the Hann window come from the signal processing
  677. literature, where it is used as one of many windowing functions for
  678. smoothing values. It is also known as an apodization (which means
  679. "removing the foot", i.e. smoothing discontinuities at the beginning
  680. and end of the sampled signal) or tapering function.
  681. References
  682. ----------
  683. .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power
  684. spectra, Dover Publications, New York.
  685. .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics",
  686. The University of Alberta Press, 1975, pp. 106-108.
  687. .. [3] Wikipedia, "Window function",
  688. https://en.wikipedia.org/wiki/Window_function
  689. .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling,
  690. "Numerical Recipes", Cambridge University Press, 1986, page 425.
  691. Examples
  692. --------
  693. Plot the window and its frequency response:
  694. >>> import numpy as np
  695. >>> from scipy import signal
  696. >>> from scipy.fft import fft, fftshift
  697. >>> import matplotlib.pyplot as plt
  698. >>> window = signal.windows.hann(51)
  699. >>> plt.plot(window)
  700. >>> plt.title("Hann window")
  701. >>> plt.ylabel("Amplitude")
  702. >>> plt.xlabel("Sample")
  703. >>> plt.figure()
  704. >>> A = fft(window, 2048) / (len(window)/2.0)
  705. >>> freq = np.linspace(-0.5, 0.5, len(A))
  706. >>> response = np.abs(fftshift(A / abs(A).max()))
  707. >>> response = 20 * np.log10(np.maximum(response, 1e-10))
  708. >>> plt.plot(freq, response)
  709. >>> plt.axis([-0.5, 0.5, -120, 0])
  710. >>> plt.title("Frequency response of the Hann window")
  711. >>> plt.ylabel("Normalized magnitude [dB]")
  712. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  713. """
  714. # Docstring adapted from NumPy's hanning function
  715. return general_hamming(M, 0.5, sym, xp=xp, device=device)
  716. @xp_capabilities()
  717. def tukey(M, alpha=0.5, sym=True, *, xp=None, device=None):
  718. r"""Return a Tukey window, also known as a tapered cosine window.
  719. Parameters
  720. ----------
  721. M : int
  722. Number of points in the output window. If zero, an empty array
  723. is returned. An exception is thrown when it is negative.
  724. alpha : float, optional
  725. Shape parameter of the Tukey window, representing the fraction of the
  726. window inside the cosine tapered region.
  727. If zero, the Tukey window is equivalent to a rectangular window.
  728. If one, the Tukey window is equivalent to a Hann window.
  729. sym : bool, optional
  730. When True (default), generates a symmetric window, for use in filter
  731. design.
  732. When False, generates a periodic window, for use in spectral analysis.
  733. %(xp_device_snippet)s
  734. Returns
  735. -------
  736. w : ndarray
  737. The window, with the maximum value normalized to 1 (though the value 1
  738. does not appear if `M` is even and `sym` is True).
  739. References
  740. ----------
  741. .. [1] Harris, Fredric J. (Jan 1978). "On the use of Windows for Harmonic
  742. Analysis with the Discrete Fourier Transform". Proceedings of the
  743. IEEE 66 (1): 51-83. :doi:`10.1109/PROC.1978.10837`
  744. .. [2] Wikipedia, "Window function",
  745. https://en.wikipedia.org/wiki/Window_function#Tukey_window
  746. Examples
  747. --------
  748. Plot the window and its frequency response:
  749. >>> import numpy as np
  750. >>> from scipy import signal
  751. >>> from scipy.fft import fft, fftshift
  752. >>> import matplotlib.pyplot as plt
  753. >>> window = signal.windows.tukey(51)
  754. >>> plt.plot(window)
  755. >>> plt.title("Tukey window")
  756. >>> plt.ylabel("Amplitude")
  757. >>> plt.xlabel("Sample")
  758. >>> plt.ylim([0, 1.1])
  759. >>> plt.figure()
  760. >>> A = fft(window, 2048) / (len(window)/2.0)
  761. >>> freq = np.linspace(-0.5, 0.5, len(A))
  762. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  763. >>> plt.plot(freq, response)
  764. >>> plt.axis([-0.5, 0.5, -120, 0])
  765. >>> plt.title("Frequency response of the Tukey window")
  766. >>> plt.ylabel("Normalized magnitude [dB]")
  767. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  768. """
  769. xp = _namespace(xp)
  770. if _len_guards(M):
  771. return xp.ones(M, dtype=xp.float64, device=device)
  772. if alpha <= 0:
  773. return xp.ones(M, dtype=xp.float64, device=device)
  774. elif alpha >= 1.0:
  775. return hann(M, sym=sym, xp=xp, device=device)
  776. M, needs_trunc = _extend(M, sym)
  777. n = xp.arange(0, M, dtype=xp.float64, device=device)
  778. width = int(math.floor(alpha*(M-1)/2.0))
  779. n1 = n[0:width+1]
  780. n2 = n[width+1:M-width-1]
  781. n3 = n[M-width-1:]
  782. w1 = 0.5 * (1 + xp.cos(xp.pi * (-1 + 2.0*n1/alpha/(M-1))))
  783. w2 = xp.ones(n2.shape, device=device)
  784. w3 = 0.5 * (1 + xp.cos(xp.pi * (-2.0/alpha + 1 + 2.0*n3/alpha/(M-1))))
  785. w = xp.concat((w1, w2, w3))
  786. return _truncate(w, needs_trunc)
  787. @xp_capabilities()
  788. def barthann(M, sym=True, *, xp=None, device=None):
  789. """Return a modified Bartlett-Hann window.
  790. Parameters
  791. ----------
  792. M : int
  793. Number of points in the output window. If zero, an empty array
  794. is returned. An exception is thrown when it is negative.
  795. sym : bool, optional
  796. When True (default), generates a symmetric window, for use in filter
  797. design.
  798. When False, generates a periodic window, for use in spectral analysis.
  799. %(xp_device_snippet)s
  800. Returns
  801. -------
  802. w : ndarray
  803. The window, with the maximum value normalized to 1 (though the value 1
  804. does not appear if `M` is even and `sym` is True).
  805. Examples
  806. --------
  807. Plot the window and its frequency response:
  808. >>> import numpy as np
  809. >>> from scipy import signal
  810. >>> from scipy.fft import fft, fftshift
  811. >>> import matplotlib.pyplot as plt
  812. >>> window = signal.windows.barthann(51)
  813. >>> plt.plot(window)
  814. >>> plt.title("Bartlett-Hann window")
  815. >>> plt.ylabel("Amplitude")
  816. >>> plt.xlabel("Sample")
  817. >>> plt.figure()
  818. >>> A = fft(window, 2048) / (len(window)/2.0)
  819. >>> freq = np.linspace(-0.5, 0.5, len(A))
  820. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  821. >>> plt.plot(freq, response)
  822. >>> plt.axis([-0.5, 0.5, -120, 0])
  823. >>> plt.title("Frequency response of the Bartlett-Hann window")
  824. >>> plt.ylabel("Normalized magnitude [dB]")
  825. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  826. """
  827. xp = _namespace(xp)
  828. if _len_guards(M):
  829. return xp.ones(M, dtype=xp.float64, device=device)
  830. M, needs_trunc = _extend(M, sym)
  831. n = xp.arange(0, M, dtype=xp.float64, device=device)
  832. fac = abs(n / (M - 1.0) - 0.5)
  833. w = 0.62 - 0.48 * fac + 0.38 * xp.cos(2 * xp.pi * fac)
  834. return _truncate(w, needs_trunc)
  835. @xp_capabilities()
  836. def general_hamming(M, alpha, sym=True, *, xp=None, device=None):
  837. r"""Return a generalized Hamming window.
  838. The generalized Hamming window is constructed by multiplying a rectangular
  839. window by one period of a cosine function [1]_.
  840. Parameters
  841. ----------
  842. M : int
  843. Number of points in the output window. If zero, an empty array
  844. is returned. An exception is thrown when it is negative.
  845. alpha : float
  846. The window coefficient, :math:`\alpha`
  847. sym : bool, optional
  848. When True (default), generates a symmetric window, for use in filter
  849. design.
  850. When False, generates a periodic window, for use in spectral analysis.
  851. %(xp_device_snippet)s
  852. Returns
  853. -------
  854. w : ndarray
  855. The window, with the maximum value normalized to 1 (though the value 1
  856. does not appear if `M` is even and `sym` is True).
  857. See Also
  858. --------
  859. hamming, hann
  860. Notes
  861. -----
  862. The generalized Hamming window is defined as
  863. .. math:: w(n) = \alpha - \left(1 - \alpha\right)
  864. \cos\left(\frac{2\pi{n}}{M-1}\right) \qquad 0 \leq n \leq M-1
  865. Both the common Hamming window and Hann window are special cases of the
  866. generalized Hamming window with :math:`\alpha` = 0.54 and :math:`\alpha` =
  867. 0.5, respectively [2]_.
  868. References
  869. ----------
  870. .. [1] DSPRelated, "Generalized Hamming Window Family",
  871. https://www.dsprelated.com/freebooks/sasp/Generalized_Hamming_Window_Family.html
  872. .. [2] Wikipedia, "Window function",
  873. https://en.wikipedia.org/wiki/Window_function
  874. .. [3] Riccardo Piantanida ESA, "Sentinel-1 Level 1 Detailed Algorithm
  875. Definition",
  876. https://sentinel.esa.int/documents/247904/1877131/Sentinel-1-Level-1-Detailed-Algorithm-Definition
  877. .. [4] Matthieu Bourbigot ESA, "Sentinel-1 Product Definition",
  878. https://sentinel.esa.int/documents/247904/1877131/Sentinel-1-Product-Definition
  879. Examples
  880. --------
  881. The Sentinel-1A/B Instrument Processing Facility uses generalized Hamming
  882. windows in the processing of spaceborne Synthetic Aperture Radar (SAR)
  883. data [3]_. The facility uses various values for the :math:`\alpha`
  884. parameter based on operating mode of the SAR instrument. Some common
  885. :math:`\alpha` values include 0.75, 0.7 and 0.52 [4]_. As an example, we
  886. plot these different windows.
  887. >>> import numpy as np
  888. >>> from scipy.signal.windows import general_hamming
  889. >>> from scipy.fft import fft, fftshift
  890. >>> import matplotlib.pyplot as plt
  891. >>> fig1, spatial_plot = plt.subplots()
  892. >>> spatial_plot.set_title("Generalized Hamming Windows")
  893. >>> spatial_plot.set_ylabel("Amplitude")
  894. >>> spatial_plot.set_xlabel("Sample")
  895. >>> fig2, freq_plot = plt.subplots()
  896. >>> freq_plot.set_title("Frequency Responses")
  897. >>> freq_plot.set_ylabel("Normalized magnitude [dB]")
  898. >>> freq_plot.set_xlabel("Normalized frequency [cycles per sample]")
  899. >>> for alpha in [0.75, 0.7, 0.52]:
  900. ... window = general_hamming(41, alpha)
  901. ... spatial_plot.plot(window, label="{:.2f}".format(alpha))
  902. ... A = fft(window, 2048) / (len(window)/2.0)
  903. ... freq = np.linspace(-0.5, 0.5, len(A))
  904. ... response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  905. ... freq_plot.plot(freq, response, label="{:.2f}".format(alpha))
  906. >>> freq_plot.legend(loc="upper right")
  907. >>> spatial_plot.legend(loc="upper right")
  908. """
  909. xp = _namespace(xp)
  910. a = xp.asarray([alpha, 1. - alpha], dtype=xp.float64, device=device)
  911. device = xp_device(a)
  912. return _general_cosine_impl(M, a, xp, device, sym=sym)
  913. @xp_capabilities()
  914. def hamming(M, sym=True, *, xp=None, device=None):
  915. r"""Return a Hamming window.
  916. The Hamming window is a taper formed by using a raised cosine with
  917. non-zero endpoints, optimized to minimize the nearest side lobe.
  918. Parameters
  919. ----------
  920. M : int
  921. Number of points in the output window. If zero, an empty array
  922. is returned. An exception is thrown when it is negative.
  923. sym : bool, optional
  924. When True (default), generates a symmetric window, for use in filter
  925. design.
  926. When False, generates a periodic window, for use in spectral analysis.
  927. %(xp_device_snippet)s
  928. Returns
  929. -------
  930. w : ndarray
  931. The window, with the maximum value normalized to 1 (though the value 1
  932. does not appear if `M` is even and `sym` is True).
  933. Notes
  934. -----
  935. The Hamming window is defined as
  936. .. math:: w(n) = 0.54 - 0.46 \cos\left(\frac{2\pi{n}}{M-1}\right)
  937. \qquad 0 \leq n \leq M-1
  938. The Hamming was named for R. W. Hamming, an associate of J. W. Tukey and
  939. is described in Blackman and Tukey. It was recommended for smoothing the
  940. truncated autocovariance function in the time domain.
  941. Most references to the Hamming window come from the signal processing
  942. literature, where it is used as one of many windowing functions for
  943. smoothing values. It is also known as an apodization (which means
  944. "removing the foot", i.e. smoothing discontinuities at the beginning
  945. and end of the sampled signal) or tapering function.
  946. References
  947. ----------
  948. .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power
  949. spectra, Dover Publications, New York.
  950. .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The
  951. University of Alberta Press, 1975, pp. 109-110.
  952. .. [3] Wikipedia, "Window function",
  953. https://en.wikipedia.org/wiki/Window_function
  954. .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling,
  955. "Numerical Recipes", Cambridge University Press, 1986, page 425.
  956. Examples
  957. --------
  958. Plot the window and its frequency response:
  959. >>> import numpy as np
  960. >>> from scipy import signal
  961. >>> from scipy.fft import fft, fftshift
  962. >>> import matplotlib.pyplot as plt
  963. >>> window = signal.windows.hamming(51)
  964. >>> plt.plot(window)
  965. >>> plt.title("Hamming window")
  966. >>> plt.ylabel("Amplitude")
  967. >>> plt.xlabel("Sample")
  968. >>> plt.figure()
  969. >>> A = fft(window, 2048) / (len(window)/2.0)
  970. >>> freq = np.linspace(-0.5, 0.5, len(A))
  971. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  972. >>> plt.plot(freq, response)
  973. >>> plt.axis([-0.5, 0.5, -120, 0])
  974. >>> plt.title("Frequency response of the Hamming window")
  975. >>> plt.ylabel("Normalized magnitude [dB]")
  976. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  977. """
  978. # Docstring adapted from NumPy's hamming function
  979. return general_hamming(M, 0.54, sym, xp=xp, device=device)
  980. @xp_capabilities()
  981. def kaiser(M, beta, sym=True, *, xp=None, device=None):
  982. r"""Return a Kaiser window.
  983. The Kaiser window is a taper formed by using a Bessel function.
  984. Parameters
  985. ----------
  986. M : int
  987. Number of points in the output window. If zero, an empty array
  988. is returned. An exception is thrown when it is negative.
  989. beta : float
  990. Shape parameter, determines trade-off between main-lobe width and
  991. side lobe level. As beta gets large, the window narrows.
  992. sym : bool, optional
  993. When True (default), generates a symmetric window, for use in filter
  994. design.
  995. When False, generates a periodic window, for use in spectral analysis.
  996. %(xp_device_snippet)s
  997. Returns
  998. -------
  999. w : ndarray
  1000. The window, with the maximum value normalized to 1 (though the value 1
  1001. does not appear if `M` is even and `sym` is True).
  1002. Notes
  1003. -----
  1004. The Kaiser window is defined as
  1005. .. math:: w(n) = I_0\left( \beta \sqrt{1-\frac{4n^2}{(M-1)^2}}
  1006. \right)/I_0(\beta)
  1007. with
  1008. .. math:: \quad -\frac{M-1}{2} \leq n \leq \frac{M-1}{2},
  1009. where :math:`I_0` is the modified zeroth-order Bessel function.
  1010. The Kaiser was named for Jim Kaiser, who discovered a simple approximation
  1011. to the DPSS window based on Bessel functions.
  1012. The Kaiser window is a very good approximation to the discrete prolate
  1013. spheroidal sequence, or Slepian window, which is the transform which
  1014. maximizes the energy in the main lobe of the window relative to total
  1015. energy.
  1016. The Kaiser can approximate other windows by varying the beta parameter.
  1017. (Some literature uses alpha = beta/pi.) [4]_
  1018. ==== =======================
  1019. beta Window shape
  1020. ==== =======================
  1021. 0 Rectangular
  1022. 5 Similar to a Hamming
  1023. 6 Similar to a Hann
  1024. 8.6 Similar to a Blackman
  1025. ==== =======================
  1026. A beta value of 14 is probably a good starting point. Note that as beta
  1027. gets large, the window narrows, and so the number of samples needs to be
  1028. large enough to sample the increasingly narrow spike, otherwise NaNs will
  1029. be returned.
  1030. Most references to the Kaiser window come from the signal processing
  1031. literature, where it is used as one of many windowing functions for
  1032. smoothing values. It is also known as an apodization (which means
  1033. "removing the foot", i.e. smoothing discontinuities at the beginning
  1034. and end of the sampled signal) or tapering function.
  1035. References
  1036. ----------
  1037. .. [1] J. F. Kaiser, "Digital Filters" - Ch 7 in "Systems analysis by
  1038. digital computer", Editors: F.F. Kuo and J.F. Kaiser, p 218-285.
  1039. John Wiley and Sons, New York, (1966).
  1040. .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The
  1041. University of Alberta Press, 1975, pp. 177-178.
  1042. .. [3] Wikipedia, "Window function",
  1043. https://en.wikipedia.org/wiki/Window_function
  1044. .. [4] F. J. Harris, "On the use of windows for harmonic analysis with the
  1045. discrete Fourier transform," Proceedings of the IEEE, vol. 66,
  1046. no. 1, pp. 51-83, Jan. 1978. :doi:`10.1109/PROC.1978.10837`.
  1047. Examples
  1048. --------
  1049. Plot the window and its frequency response:
  1050. >>> import numpy as np
  1051. >>> from scipy import signal
  1052. >>> from scipy.fft import fft, fftshift
  1053. >>> import matplotlib.pyplot as plt
  1054. >>> window = signal.windows.kaiser(51, beta=14)
  1055. >>> plt.plot(window)
  1056. >>> plt.title(r"Kaiser window ($\beta$=14)")
  1057. >>> plt.ylabel("Amplitude")
  1058. >>> plt.xlabel("Sample")
  1059. >>> plt.figure()
  1060. >>> A = fft(window, 2048) / (len(window)/2.0)
  1061. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1062. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1063. >>> plt.plot(freq, response)
  1064. >>> plt.axis([-0.5, 0.5, -120, 0])
  1065. >>> plt.title(r"Frequency response of the Kaiser window ($\beta$=14)")
  1066. >>> plt.ylabel("Normalized magnitude [dB]")
  1067. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1068. """
  1069. xp = _namespace(xp)
  1070. # Docstring adapted from NumPy's kaiser function
  1071. if _len_guards(M):
  1072. return xp.ones(M, dtype=xp.float64, device=device)
  1073. M, needs_trunc = _extend(M, sym)
  1074. n = xp.arange(0, M, dtype=xp.float64, device=device)
  1075. alpha = (M - 1) / 2.0
  1076. w = (special.i0(beta * xp.sqrt(1 - ((n - alpha) / alpha) ** 2.0)) /
  1077. special.i0(xp.asarray(beta, dtype=xp.float64)))
  1078. return _truncate(w, needs_trunc)
  1079. @xp_capabilities()
  1080. def kaiser_bessel_derived(M, beta, *, sym=True, xp=None, device=None):
  1081. """Return a Kaiser-Bessel derived window.
  1082. Parameters
  1083. ----------
  1084. M : int
  1085. Number of points in the output window. If zero, an empty array
  1086. is returned. An exception is thrown when it is negative.
  1087. Note that this window is only defined for an even
  1088. number of points.
  1089. beta : float
  1090. Kaiser window shape parameter.
  1091. sym : bool, optional
  1092. This parameter only exists to comply with the interface offered by
  1093. the other window functions and to be callable by `get_window`.
  1094. When True (default), generates a symmetric window, for use in filter
  1095. design.
  1096. %(xp_device_snippet)s
  1097. Returns
  1098. -------
  1099. w : ndarray
  1100. The window, normalized to fulfil the Princen-Bradley condition.
  1101. See Also
  1102. --------
  1103. kaiser
  1104. Notes
  1105. -----
  1106. It is designed to be suitable for use with the modified discrete cosine
  1107. transform (MDCT) and is mainly used in audio signal processing and
  1108. audio coding.
  1109. .. versionadded:: 1.9.0
  1110. References
  1111. ----------
  1112. .. [1] Bosi, Marina, and Richard E. Goldberg. Introduction to Digital
  1113. Audio Coding and Standards. Dordrecht: Kluwer, 2003.
  1114. .. [2] Wikipedia, "Kaiser window",
  1115. https://en.wikipedia.org/wiki/Kaiser_window
  1116. Examples
  1117. --------
  1118. Plot the Kaiser-Bessel derived window based on the wikipedia
  1119. reference [2]_:
  1120. >>> import numpy as np
  1121. >>> from scipy import signal
  1122. >>> import matplotlib.pyplot as plt
  1123. >>> fig, ax = plt.subplots()
  1124. >>> N = 50
  1125. >>> for alpha in [0.64, 2.55, 7.64, 31.83]:
  1126. ... ax.plot(signal.windows.kaiser_bessel_derived(2*N, np.pi*alpha),
  1127. ... label=f"{alpha=}")
  1128. >>> ax.grid(True)
  1129. >>> ax.set_title("Kaiser-Bessel derived window")
  1130. >>> ax.set_ylabel("Amplitude")
  1131. >>> ax.set_xlabel("Sample")
  1132. >>> ax.set_xticks([0, N, 2*N-1])
  1133. >>> ax.set_xticklabels(["0", "N", "2N+1"]) # doctest: +SKIP
  1134. >>> ax.set_yticks([0.0, 0.2, 0.4, 0.6, 0.707, 0.8, 1.0])
  1135. >>> fig.legend(loc="center")
  1136. >>> fig.tight_layout()
  1137. >>> fig.show()
  1138. """
  1139. xp = _namespace(xp)
  1140. if not sym:
  1141. raise ValueError(
  1142. "Kaiser-Bessel Derived windows are only defined for symmetric "
  1143. "shapes"
  1144. )
  1145. elif M < 1:
  1146. return xp.asarray([])
  1147. elif M % 2:
  1148. raise ValueError(
  1149. "Kaiser-Bessel Derived windows are only defined for even number "
  1150. "of points"
  1151. )
  1152. kaiser_window = kaiser(M // 2 + 1, beta, xp=xp, device=device)
  1153. csum = xp.cumulative_sum(kaiser_window)
  1154. half_window = xp.sqrt(csum[:-1] / csum[-1])
  1155. w = xp.concat((half_window, xp.flip(half_window)), axis=0)
  1156. return xp.asarray(w, device=device)
  1157. @xp_capabilities()
  1158. def gaussian(M, std, sym=True, *, xp=None, device=None):
  1159. r"""Return a Gaussian window.
  1160. Parameters
  1161. ----------
  1162. M : int
  1163. Number of points in the output window. If zero, an empty array
  1164. is returned. An exception is thrown when it is negative.
  1165. std : float
  1166. The standard deviation, sigma.
  1167. sym : bool, optional
  1168. When True (default), generates a symmetric window, for use in filter
  1169. design.
  1170. When False, generates a periodic window, for use in spectral analysis.
  1171. %(xp_device_snippet)s
  1172. Returns
  1173. -------
  1174. w : ndarray
  1175. The window, with the maximum value normalized to 1 (though the value 1
  1176. does not appear if `M` is even and `sym` is True).
  1177. Notes
  1178. -----
  1179. The Gaussian window is defined as
  1180. .. math:: w(n) = e^{ -\frac{1}{2}\left(\frac{n}{\sigma}\right)^2 }
  1181. Examples
  1182. --------
  1183. Plot the window and its frequency response:
  1184. >>> import numpy as np
  1185. >>> from scipy import signal
  1186. >>> from scipy.fft import fft, fftshift
  1187. >>> import matplotlib.pyplot as plt
  1188. >>> window = signal.windows.gaussian(51, std=7)
  1189. >>> plt.plot(window)
  1190. >>> plt.title(r"Gaussian window ($\sigma$=7)")
  1191. >>> plt.ylabel("Amplitude")
  1192. >>> plt.xlabel("Sample")
  1193. >>> plt.figure()
  1194. >>> A = fft(window, 2048) / (len(window)/2.0)
  1195. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1196. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1197. >>> plt.plot(freq, response)
  1198. >>> plt.axis([-0.5, 0.5, -120, 0])
  1199. >>> plt.title(r"Frequency response of the Gaussian window ($\sigma$=7)")
  1200. >>> plt.ylabel("Normalized magnitude [dB]")
  1201. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1202. """
  1203. xp = _namespace(xp)
  1204. if _len_guards(M):
  1205. return xp.ones(M, dtype=xp.float64, device=device)
  1206. M, needs_trunc = _extend(M, sym)
  1207. n = xp.arange(0, M, dtype=xp.float64, device=device) - (M - 1.0) / 2.0
  1208. sig2 = 2 * std * std
  1209. w = xp.exp(-n ** 2 / sig2)
  1210. return _truncate(w, needs_trunc)
  1211. @xp_capabilities()
  1212. def general_gaussian(M, p, sig, sym=True, *, xp=None, device=None):
  1213. r"""Return a window with a generalized Gaussian shape.
  1214. Parameters
  1215. ----------
  1216. M : int
  1217. Number of points in the output window. If zero, an empty array
  1218. is returned. An exception is thrown when it is negative.
  1219. p : float
  1220. Shape parameter. p = 1 is identical to `gaussian`, p = 0.5 is
  1221. the same shape as the Laplace distribution.
  1222. sig : float
  1223. The standard deviation, sigma.
  1224. sym : bool, optional
  1225. When True (default), generates a symmetric window, for use in filter
  1226. design.
  1227. When False, generates a periodic window, for use in spectral analysis.
  1228. %(xp_device_snippet)s
  1229. Returns
  1230. -------
  1231. w : ndarray
  1232. The window, with the maximum value normalized to 1 (though the value 1
  1233. does not appear if `M` is even and `sym` is True).
  1234. Notes
  1235. -----
  1236. The generalized Gaussian window is defined as
  1237. .. math:: w(n) = e^{ -\frac{1}{2}\left|\frac{n}{\sigma}\right|^{2p} }
  1238. the half-power point is at
  1239. .. math:: (2 \log(2))^{1/(2 p)} \sigma
  1240. Examples
  1241. --------
  1242. Plot the window and its frequency response:
  1243. >>> import numpy as np
  1244. >>> from scipy import signal
  1245. >>> from scipy.fft import fft, fftshift
  1246. >>> import matplotlib.pyplot as plt
  1247. >>> window = signal.windows.general_gaussian(51, p=1.5, sig=7)
  1248. >>> plt.plot(window)
  1249. >>> plt.title(r"Generalized Gaussian window (p=1.5, $\sigma$=7)")
  1250. >>> plt.ylabel("Amplitude")
  1251. >>> plt.xlabel("Sample")
  1252. >>> plt.figure()
  1253. >>> A = fft(window, 2048) / (len(window)/2.0)
  1254. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1255. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1256. >>> plt.plot(freq, response)
  1257. >>> plt.axis([-0.5, 0.5, -120, 0])
  1258. >>> plt.title(r"Freq. resp. of the gen. Gaussian "
  1259. ... r"window (p=1.5, $\sigma$=7)")
  1260. >>> plt.ylabel("Normalized magnitude [dB]")
  1261. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1262. """
  1263. xp = _namespace(xp)
  1264. if _len_guards(M):
  1265. return xp.ones(M, dtype=xp.float64, device=device)
  1266. M, needs_trunc = _extend(M, sym)
  1267. n = xp.arange(0, M, dtype=xp.float64, device=device) - (M - 1.0) / 2.0
  1268. w = xp.exp(-0.5 * abs(n / sig) ** (2 * p))
  1269. return _truncate(w, needs_trunc)
  1270. # `chebwin` contributed by Kumar Appaiah.
  1271. @xp_capabilities(skip_backends=(("jax.numpy", "item assignment"),
  1272. ("dask.array", "data-dependent output shapes")))
  1273. def chebwin(M, at, sym=True, *, xp=None, device=None):
  1274. r"""Return a Dolph-Chebyshev window.
  1275. Parameters
  1276. ----------
  1277. M : int
  1278. Number of points in the output window. If zero, an empty array
  1279. is returned. An exception is thrown when it is negative.
  1280. at : float
  1281. Attenuation (in dB).
  1282. sym : bool, optional
  1283. When True (default), generates a symmetric window, for use in filter
  1284. design.
  1285. When False, generates a periodic window, for use in spectral analysis.
  1286. %(xp_device_snippet)s
  1287. Returns
  1288. -------
  1289. w : ndarray
  1290. The window, with the maximum value always normalized to 1
  1291. Notes
  1292. -----
  1293. This window optimizes for the narrowest main lobe width for a given order
  1294. `M` and sidelobe equiripple attenuation `at`, using Chebyshev
  1295. polynomials. It was originally developed by Dolph to optimize the
  1296. directionality of radio antenna arrays.
  1297. Unlike most windows, the Dolph-Chebyshev is defined in terms of its
  1298. frequency response:
  1299. .. math:: W(k) = \frac
  1300. {\cos\{M \cos^{-1}[\beta \cos(\frac{\pi k}{M})]\}}
  1301. {\cosh[M \cosh^{-1}(\beta)]}
  1302. where
  1303. .. math:: \beta = \cosh \left [\frac{1}{M}
  1304. \cosh^{-1}(10^\frac{A}{20}) \right ]
  1305. and 0 <= abs(k) <= M-1. A is the attenuation in decibels (`at`).
  1306. The time domain window is then generated using the IFFT, so
  1307. power-of-two `M` are the fastest to generate, and prime number `M` are
  1308. the slowest.
  1309. The equiripple condition in the frequency domain creates impulses in the
  1310. time domain, which appear at the ends of the window.
  1311. References
  1312. ----------
  1313. .. [1] C. Dolph, "A current distribution for broadside arrays which
  1314. optimizes the relationship between beam width and side-lobe level",
  1315. Proceedings of the IEEE, Vol. 34, Issue 6
  1316. .. [2] Peter Lynch, "The Dolph-Chebyshev Window: A Simple Optimal Filter",
  1317. American Meteorological Society (April 1997)
  1318. http://mathsci.ucd.ie/~plynch/Publications/Dolph.pdf
  1319. .. [3] F. J. Harris, "On the use of windows for harmonic analysis with the
  1320. discrete Fourier transforms", Proceedings of the IEEE, Vol. 66,
  1321. No. 1, January 1978
  1322. Examples
  1323. --------
  1324. Plot the window and its frequency response:
  1325. >>> import numpy as np
  1326. >>> from scipy import signal
  1327. >>> from scipy.fft import fft, fftshift
  1328. >>> import matplotlib.pyplot as plt
  1329. >>> window = signal.windows.chebwin(51, at=100)
  1330. >>> plt.plot(window)
  1331. >>> plt.title("Dolph-Chebyshev window (100 dB)")
  1332. >>> plt.ylabel("Amplitude")
  1333. >>> plt.xlabel("Sample")
  1334. >>> plt.figure()
  1335. >>> A = fft(window, 2048) / (len(window)/2.0)
  1336. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1337. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1338. >>> plt.plot(freq, response)
  1339. >>> plt.axis([-0.5, 0.5, -120, 0])
  1340. >>> plt.title("Frequency response of the Dolph-Chebyshev window (100 dB)")
  1341. >>> plt.ylabel("Normalized magnitude [dB]")
  1342. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1343. """
  1344. xp = _namespace(xp)
  1345. if abs(at) < 45:
  1346. warnings.warn("This window is not suitable for spectral analysis "
  1347. "for attenuation values lower than about 45dB because "
  1348. "the equivalent noise bandwidth of a Chebyshev window "
  1349. "does not grow monotonically with increasing sidelobe "
  1350. "attenuation when the attenuation is smaller than "
  1351. "about 45 dB.",
  1352. stacklevel=2)
  1353. if _len_guards(M):
  1354. return xp.ones(M, dtype=xp.float64, device=device)
  1355. M, needs_trunc = _extend(M, sym)
  1356. # compute the parameter beta
  1357. order = M - 1.0
  1358. _val = xp.asarray(10 ** (abs(at) / 20.), dtype=xp.float64, device=device)
  1359. beta = xp.cosh(1.0 / order * xp.acosh(_val))
  1360. k = xp.arange(M, dtype=xp.float64, device=device)
  1361. x = beta * xp.cos(xp.pi * k / M)
  1362. # Find the window's DFT coefficients
  1363. # Use analytic definition of Chebyshev polynomial instead of expansion
  1364. # from scipy.special. Using the expansion in scipy.special leads to errors.
  1365. p = xp.zeros_like(x)
  1366. p[x > 1] = xp.cosh(order * xp.acosh(x[x > 1]))
  1367. p[x < -1] = (2 * (M % 2) - 1) * xp.cosh(order * xp.acosh(-x[x < -1]))
  1368. p[abs(x) <= 1] = xp.cos(order * xp.acos(x[abs(x) <= 1]))
  1369. # Appropriate IDFT and filling up
  1370. # depending on even/odd M
  1371. if M % 2:
  1372. w = xp.real(sp_fft.fft(p))
  1373. n = (M + 1) // 2
  1374. w = w[:n]
  1375. w = xp.concat((xp.flip(w[1:n]), w))
  1376. else:
  1377. p = p * xp.exp(1j * xp.pi / M * xp.arange(M, dtype=xp.float64, device=device))
  1378. w = xp.real(sp_fft.fft(p))
  1379. n = M // 2 + 1
  1380. w = xp.concat((xp.flip(w[1:n]), w[1:n]))
  1381. w = w / xp.max(w)
  1382. return _truncate(w, needs_trunc)
  1383. @xp_capabilities()
  1384. def cosine(M, sym=True, *, xp=None, device=None):
  1385. """Return a window with a simple cosine shape.
  1386. Parameters
  1387. ----------
  1388. M : int
  1389. Number of points in the output window. If zero, an empty array
  1390. is returned. An exception is thrown when it is negative.
  1391. sym : bool, optional
  1392. When True (default), generates a symmetric window, for use in filter
  1393. design.
  1394. When False, generates a periodic window, for use in spectral analysis.
  1395. %(xp_device_snippet)s
  1396. Returns
  1397. -------
  1398. w : ndarray
  1399. The window, with the maximum value normalized to 1 (though the value 1
  1400. does not appear if `M` is even and `sym` is True).
  1401. Notes
  1402. -----
  1403. .. versionadded:: 0.13.0
  1404. Examples
  1405. --------
  1406. Plot the window and its frequency response:
  1407. >>> import numpy as np
  1408. >>> from scipy import signal
  1409. >>> from scipy.fft import fft, fftshift
  1410. >>> import matplotlib.pyplot as plt
  1411. >>> window = signal.windows.cosine(51)
  1412. >>> plt.plot(window)
  1413. >>> plt.title("Cosine window")
  1414. >>> plt.ylabel("Amplitude")
  1415. >>> plt.xlabel("Sample")
  1416. >>> plt.figure()
  1417. >>> A = fft(window, 2047) / (len(window)/2.0)
  1418. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1419. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1420. >>> plt.plot(freq, response)
  1421. >>> plt.axis([-0.5, 0.5, -120, 0])
  1422. >>> plt.title("Frequency response of the cosine window")
  1423. >>> plt.ylabel("Normalized magnitude [dB]")
  1424. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1425. >>> plt.show()
  1426. """
  1427. xp = _namespace(xp)
  1428. if _len_guards(M):
  1429. return xp.ones(M, dtype=xp.float64, device=device)
  1430. M, needs_trunc = _extend(M, sym)
  1431. w = xp.sin(xp.pi / M * (xp.arange(M, dtype=xp.float64, device=device) + .5))
  1432. return _truncate(w, needs_trunc)
  1433. @xp_capabilities()
  1434. def exponential(M, center=None, tau=1., sym=True, *, xp=None, device=None):
  1435. r"""Return an exponential (or Poisson) window.
  1436. Parameters
  1437. ----------
  1438. M : int
  1439. Number of points in the output window. If zero, an empty array
  1440. is returned. An exception is thrown when it is negative.
  1441. center : float, optional
  1442. Parameter defining the center location of the window function.
  1443. The default value if not given is ``center = (M-1) / 2``. This
  1444. parameter must take its default value for symmetric windows.
  1445. tau : float, optional
  1446. Parameter defining the decay. For ``center = 0`` use
  1447. ``tau = -(M-1) / ln(x)`` if ``x`` is the fraction of the window
  1448. remaining at the end.
  1449. sym : bool, optional
  1450. When True (default), generates a symmetric window, for use in filter
  1451. design.
  1452. When False, generates a periodic window, for use in spectral analysis.
  1453. %(xp_device_snippet)s
  1454. Returns
  1455. -------
  1456. w : ndarray
  1457. The window, with the maximum value normalized to 1 (though the value 1
  1458. does not appear if `M` is even and `sym` is True).
  1459. Notes
  1460. -----
  1461. The Exponential window is defined as
  1462. .. math:: w(n) = e^{-|n-center| / \tau}
  1463. References
  1464. ----------
  1465. .. [1] S. Gade and H. Herlufsen, "Windows to FFT analysis (Part I)",
  1466. Technical Review 3, Bruel & Kjaer, 1987.
  1467. Examples
  1468. --------
  1469. Plot the symmetric window and its frequency response:
  1470. >>> import numpy as np
  1471. >>> from scipy import signal
  1472. >>> from scipy.fft import fft, fftshift
  1473. >>> import matplotlib.pyplot as plt
  1474. >>> M = 51
  1475. >>> tau = 3.0
  1476. >>> window = signal.windows.exponential(M, tau=tau)
  1477. >>> plt.plot(window)
  1478. >>> plt.title("Exponential Window (tau=3.0)")
  1479. >>> plt.ylabel("Amplitude")
  1480. >>> plt.xlabel("Sample")
  1481. >>> plt.figure()
  1482. >>> A = fft(window, 2048) / (len(window)/2.0)
  1483. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1484. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1485. >>> plt.plot(freq, response)
  1486. >>> plt.axis([-0.5, 0.5, -35, 0])
  1487. >>> plt.title("Frequency response of the Exponential window (tau=3.0)")
  1488. >>> plt.ylabel("Normalized magnitude [dB]")
  1489. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1490. This function can also generate non-symmetric windows:
  1491. >>> tau2 = -(M-1) / np.log(0.01)
  1492. >>> window2 = signal.windows.exponential(M, 0, tau2, False)
  1493. >>> plt.figure()
  1494. >>> plt.plot(window2)
  1495. >>> plt.ylabel("Amplitude")
  1496. >>> plt.xlabel("Sample")
  1497. """
  1498. xp = _namespace(xp)
  1499. if sym and center is not None:
  1500. raise ValueError("If sym==True, center must be None.")
  1501. if _len_guards(M):
  1502. return xp.ones(M, dtype=xp.float64, device=device)
  1503. M, needs_trunc = _extend(M, sym)
  1504. if center is None:
  1505. center = (M-1) / 2
  1506. n = xp.arange(0, M, dtype=xp.float64, device=device)
  1507. w = xp.exp(-abs(n-center) / tau)
  1508. return _truncate(w, needs_trunc)
  1509. @xp_capabilities(skip_backends=[("jax.numpy", "item assignment")])
  1510. def taylor(M, nbar=4, sll=30, norm=True, sym=True, *, xp=None, device=None):
  1511. """
  1512. Return a Taylor window.
  1513. The Taylor window taper function approximates the Dolph-Chebyshev window's
  1514. constant sidelobe level for a parameterized number of near-in sidelobes,
  1515. but then allows a taper beyond [2]_.
  1516. The SAR (synthetic aperture radar) community commonly uses Taylor
  1517. weighting for image formation processing because it provides strong,
  1518. selectable sidelobe suppression with minimum broadening of the
  1519. mainlobe [1]_.
  1520. Parameters
  1521. ----------
  1522. M : int
  1523. Number of points in the output window. If zero, an empty array
  1524. is returned. An exception is thrown when it is negative.
  1525. nbar : int, optional
  1526. Number of nearly constant level sidelobes adjacent to the mainlobe.
  1527. sll : float, optional
  1528. Desired suppression of sidelobe level in decibels (dB) relative to the
  1529. DC gain of the mainlobe. This should be a positive number.
  1530. norm : bool, optional
  1531. When True (default), divides the window by the largest (middle) value
  1532. for odd-length windows or the value that would occur between the two
  1533. repeated middle values for even-length windows such that all values
  1534. are less than or equal to 1. When False the DC gain will remain at 1
  1535. (0 dB) and the sidelobes will be `sll` dB down.
  1536. sym : bool, optional
  1537. When True (default), generates a symmetric window, for use in filter
  1538. design.
  1539. When False, generates a periodic window, for use in spectral analysis.
  1540. %(xp_device_snippet)s
  1541. Returns
  1542. -------
  1543. out : array
  1544. The window. When `norm` is True (default), the maximum value is
  1545. normalized to 1 (though the value 1 does not appear if `M` is
  1546. even and `sym` is True).
  1547. See Also
  1548. --------
  1549. chebwin, kaiser, bartlett, blackman, hamming, hann
  1550. References
  1551. ----------
  1552. .. [1] W. Carrara, R. Goodman, and R. Majewski, "Spotlight Synthetic
  1553. Aperture Radar: Signal Processing Algorithms" Pages 512-513,
  1554. July 1995.
  1555. .. [2] Armin Doerry, "Catalog of Window Taper Functions for
  1556. Sidelobe Control", 2017.
  1557. 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
  1558. Examples
  1559. --------
  1560. Plot the window and its frequency response:
  1561. >>> import numpy as np
  1562. >>> from scipy import signal
  1563. >>> from scipy.fft import fft, fftshift
  1564. >>> import matplotlib.pyplot as plt
  1565. >>> window = signal.windows.taylor(51, nbar=20, sll=100, norm=False)
  1566. >>> plt.plot(window)
  1567. >>> plt.title("Taylor window (100 dB)")
  1568. >>> plt.ylabel("Amplitude")
  1569. >>> plt.xlabel("Sample")
  1570. >>> plt.figure()
  1571. >>> A = fft(window, 2048) / (len(window)/2.0)
  1572. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1573. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1574. >>> plt.plot(freq, response)
  1575. >>> plt.axis([-0.5, 0.5, -120, 0])
  1576. >>> plt.title("Frequency response of the Taylor window (100 dB)")
  1577. >>> plt.ylabel("Normalized magnitude [dB]")
  1578. >>> plt.xlabel("Normalized frequency [cycles per sample]")
  1579. """ # noqa: E501
  1580. xp = _namespace(xp)
  1581. if _len_guards(M):
  1582. return xp.ones(M, dtype=xp.float64, device=device)
  1583. M, needs_trunc = _extend(M, sym)
  1584. # Original text uses a negative sidelobe level parameter and then negates
  1585. # it in the calculation of B. To keep consistent with other methods we
  1586. # assume the sidelobe level parameter to be positive.
  1587. B = xp.asarray(10**(sll / 20), device=device)
  1588. A = xp.acosh(B) / xp.pi
  1589. s2 = nbar**2 / (A**2 + (nbar - 0.5)**2)
  1590. ma = xp.arange(1, nbar, dtype=xp.float64, device=device)
  1591. Fm = xp.empty(nbar - 1, dtype=xp.float64, device=device)
  1592. signs = xp.empty_like(ma)
  1593. signs[::2] = 1
  1594. signs[1::2] = -1
  1595. m2 = ma*ma
  1596. for mi, m in enumerate(ma):
  1597. numer = signs[mi] * xp.prod(1 - m2[mi]/s2/(A**2 + (ma - 0.5)**2))
  1598. denom = 2 * xp.prod(1 - m2[mi]/m2[:mi]) * xp.prod(1 - m2[mi]/m2[mi+1:])
  1599. Fm[mi] = numer / denom
  1600. def W(n):
  1601. return 1 + 2*xp.matmul(Fm, xp.cos(
  1602. 2*xp.pi*ma[:, xp.newaxis]*(n-M/2.+0.5)/M))
  1603. w = W(xp.arange(M, dtype=xp.float64, device=device))
  1604. # normalize (Note that this is not described in the original text [1])
  1605. if norm:
  1606. scale = 1.0 / W((M - 1) / 2)
  1607. w *= scale
  1608. return _truncate(w, needs_trunc)
  1609. @xp_capabilities(np_only=True, reason='banded linear algebra is numpy-only')
  1610. def dpss(M, NW, Kmax=None, sym=True, norm=None, return_ratios=False,
  1611. *, xp=None, device=None):
  1612. """
  1613. Compute the Discrete Prolate Spheroidal Sequences (DPSS).
  1614. DPSS (or Slepian sequences) are often used in multitaper power spectral
  1615. density estimation (see [1]_). The first window in the sequence can be
  1616. used to maximize the energy concentration in the main lobe, and is also
  1617. called the Slepian window.
  1618. Parameters
  1619. ----------
  1620. M : int
  1621. Window length.
  1622. NW : float
  1623. Standardized half bandwidth corresponding to ``2*NW = BW/f0 = BW*M*dt``
  1624. where ``dt`` is taken as 1.
  1625. Kmax : int | None, optional
  1626. Number of DPSS windows to return (orders ``0`` through ``Kmax-1``).
  1627. If None (default), return only a single window of shape ``(M,)``
  1628. instead of an array of windows of shape ``(Kmax, M)``.
  1629. sym : bool, optional
  1630. When True (default), generates a symmetric window, for use in filter
  1631. design.
  1632. When False, generates a periodic window, for use in spectral analysis.
  1633. norm : {2, 'approximate', 'subsample'} | None, optional
  1634. If 'approximate' or 'subsample', then the windows are normalized by the
  1635. maximum, and a correction scale-factor for even-length windows
  1636. is applied either using ``M**2/(M**2+NW)`` ("approximate") or
  1637. a FFT-based subsample shift ("subsample"), see Notes for details.
  1638. If None, then "approximate" is used when ``Kmax=None`` and 2 otherwise
  1639. (which uses the l2 norm).
  1640. return_ratios : bool, optional
  1641. If True, also return the concentration ratios in addition to the
  1642. windows.
  1643. %(xp_device_snippet)s
  1644. Returns
  1645. -------
  1646. v : ndarray, shape (Kmax, M) or (M,)
  1647. The DPSS windows. Will be 1D if `Kmax` is None.
  1648. r : ndarray, shape (Kmax,) or float, optional
  1649. The concentration ratios for the windows. Only returned if
  1650. `return_ratios` evaluates to True. Will be 0D if `Kmax` is None.
  1651. Notes
  1652. -----
  1653. This computation uses the tridiagonal eigenvector formulation given
  1654. in [2]_.
  1655. The default normalization for ``Kmax=None``, i.e. window-generation mode,
  1656. simply using the l-infinity norm would create a window with two unity
  1657. values, which creates slight normalization differences between even and odd
  1658. orders. The approximate correction of ``M**2/float(M**2+NW)`` for even
  1659. sample numbers is used to counteract this effect (see Examples below).
  1660. For very long signals (e.g., 1e6 elements), it can be useful to compute
  1661. windows orders of magnitude shorter and use interpolation (e.g.,
  1662. `scipy.interpolate.interp1d`) to obtain tapers of length `M`,
  1663. but this in general will not preserve orthogonality between the tapers.
  1664. .. versionadded:: 1.1
  1665. References
  1666. ----------
  1667. .. [1] Percival DB, Walden WT. Spectral Analysis for Physical Applications:
  1668. Multitaper and Conventional Univariate Techniques.
  1669. Cambridge University Press; 1993.
  1670. .. [2] Slepian, D. Prolate spheroidal wave functions, Fourier analysis, and
  1671. uncertainty V: The discrete case. Bell System Technical Journal,
  1672. Volume 57 (1978), 1371430.
  1673. .. [3] Kaiser, JF, Schafer RW. On the Use of the I0-Sinh Window for
  1674. Spectrum Analysis. IEEE Transactions on Acoustics, Speech and
  1675. Signal Processing. ASSP-28 (1): 105-107; 1980.
  1676. Examples
  1677. --------
  1678. We can compare the window to `kaiser`, which was invented as an alternative
  1679. that was easier to calculate [3]_ (example adapted from
  1680. `here <https://ccrma.stanford.edu/~jos/sasp/Kaiser_DPSS_Windows_Compared.html>`_):
  1681. >>> import numpy as np
  1682. >>> import matplotlib.pyplot as plt
  1683. >>> from scipy.signal import windows, freqz
  1684. >>> M = 51
  1685. >>> fig, axes = plt.subplots(3, 2, figsize=(5, 7))
  1686. >>> for ai, alpha in enumerate((1, 3, 5)):
  1687. ... win_dpss = windows.dpss(M, alpha)
  1688. ... beta = alpha*np.pi
  1689. ... win_kaiser = windows.kaiser(M, beta)
  1690. ... for win, c in ((win_dpss, 'k'), (win_kaiser, 'r')):
  1691. ... win /= win.sum()
  1692. ... axes[ai, 0].plot(win, color=c, lw=1.)
  1693. ... axes[ai, 0].set(xlim=[0, M-1], title=rf'$\\alpha$ = {alpha}',
  1694. ... ylabel='Amplitude')
  1695. ... w, h = freqz(win)
  1696. ... axes[ai, 1].plot(w, 20 * np.log10(np.abs(h)), color=c, lw=1.)
  1697. ... axes[ai, 1].set(xlim=[0, np.pi],
  1698. ... title=rf'$\\beta$ = {beta:0.2f}',
  1699. ... ylabel='Magnitude (dB)')
  1700. >>> for ax in axes.ravel():
  1701. ... ax.grid(True)
  1702. >>> axes[2, 1].legend(['DPSS', 'Kaiser'])
  1703. >>> fig.tight_layout()
  1704. >>> plt.show()
  1705. And here are examples of the first four windows, along with their
  1706. concentration ratios:
  1707. >>> M = 512
  1708. >>> NW = 2.5
  1709. >>> win, eigvals = windows.dpss(M, NW, 4, return_ratios=True)
  1710. >>> fig, ax = plt.subplots(1)
  1711. >>> ax.plot(win.T, linewidth=1.)
  1712. >>> ax.set(xlim=[0, M-1], ylim=[-0.1, 0.1], xlabel='Samples',
  1713. ... title=f'DPSS, {M:d}, {NW:0.1f}')
  1714. >>> ax.legend([f'win[{ii}] ({ratio:0.4f})'
  1715. ... for ii, ratio in enumerate(eigvals)])
  1716. >>> fig.tight_layout()
  1717. >>> plt.show()
  1718. Using a standard :math:`l_{\\infty}` norm would produce two unity values
  1719. for even `M`, but only one unity value for odd `M`. This produces uneven
  1720. window power that can be counteracted by the approximate correction
  1721. ``M**2/float(M**2+NW)``, which can be selected by using
  1722. ``norm='approximate'`` (which is the same as ``norm=None`` when
  1723. ``Kmax=None``, as is the case here). Alternatively, the slower
  1724. ``norm='subsample'`` can be used, which uses subsample shifting in the
  1725. frequency domain (FFT) to compute the correction:
  1726. >>> Ms = np.arange(1, 41)
  1727. >>> factors = (50, 20, 10, 5, 2.0001)
  1728. >>> energy = np.empty((3, len(Ms), len(factors)))
  1729. >>> for mi, M in enumerate(Ms):
  1730. ... for fi, factor in enumerate(factors):
  1731. ... NW = M / float(factor)
  1732. ... # Corrected using empirical approximation (default)
  1733. ... win = windows.dpss(M, NW)
  1734. ... energy[0, mi, fi] = np.sum(win ** 2) / np.sqrt(M)
  1735. ... # Corrected using subsample shifting
  1736. ... win = windows.dpss(M, NW, norm='subsample')
  1737. ... energy[1, mi, fi] = np.sum(win ** 2) / np.sqrt(M)
  1738. ... # Uncorrected (using l-infinity norm)
  1739. ... win /= win.max()
  1740. ... energy[2, mi, fi] = np.sum(win ** 2) / np.sqrt(M)
  1741. >>> fig, ax = plt.subplots(1)
  1742. >>> hs = ax.plot(Ms, energy[2], '-o', markersize=4,
  1743. ... markeredgecolor='none')
  1744. >>> leg = [hs[-1]]
  1745. >>> for hi, hh in enumerate(hs):
  1746. ... h1 = ax.plot(Ms, energy[0, :, hi], '-o', markersize=4,
  1747. ... color=hh.get_color(), markeredgecolor='none',
  1748. ... alpha=0.66)
  1749. ... h2 = ax.plot(Ms, energy[1, :, hi], '-o', markersize=4,
  1750. ... color=hh.get_color(), markeredgecolor='none',
  1751. ... alpha=0.33)
  1752. ... if hi == len(hs) - 1:
  1753. ... leg.insert(0, h1[0])
  1754. ... leg.insert(0, h2[0])
  1755. >>> ax.set(xlabel='M (samples)', ylabel=r'Power / $\\sqrt{M}$')
  1756. >>> ax.legend(leg, ['Uncorrected', r'Corrected: $\\frac{M^2}{M^2+NW}$',
  1757. ... 'Corrected (subsample)'])
  1758. >>> fig.tight_layout()
  1759. """
  1760. xp = _namespace(xp)
  1761. if norm is None:
  1762. norm = 'approximate' if Kmax is None else 2
  1763. known_norms = (2, 'approximate', 'subsample')
  1764. if norm not in known_norms:
  1765. raise ValueError(f'norm must be one of {known_norms}, got {norm}')
  1766. if Kmax is None:
  1767. singleton = True
  1768. Kmax = 1
  1769. else:
  1770. singleton = False
  1771. if _len_guards(M):
  1772. if not return_ratios:
  1773. return xp.ones(M, dtype=xp.float64)
  1774. elif singleton:
  1775. return xp.ones(M, dtype=xp.float64), 1.
  1776. else:
  1777. return xp.ones(M, dtype=xp.float64), xp.ones(1, dtype=xp.float64)
  1778. Kmax = operator.index(Kmax)
  1779. if not 0 < Kmax <= M:
  1780. raise ValueError('Kmax must be greater than 0 and less than M')
  1781. if NW >= M/2.:
  1782. raise ValueError('NW must be less than M/2.')
  1783. if NW <= 0:
  1784. raise ValueError('NW must be positive')
  1785. M, needs_trunc = _extend(M, sym)
  1786. W = float(NW) / M
  1787. nidx = xp.arange(M, dtype=xp.float64, device=device)
  1788. # Here we want to set up an optimization problem to find a sequence
  1789. # whose energy is maximally concentrated within band [-W,W].
  1790. # Thus, the measure lambda(T,W) is the ratio between the energy within
  1791. # that band, and the total energy. This leads to the eigen-system
  1792. # (A - (l1)I)v = 0, where the eigenvector corresponding to the largest
  1793. # eigenvalue is the sequence with maximally concentrated energy. The
  1794. # collection of eigenvectors of this system are called Slepian
  1795. # sequences, or discrete prolate spheroidal sequences (DPSS). Only the
  1796. # first K, K = 2NW/dt orders of DPSS will exhibit good spectral
  1797. # concentration
  1798. # [see https://en.wikipedia.org/wiki/Spectral_concentration_problem]
  1799. # Here we set up an alternative symmetric tri-diagonal eigenvalue
  1800. # problem such that
  1801. # (B - (l2)I)v = 0, and v are our DPSS (but eigenvalues l2 != l1)
  1802. # the main diagonal = ([M-1-2*t]/2)**2 cos(2PIW), t=[0,1,2,...,M-1]
  1803. # and the first off-diagonal = t(M-t)/2, t=[1,2,...,M-1]
  1804. # [see Percival and Walden, 1993]
  1805. d = ((M - 1 - 2 * nidx) / 2.) ** 2 * xp.cos(xp.asarray(2 * xp.pi * W))
  1806. e = nidx[1:] * (M - nidx[1:]) / 2.
  1807. # only calculate the highest Kmax eigenvalues
  1808. w, windows = linalg.eigh_tridiagonal(
  1809. d, e, select='i', select_range=(M - Kmax, M - 1))
  1810. w = w[::-1]
  1811. windows = windows[:, ::-1].T
  1812. # By convention (Percival and Walden, 1993 pg 379)
  1813. # * symmetric tapers (k=0,2,4,...) should have a positive average.
  1814. fix_even = (windows[::2, ...].sum(axis=1) < 0)
  1815. for i, f in enumerate(fix_even):
  1816. if f:
  1817. windows[2 * i] *= -1
  1818. # * antisymmetric tapers should begin with a positive lobe
  1819. # (this depends on the definition of "lobe", here we'll take the first
  1820. # point above the numerical noise, which should be good enough for
  1821. # sufficiently smooth functions, and more robust than relying on an
  1822. # algorithm that uses max(abs(w)), which is susceptible to numerical
  1823. # noise problems)
  1824. thresh = max(1e-7, 1. / M)
  1825. for i, w in enumerate(windows[1::2, ...]):
  1826. if w[w * w > thresh][0] < 0:
  1827. windows[2 * i + 1] *= -1
  1828. # Now find the eigenvalues of the original spectral concentration problem
  1829. # Use the autocorr sequence technique from Percival and Walden, 1993 pg 390
  1830. if return_ratios:
  1831. dpss_rxx = _fftautocorr(xp.asarray(windows))
  1832. r = 4 * W * xpx.sinc(xp.asarray(2 * W * nidx), xp=xp)
  1833. r[0] = 2 * W
  1834. ratios = xp.matmul(dpss_rxx, r)
  1835. if singleton:
  1836. ratios = ratios[0]
  1837. ratios = xp.asarray(ratios, device=device)
  1838. # Deal with sym and Kmax=None
  1839. if norm != 2:
  1840. windows /= windows.max()
  1841. if M % 2 == 0:
  1842. if norm == 'approximate':
  1843. correction = M**2 / float(M**2 + NW)
  1844. else:
  1845. s = sp_fft.rfft(windows[0])
  1846. shift = -(1 - 1./M) * xp.arange(1, M//2 + 1, dtype=xp.float64)
  1847. s[1:] *= 2 * xp.exp(-1j * xp.pi * shift)
  1848. correction = M / s.real.sum()
  1849. windows *= correction
  1850. # else we're already l2 normed, so do nothing
  1851. if needs_trunc:
  1852. windows = windows[:, :-1]
  1853. if singleton:
  1854. windows = windows[0]
  1855. windows = xp.asarray(windows, device=device)
  1856. return (windows, ratios) if return_ratios else windows
  1857. @xp_capabilities()
  1858. def lanczos(M, *, sym=True, xp=None, device=None):
  1859. r"""Return a Lanczos window also known as a sinc window.
  1860. Parameters
  1861. ----------
  1862. M : int
  1863. Number of points in the output window. If zero, an empty array
  1864. is returned. An exception is thrown when it is negative.
  1865. sym : bool, optional
  1866. When True (default), generates a symmetric window, for use in filter
  1867. design.
  1868. When False, generates a periodic window, for use in spectral analysis.
  1869. %(xp_device_snippet)s
  1870. Returns
  1871. -------
  1872. w : ndarray
  1873. The window, with the maximum value normalized to 1 (though the value 1
  1874. does not appear if `M` is even and `sym` is True).
  1875. Notes
  1876. -----
  1877. The Lanczos window is defined as
  1878. .. math:: w(n) = sinc \left( \frac{2n}{M - 1} - 1 \right)
  1879. where
  1880. .. math:: sinc(x) = \frac{\sin(\pi x)}{\pi x}
  1881. The Lanczos window has reduced Gibbs oscillations and is widely used for
  1882. filtering climate timeseries with good properties in the physical and
  1883. spectral domains.
  1884. .. versionadded:: 1.10
  1885. References
  1886. ----------
  1887. .. [1] Lanczos, C., and Teichmann, T. (1957). Applied analysis.
  1888. Physics Today, 10, 44.
  1889. .. [2] Duchon C. E. (1979) Lanczos Filtering in One and Two Dimensions.
  1890. Journal of Applied Meteorology, Vol 18, pp 1016-1022.
  1891. .. [3] Thomson, R. E. and Emery, W. J. (2014) Data Analysis Methods in
  1892. Physical Oceanography (Third Edition), Elsevier, pp 593-637.
  1893. .. [4] Wikipedia, "Window function",
  1894. http://en.wikipedia.org/wiki/Window_function
  1895. Examples
  1896. --------
  1897. Plot the window
  1898. >>> import numpy as np
  1899. >>> from scipy.signal.windows import lanczos
  1900. >>> from scipy.fft import fft, fftshift
  1901. >>> import matplotlib.pyplot as plt
  1902. >>> fig, ax = plt.subplots(1)
  1903. >>> window = lanczos(51)
  1904. >>> ax.plot(window)
  1905. >>> ax.set_title("Lanczos window")
  1906. >>> ax.set_ylabel("Amplitude")
  1907. >>> ax.set_xlabel("Sample")
  1908. >>> fig.tight_layout()
  1909. >>> plt.show()
  1910. and its frequency response:
  1911. >>> fig, ax = plt.subplots(1)
  1912. >>> A = fft(window, 2048) / (len(window)/2.0)
  1913. >>> freq = np.linspace(-0.5, 0.5, len(A))
  1914. >>> response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
  1915. >>> ax.plot(freq, response)
  1916. >>> ax.set_xlim(-0.5, 0.5)
  1917. >>> ax.set_ylim(-120, 0)
  1918. >>> ax.set_title("Frequency response of the lanczos window")
  1919. >>> ax.set_ylabel("Normalized magnitude [dB]")
  1920. >>> ax.set_xlabel("Normalized frequency [cycles per sample]")
  1921. >>> fig.tight_layout()
  1922. >>> plt.show()
  1923. """
  1924. xp = _namespace(xp)
  1925. if _len_guards(M):
  1926. return xp.ones(M, dtype=xp.float64, device=device)
  1927. M, needs_trunc = _extend(M, sym)
  1928. # To make sure that the window is symmetric, we concatenate the right hand
  1929. # half of the window and the flipped one which is the left hand half of
  1930. # the window.
  1931. def _calc_right_side_lanczos(n, m):
  1932. return xpx.sinc(2. * xp.arange(n, m, dtype=xp.float64) / (m - 1) - 1.0, xp=xp)
  1933. if M % 2 == 0:
  1934. wh = _calc_right_side_lanczos(M/2, M)
  1935. w = xp.concat([xp.flip(wh), wh])
  1936. else:
  1937. wh = _calc_right_side_lanczos((M+1)/2, M)
  1938. w = xp.concat([xp.flip(wh), xp.ones(1), wh])
  1939. return _truncate(w, needs_trunc)
  1940. def _fftautocorr(x):
  1941. """Compute the autocorrelation of a real array and crop the result."""
  1942. N = x.shape[-1]
  1943. use_N = sp_fft.next_fast_len(2*N-1)
  1944. x_fft = sp_fft.rfft(x, use_N, axis=-1)
  1945. cxy = sp_fft.irfft(x_fft * x_fft.conj(), n=use_N)[:, :N]
  1946. # Or equivalently (but in most cases slower):
  1947. # cxy = xp.asarray([xp.convolve(xx, yy[::-1], mode='full')
  1948. # for xx, yy in zip(x, x)])[:, N-1:2*N-1]
  1949. return cxy
  1950. _WIN_FUNC_DATA = { # Format: {(name0, name1, ...): (function, needs parameters)
  1951. ('barthann', 'brthan', 'bth'): (barthann, False),
  1952. ('bartlett', 'bart', 'brt'): (bartlett, False),
  1953. ('blackman', 'black', 'blk'): (blackman, False),
  1954. ('blackmanharris', 'blackharr', 'bkh'): (blackmanharris, False),
  1955. ('bohman', 'bman', 'bmn'): (bohman, False),
  1956. ('boxcar', 'box', 'ones', 'rect', 'rectangular'): (boxcar, False),
  1957. ('chebwin', 'cheb'): (chebwin, True),
  1958. ('cosine', 'halfcosine'): (cosine, False),
  1959. ('dpss',): (dpss, True),
  1960. ('exponential', 'poisson'): (exponential, 'OPTIONAL'),
  1961. ('flattop', 'flat', 'flt'): (flattop, False),
  1962. ('gaussian', 'gauss', 'gss'): (gaussian, True),
  1963. ('general cosine', 'general_cosine'): (general_cosine, True),
  1964. ('general gaussian', 'general_gaussian', 'general gauss',
  1965. 'general_gauss', 'ggs'): (general_gaussian, True),
  1966. ('general hamming', 'general_hamming'): (general_hamming, True),
  1967. ('hamming', 'hamm', 'ham'): (hamming, False),
  1968. ('hann', 'han'): (hann, False),
  1969. ('kaiser', 'ksr'): (kaiser, True),
  1970. ('kaiser bessel derived', 'kaiser_bessel_derived',
  1971. 'kbd'): (kaiser_bessel_derived, True),
  1972. ('lanczos', 'sinc'): (lanczos, False),
  1973. ('nuttall', 'nutl', 'nut'): (nuttall, False),
  1974. ('parzen', 'parz', 'par'): (parzen, False),
  1975. ('taylor', 'taylorwin'): (taylor, 'OPTIONAL'),
  1976. ('triangle', 'triang', 'tri'): (triang, False),
  1977. ('tukey', 'tuk'): (tukey, 'OPTIONAL'), }
  1978. _WIN_FUNCS = dict()
  1979. for nn_, v_ in _WIN_FUNC_DATA.items():
  1980. _WIN_FUNCS.update({n_: v_ for n_ in nn_})
  1981. @xp_capabilities()
  1982. def get_window(window, Nx, fftbins=True, *, xp=None, device=None):
  1983. r"""Convenience function for creating various windows.
  1984. This function is a wrapper for the window functions provided in the
  1985. `scipy.signal.windows` namespace.
  1986. Parameters
  1987. ----------
  1988. window : str | tuple | float
  1989. Either a string with the window name or a tuple consisting of window name and
  1990. window parameters. If it is a float, a `~scipy.singal.kaiser` window is
  1991. created with `window` being the shape parameter. Consult the Notes below for
  1992. more details.
  1993. Nx : int
  1994. The number of samples in the window.
  1995. fftbins : bool, optional
  1996. If ``True`` (default), create a periodic window, ready to use with `ifftshift`
  1997. and be multiplied by the result of an FFT (see also
  1998. :func:`~scipy.fft.fftfreq`). If ``False``, create a symmetric window, for use
  1999. in filter design. This parameter is ignored, if the window name in the
  2000. `window` parameter has a suffix ``'_periodic'`` or ``'_symmetric'`` appended to
  2001. it (e.g., ``'hann_symmetric'``).
  2002. %(xp_device_snippet)s
  2003. Returns
  2004. -------
  2005. get_window : ndarray
  2006. Returns the created window as a one-dimensional array made of `Nx` samples.
  2007. Raises
  2008. ------
  2009. ValueError
  2010. If the provided parameters do not allow to choose a valid window function
  2011. with valid parameters.
  2012. Notes
  2013. -----
  2014. Note that by default this function returns a periodic window, whereas the wrapped
  2015. window functions return a symmetric window by default. This is caused by the
  2016. `fftbins` parameter having the inverse meaning of the `sym` parameter of the
  2017. wrapped function, which are both ``True`` by default.
  2018. .. currentmodule:: scipy.signal.windows
  2019. The following list shows the wrapped window functions with the respective settings
  2020. of the `window` parameter followed by a short description. Aliases for alternative
  2021. window names are given in parenthesis.
  2022. `barthann` / ``'barthann'``:
  2023. Modified Bartlett-Hann window (aliases: ``'brthan', 'bth'``)
  2024. `bartlett` / ``'bartlett'``:
  2025. Bartlett window (aliases: ``'bart', 'brt'``)
  2026. `blackman`/ ``'blackman'``:
  2027. Blackman window (aliases: ``'black', 'blk'``)
  2028. `blackmanharris` / ``'blackmanharris'``:
  2029. 4-term Blackman-Harris window (aliases: ``'blackharr', 'bkh'``)
  2030. `bohman` / ``'bohman'``:
  2031. Bohman window (aliases: ``'bman', 'bmn'``)
  2032. `boxcar` / ``'boxcar'``:
  2033. Rectangular window (aliases: ``'box', 'ones', 'rect', 'rectangular'``)
  2034. `chebwin` / ``('chebwin', at)``:
  2035. Dolph-Chebyshev window with `at` dB attenuation (aliases: ``'cheb'``)
  2036. `cosine` / ``'cosine'``:
  2037. Cosine window (aliases: ``'halfcosine'``)
  2038. `dpss` / ``('dpss', NW)``:
  2039. First window of discrete prolate spheroidal sequence with standardized half
  2040. bandwidth ``NW`` and "approximate" norm.
  2041. `exponential` / ``'exponential'`` / ``('exponential', center, tau)``:
  2042. Exponential / Poisson window centered at ``center`` (default: ``None``) with
  2043. deacy ``tau`` (default: ``1``) (aliases: ``'poisson'``)
  2044. `flattop`/ ``'flattop'``:
  2045. Flat top window (aliases: ``'flat', 'flt'``)
  2046. `gaussian` / ``('gaussian', std)``:
  2047. Gaussian with standard deviation `std` (aliases: ``'gauss', 'gss'``)
  2048. `general_cosine` / ``('general cosine', a)``:
  2049. Generic weighted sum of cosine terms with weighting coefficients ``a``
  2050. (aliases: ``'general_cosine'``)
  2051. `general_gaussian` / ``('general gaussian', p, sig)``:
  2052. Generalized Gaussian with shape parameter ``p`` and standard deviation ``sig``
  2053. (aliases: ``'general_gaussian', 'general gauss', 'general_gauss', 'ggs'``)
  2054. `general_hamming` / ``('general hamming', alpha)``:
  2055. Generalized Hamming window with coefficent ``alpha``
  2056. (aliases: ``'general_hamming'``)
  2057. `hamming` / ``'hamming'``:
  2058. Hamming window (aliases: ``'hamm', 'ham'``)
  2059. `hann` / ``'hann'``:
  2060. Hann window (aliases: ``'han'``)
  2061. `kaiser` / ``('kaiser', beta)``:
  2062. Kaiser window with shape parameter ``beta`` (aliases: ``'ksr'``)
  2063. `kaiser_bessel_derived` / ``('kaiser bessel derived', beta)``:
  2064. Kaiser-Bessel derived window with shape parameter ``beta``
  2065. (aliases: ``'kaiser_bessel_derived', 'kbd'``)
  2066. `lanczos` / ``'lanczos'``:
  2067. Lanczos / sinc window (aliases: ``'sinc'``)
  2068. `nuttall`/ ``'nuttall'``:
  2069. Minimum 4-term Blackman-Harris window according to Nuttall
  2070. (aliases: ``'nutl', 'nut'``)
  2071. `parzen` / ``'parzen'``:
  2072. Parzen window (aliases: ``'parz', 'par'``)
  2073. `taylor` / ``'taylor'`` / (``'taylor', nbar, sll, norm)``:
  2074. Taylor window with ``nbar`` adjascent sidelobes (default: ``4``)), ``sll`` dB
  2075. suppression level (default: ``30``) and boolean value ``norm``
  2076. (default: ``True``) (aliases: ``taylorwin``)
  2077. `triang`/ ``'triangle'``:
  2078. Triangle window (aliases: ``'triang', 'tri'``)
  2079. `tukey` / ``'tukey'`` / ``('tukey', alpha)``:
  2080. Tukey window with shape parameter ``alpha`` (default: ``0.5``)
  2081. (aliases: ``'tuk'``)
  2082. Examples
  2083. --------
  2084. This example shows different usages of the `window` parameter:
  2085. >>> from scipy.signal import get_window
  2086. >>> get_window('triang', 7)
  2087. array([ 0.125, 0.375, 0.625, 0.875, 0.875, 0.625, 0.375])
  2088. >>> get_window(('exponential', None, 1.), 9)
  2089. array([ 0.011109 , 0.03019738, 0.082085 , 0.22313016, 0.60653066,
  2090. 0.60653066, 0.22313016, 0.082085 , 0.03019738])
  2091. >>> get_window(('kaiser', 4.0), 9)
  2092. array([ 0.08848053, 0.29425961, 0.56437221, 0.82160913, 0.97885093,
  2093. 0.97885093, 0.82160913, 0.56437221, 0.29425961])
  2094. >>> get_window(4.0, 9) # same as previous call
  2095. array([ 0.08848053, 0.29425961, 0.56437221, 0.82160913, 0.97885093,
  2096. 0.97885093, 0.82160913, 0.56437221, 0.29425961])
  2097. The following snippet shows different ways to create identical symmetric and
  2098. periodic Bartlett windows:
  2099. >>> from scipy.signal import get_window, windows
  2100. >>> # Symmetric window:
  2101. >>> windows.bartlett(5) # Parameter `sym` defaults to True
  2102. array([0. , 0.5, 1. , 0.5, 0. ])
  2103. >>> get_window('bartlett', 5, fftbins=False)
  2104. array([0. , 0.5, 1. , 0.5, 0. ])
  2105. >>> get_window('bartlett_symmetric', 5)
  2106. array([0. , 0.5, 1. , 0.5, 0. ])
  2107. >>> # Periodic window:
  2108. >>> windows.bartlett(4, sym=False)
  2109. array([0. , 0.5, 1. , 0.5])
  2110. >>> get_window('bartlett', 4) # Parameter `fftbins` defaults to True
  2111. array([0. , 0.5, 1. , 0.5])
  2112. >>> get_window('bartlett_periodic', 4)
  2113. array([0. , 0.5, 1. , 0.5])
  2114. >>> # `_periodic' suffix overrides `fftbins` parameter:
  2115. >>> get_window('bartlett_periodic', 4, fftbins=False)
  2116. array([0. , 0.5, 1. , 0.5])
  2117. Note that a periodic window can be created out of a symmetric window by discarding
  2118. the last sample.
  2119. """
  2120. if not (Nx > 0 and isinstance(Nx, numbers.Integral)):
  2121. raise ValueError(f"Parameter {Nx=} is not a positive integer")
  2122. if not isinstance(fftbins, bool):
  2123. raise ValueError(f"Parameter {fftbins=} is not of type bool!")
  2124. if not isinstance(window, str | tuple):
  2125. try: # if parameter window can be converted to a float, return kaiser window:
  2126. beta = float(window)
  2127. except Exception as float_exception:
  2128. err_msg = f"Parameter {window=} must be a tuple, a string or a float!"
  2129. raise ValueError(err_msg) from float_exception
  2130. return kaiser(Nx, beta, not fftbins, xp=xp, device=device)
  2131. if isinstance(window, tuple) and not isinstance(window[0], str):
  2132. raise ValueError(f"First tuple entry of parameter {window=} is not a str!")
  2133. sym = not fftbins
  2134. win_name = window if isinstance(window, str) else window[0]
  2135. if win_name.endswith('_symmetric'): # overwrite `fftbins` / `sym` if needed
  2136. sym, win_name = True, win_name[:-10] # remove '_symmetric' from `win_name`
  2137. elif win_name.endswith('_periodic'):
  2138. sym, win_name = False, win_name[:-9] # remove '_periodic' from `win_name`
  2139. if win_name not in _WIN_FUNCS:
  2140. raise ValueError(f"Invalid window name '{win_name}' in parameter {window=}!")
  2141. func, has_args = _WIN_FUNCS[win_name]
  2142. args = window[1:] if isinstance(window, tuple) else tuple()
  2143. if len(args) > 0 and has_args is False:
  2144. raise ValueError(f"'{win_name}' does not allow parameters, but {window=}!")
  2145. if len(args) == 0 and has_args is True:
  2146. raise ValueError(f"'{win_name}' must have parameters, but {window=}!")
  2147. # has_args == 'OPTIONAL' allows len(args) == 0 as well as len(args) > 0
  2148. if not has_args:
  2149. return func(Nx, sym=sym, xp=xp, device=device)
  2150. # special cases taken from original implementation:
  2151. if func is dpss:
  2152. if len(args) != 1:
  2153. raise ValueError(f"Window {win_name} must have one parameter but {window=}")
  2154. return dpss(Nx, args[0], Kmax=None, sym=sym, xp=xp, device=device)
  2155. if func is general_cosine:
  2156. if not (xp is None and device is None):
  2157. raise ValueError("'general_cosine' does not accept the parameters xp " +
  2158. "and device not being None!")
  2159. return general_cosine(Nx, *args, sym=sym)
  2160. return func(Nx, *args, sym=sym, xp=xp, device=device)
  2161. ########## complete the docstrings, on import
  2162. _xp_device_snippet = {'xp_device_snippet':
  2163. """\
  2164. xp : array_namespace, optional
  2165. Optional array namespace.
  2166. Should be compatible with the array API standard, or supported by array-api-compat.
  2167. Default: ``numpy``
  2168. device: any
  2169. optional device specification for output. Should match one of the
  2170. supported device specification in ``xp``.
  2171. """
  2172. }
  2173. _names = [x for x in __all__ if x != 'general_cosine']
  2174. for name in _names:
  2175. window = vars()[name]
  2176. window.__doc__ = doccer.docformat(window.__doc__, _xp_device_snippet)