test_spectral.py 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602
  1. import numpy as np
  2. from numpy.testing import (assert_, assert_approx_equal,
  3. assert_allclose, assert_array_equal, assert_equal,
  4. assert_array_almost_equal_nulp, suppress_warnings)
  5. import pytest
  6. from pytest import raises as assert_raises
  7. from scipy import signal
  8. from scipy.fft import fftfreq
  9. from scipy.signal import (periodogram, welch, lombscargle, csd, coherence,
  10. spectrogram, stft, istft, check_COLA, check_NOLA)
  11. from scipy.signal._spectral_py import _spectral_helper
  12. class TestPeriodogram:
  13. def test_real_onesided_even(self):
  14. x = np.zeros(16)
  15. x[0] = 1
  16. f, p = periodogram(x)
  17. assert_allclose(f, np.linspace(0, 0.5, 9))
  18. q = np.ones(9)
  19. q[0] = 0
  20. q[-1] /= 2.0
  21. q /= 8
  22. assert_allclose(p, q)
  23. def test_real_onesided_odd(self):
  24. x = np.zeros(15)
  25. x[0] = 1
  26. f, p = periodogram(x)
  27. assert_allclose(f, np.arange(8.0)/15.0)
  28. q = np.ones(8)
  29. q[0] = 0
  30. q *= 2.0/15.0
  31. assert_allclose(p, q, atol=1e-15)
  32. def test_real_twosided(self):
  33. x = np.zeros(16)
  34. x[0] = 1
  35. f, p = periodogram(x, return_onesided=False)
  36. assert_allclose(f, fftfreq(16, 1.0))
  37. q = np.full(16, 1/16.0)
  38. q[0] = 0
  39. assert_allclose(p, q)
  40. def test_real_spectrum(self):
  41. x = np.zeros(16)
  42. x[0] = 1
  43. f, p = periodogram(x, scaling='spectrum')
  44. g, q = periodogram(x, scaling='density')
  45. assert_allclose(f, np.linspace(0, 0.5, 9))
  46. assert_allclose(p, q/16.0)
  47. def test_integer_even(self):
  48. x = np.zeros(16, dtype=int)
  49. x[0] = 1
  50. f, p = periodogram(x)
  51. assert_allclose(f, np.linspace(0, 0.5, 9))
  52. q = np.ones(9)
  53. q[0] = 0
  54. q[-1] /= 2.0
  55. q /= 8
  56. assert_allclose(p, q)
  57. def test_integer_odd(self):
  58. x = np.zeros(15, dtype=int)
  59. x[0] = 1
  60. f, p = periodogram(x)
  61. assert_allclose(f, np.arange(8.0)/15.0)
  62. q = np.ones(8)
  63. q[0] = 0
  64. q *= 2.0/15.0
  65. assert_allclose(p, q, atol=1e-15)
  66. def test_integer_twosided(self):
  67. x = np.zeros(16, dtype=int)
  68. x[0] = 1
  69. f, p = periodogram(x, return_onesided=False)
  70. assert_allclose(f, fftfreq(16, 1.0))
  71. q = np.full(16, 1/16.0)
  72. q[0] = 0
  73. assert_allclose(p, q)
  74. def test_complex(self):
  75. x = np.zeros(16, np.complex128)
  76. x[0] = 1.0 + 2.0j
  77. f, p = periodogram(x, return_onesided=False)
  78. assert_allclose(f, fftfreq(16, 1.0))
  79. q = np.full(16, 5.0/16.0)
  80. q[0] = 0
  81. assert_allclose(p, q)
  82. def test_unk_scaling(self):
  83. assert_raises(ValueError, periodogram, np.zeros(4, np.complex128),
  84. scaling='foo')
  85. def test_nd_axis_m1(self):
  86. x = np.zeros(20, dtype=np.float64)
  87. x = x.reshape((2,1,10))
  88. x[:,:,0] = 1.0
  89. f, p = periodogram(x)
  90. assert_array_equal(p.shape, (2, 1, 6))
  91. assert_array_almost_equal_nulp(p[0,0,:], p[1,0,:], 60)
  92. f0, p0 = periodogram(x[0,0,:])
  93. assert_array_almost_equal_nulp(p0[np.newaxis,:], p[1,:], 60)
  94. def test_nd_axis_0(self):
  95. x = np.zeros(20, dtype=np.float64)
  96. x = x.reshape((10,2,1))
  97. x[0,:,:] = 1.0
  98. f, p = periodogram(x, axis=0)
  99. assert_array_equal(p.shape, (6,2,1))
  100. assert_array_almost_equal_nulp(p[:,0,0], p[:,1,0], 60)
  101. f0, p0 = periodogram(x[:,0,0])
  102. assert_array_almost_equal_nulp(p0, p[:,1,0])
  103. def test_window_external(self):
  104. x = np.zeros(16)
  105. x[0] = 1
  106. f, p = periodogram(x, 10, 'hann')
  107. win = signal.get_window('hann', 16)
  108. fe, pe = periodogram(x, 10, win)
  109. assert_array_almost_equal_nulp(p, pe)
  110. assert_array_almost_equal_nulp(f, fe)
  111. win_err = signal.get_window('hann', 32)
  112. assert_raises(ValueError, periodogram, x,
  113. 10, win_err) # win longer than signal
  114. def test_padded_fft(self):
  115. x = np.zeros(16)
  116. x[0] = 1
  117. f, p = periodogram(x)
  118. fp, pp = periodogram(x, nfft=32)
  119. assert_allclose(f, fp[::2])
  120. assert_allclose(p, pp[::2])
  121. assert_array_equal(pp.shape, (17,))
  122. def test_empty_input(self):
  123. f, p = periodogram([])
  124. assert_array_equal(f.shape, (0,))
  125. assert_array_equal(p.shape, (0,))
  126. for shape in [(0,), (3,0), (0,5,2)]:
  127. f, p = periodogram(np.empty(shape))
  128. assert_array_equal(f.shape, shape)
  129. assert_array_equal(p.shape, shape)
  130. def test_empty_input_other_axis(self):
  131. for shape in [(3,0), (0,5,2)]:
  132. f, p = periodogram(np.empty(shape), axis=1)
  133. assert_array_equal(f.shape, shape)
  134. assert_array_equal(p.shape, shape)
  135. def test_short_nfft(self):
  136. x = np.zeros(18)
  137. x[0] = 1
  138. f, p = periodogram(x, nfft=16)
  139. assert_allclose(f, np.linspace(0, 0.5, 9))
  140. q = np.ones(9)
  141. q[0] = 0
  142. q[-1] /= 2.0
  143. q /= 8
  144. assert_allclose(p, q)
  145. def test_nfft_is_xshape(self):
  146. x = np.zeros(16)
  147. x[0] = 1
  148. f, p = periodogram(x, nfft=16)
  149. assert_allclose(f, np.linspace(0, 0.5, 9))
  150. q = np.ones(9)
  151. q[0] = 0
  152. q[-1] /= 2.0
  153. q /= 8
  154. assert_allclose(p, q)
  155. def test_real_onesided_even_32(self):
  156. x = np.zeros(16, 'f')
  157. x[0] = 1
  158. f, p = periodogram(x)
  159. assert_allclose(f, np.linspace(0, 0.5, 9))
  160. q = np.ones(9, 'f')
  161. q[0] = 0
  162. q[-1] /= 2.0
  163. q /= 8
  164. assert_allclose(p, q)
  165. assert_(p.dtype == q.dtype)
  166. def test_real_onesided_odd_32(self):
  167. x = np.zeros(15, 'f')
  168. x[0] = 1
  169. f, p = periodogram(x)
  170. assert_allclose(f, np.arange(8.0)/15.0)
  171. q = np.ones(8, 'f')
  172. q[0] = 0
  173. q *= 2.0/15.0
  174. assert_allclose(p, q, atol=1e-7)
  175. assert_(p.dtype == q.dtype)
  176. def test_real_twosided_32(self):
  177. x = np.zeros(16, 'f')
  178. x[0] = 1
  179. f, p = periodogram(x, return_onesided=False)
  180. assert_allclose(f, fftfreq(16, 1.0))
  181. q = np.full(16, 1/16.0, 'f')
  182. q[0] = 0
  183. assert_allclose(p, q)
  184. assert_(p.dtype == q.dtype)
  185. def test_complex_32(self):
  186. x = np.zeros(16, 'F')
  187. x[0] = 1.0 + 2.0j
  188. f, p = periodogram(x, return_onesided=False)
  189. assert_allclose(f, fftfreq(16, 1.0))
  190. q = np.full(16, 5.0/16.0, 'f')
  191. q[0] = 0
  192. assert_allclose(p, q)
  193. assert_(p.dtype == q.dtype)
  194. def test_shorter_window_error(self):
  195. x = np.zeros(16)
  196. x[0] = 1
  197. win = signal.get_window('hann', 10)
  198. expected_msg = ('the size of the window must be the same size '
  199. 'of the input on the specified axis')
  200. with assert_raises(ValueError, match=expected_msg):
  201. periodogram(x, window=win)
  202. class TestWelch:
  203. def test_real_onesided_even(self):
  204. x = np.zeros(16)
  205. x[0] = 1
  206. x[8] = 1
  207. f, p = welch(x, nperseg=8)
  208. assert_allclose(f, np.linspace(0, 0.5, 5))
  209. q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
  210. 0.11111111])
  211. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  212. def test_real_onesided_odd(self):
  213. x = np.zeros(16)
  214. x[0] = 1
  215. x[8] = 1
  216. f, p = welch(x, nperseg=9)
  217. assert_allclose(f, np.arange(5.0)/9.0)
  218. q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
  219. 0.17072113])
  220. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  221. def test_real_twosided(self):
  222. x = np.zeros(16)
  223. x[0] = 1
  224. x[8] = 1
  225. f, p = welch(x, nperseg=8, return_onesided=False)
  226. assert_allclose(f, fftfreq(8, 1.0))
  227. q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
  228. 0.11111111, 0.11111111, 0.11111111, 0.07638889])
  229. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  230. def test_real_spectrum(self):
  231. x = np.zeros(16)
  232. x[0] = 1
  233. x[8] = 1
  234. f, p = welch(x, nperseg=8, scaling='spectrum')
  235. assert_allclose(f, np.linspace(0, 0.5, 5))
  236. q = np.array([0.015625, 0.02864583, 0.04166667, 0.04166667,
  237. 0.02083333])
  238. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  239. def test_integer_onesided_even(self):
  240. x = np.zeros(16, dtype=int)
  241. x[0] = 1
  242. x[8] = 1
  243. f, p = welch(x, nperseg=8)
  244. assert_allclose(f, np.linspace(0, 0.5, 5))
  245. q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
  246. 0.11111111])
  247. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  248. def test_integer_onesided_odd(self):
  249. x = np.zeros(16, dtype=int)
  250. x[0] = 1
  251. x[8] = 1
  252. f, p = welch(x, nperseg=9)
  253. assert_allclose(f, np.arange(5.0)/9.0)
  254. q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
  255. 0.17072113])
  256. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  257. def test_integer_twosided(self):
  258. x = np.zeros(16, dtype=int)
  259. x[0] = 1
  260. x[8] = 1
  261. f, p = welch(x, nperseg=8, return_onesided=False)
  262. assert_allclose(f, fftfreq(8, 1.0))
  263. q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
  264. 0.11111111, 0.11111111, 0.11111111, 0.07638889])
  265. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  266. def test_complex(self):
  267. x = np.zeros(16, np.complex128)
  268. x[0] = 1.0 + 2.0j
  269. x[8] = 1.0 + 2.0j
  270. f, p = welch(x, nperseg=8, return_onesided=False)
  271. assert_allclose(f, fftfreq(8, 1.0))
  272. q = np.array([0.41666667, 0.38194444, 0.55555556, 0.55555556,
  273. 0.55555556, 0.55555556, 0.55555556, 0.38194444])
  274. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  275. def test_unk_scaling(self):
  276. assert_raises(ValueError, welch, np.zeros(4, np.complex128),
  277. scaling='foo', nperseg=4)
  278. def test_detrend_linear(self):
  279. x = np.arange(10, dtype=np.float64) + 0.04
  280. f, p = welch(x, nperseg=10, detrend='linear')
  281. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  282. def test_no_detrending(self):
  283. x = np.arange(10, dtype=np.float64) + 0.04
  284. f1, p1 = welch(x, nperseg=10, detrend=False)
  285. f2, p2 = welch(x, nperseg=10, detrend=lambda x: x)
  286. assert_allclose(f1, f2, atol=1e-15)
  287. assert_allclose(p1, p2, atol=1e-15)
  288. def test_detrend_external(self):
  289. x = np.arange(10, dtype=np.float64) + 0.04
  290. f, p = welch(x, nperseg=10,
  291. detrend=lambda seg: signal.detrend(seg, type='l'))
  292. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  293. def test_detrend_external_nd_m1(self):
  294. x = np.arange(40, dtype=np.float64) + 0.04
  295. x = x.reshape((2,2,10))
  296. f, p = welch(x, nperseg=10,
  297. detrend=lambda seg: signal.detrend(seg, type='l'))
  298. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  299. def test_detrend_external_nd_0(self):
  300. x = np.arange(20, dtype=np.float64) + 0.04
  301. x = x.reshape((2,1,10))
  302. x = np.moveaxis(x, 2, 0)
  303. f, p = welch(x, nperseg=10, axis=0,
  304. detrend=lambda seg: signal.detrend(seg, axis=0, type='l'))
  305. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  306. def test_nd_axis_m1(self):
  307. x = np.arange(20, dtype=np.float64) + 0.04
  308. x = x.reshape((2,1,10))
  309. f, p = welch(x, nperseg=10)
  310. assert_array_equal(p.shape, (2, 1, 6))
  311. assert_allclose(p[0,0,:], p[1,0,:], atol=1e-13, rtol=1e-13)
  312. f0, p0 = welch(x[0,0,:], nperseg=10)
  313. assert_allclose(p0[np.newaxis,:], p[1,:], atol=1e-13, rtol=1e-13)
  314. def test_nd_axis_0(self):
  315. x = np.arange(20, dtype=np.float64) + 0.04
  316. x = x.reshape((10,2,1))
  317. f, p = welch(x, nperseg=10, axis=0)
  318. assert_array_equal(p.shape, (6,2,1))
  319. assert_allclose(p[:,0,0], p[:,1,0], atol=1e-13, rtol=1e-13)
  320. f0, p0 = welch(x[:,0,0], nperseg=10)
  321. assert_allclose(p0, p[:,1,0], atol=1e-13, rtol=1e-13)
  322. def test_window_external(self):
  323. x = np.zeros(16)
  324. x[0] = 1
  325. x[8] = 1
  326. f, p = welch(x, 10, 'hann', nperseg=8)
  327. win = signal.get_window('hann', 8)
  328. fe, pe = welch(x, 10, win, nperseg=None)
  329. assert_array_almost_equal_nulp(p, pe)
  330. assert_array_almost_equal_nulp(f, fe)
  331. assert_array_equal(fe.shape, (5,)) # because win length used as nperseg
  332. assert_array_equal(pe.shape, (5,))
  333. assert_raises(ValueError, welch, x,
  334. 10, win, nperseg=4) # because nperseg != win.shape[-1]
  335. win_err = signal.get_window('hann', 32)
  336. assert_raises(ValueError, welch, x,
  337. 10, win_err, nperseg=None) # win longer than signal
  338. def test_empty_input(self):
  339. f, p = welch([])
  340. assert_array_equal(f.shape, (0,))
  341. assert_array_equal(p.shape, (0,))
  342. for shape in [(0,), (3,0), (0,5,2)]:
  343. f, p = welch(np.empty(shape))
  344. assert_array_equal(f.shape, shape)
  345. assert_array_equal(p.shape, shape)
  346. def test_empty_input_other_axis(self):
  347. for shape in [(3,0), (0,5,2)]:
  348. f, p = welch(np.empty(shape), axis=1)
  349. assert_array_equal(f.shape, shape)
  350. assert_array_equal(p.shape, shape)
  351. def test_short_data(self):
  352. x = np.zeros(8)
  353. x[0] = 1
  354. #for string-like window, input signal length < nperseg value gives
  355. #UserWarning, sets nperseg to x.shape[-1]
  356. with suppress_warnings() as sup:
  357. sup.filter(UserWarning, "nperseg = 256 is greater than input length = 8, using nperseg = 8")
  358. f, p = welch(x,window='hann') # default nperseg
  359. f1, p1 = welch(x,window='hann', nperseg=256) # user-specified nperseg
  360. f2, p2 = welch(x, nperseg=8) # valid nperseg, doesn't give warning
  361. assert_allclose(f, f2)
  362. assert_allclose(p, p2)
  363. assert_allclose(f1, f2)
  364. assert_allclose(p1, p2)
  365. def test_window_long_or_nd(self):
  366. assert_raises(ValueError, welch, np.zeros(4), 1, np.array([1,1,1,1,1]))
  367. assert_raises(ValueError, welch, np.zeros(4), 1,
  368. np.arange(6).reshape((2,3)))
  369. def test_nondefault_noverlap(self):
  370. x = np.zeros(64)
  371. x[::8] = 1
  372. f, p = welch(x, nperseg=16, noverlap=4)
  373. q = np.array([0, 1./12., 1./3., 1./5., 1./3., 1./5., 1./3., 1./5.,
  374. 1./6.])
  375. assert_allclose(p, q, atol=1e-12)
  376. def test_bad_noverlap(self):
  377. assert_raises(ValueError, welch, np.zeros(4), 1, 'hann', 2, 7)
  378. def test_nfft_too_short(self):
  379. assert_raises(ValueError, welch, np.ones(12), nfft=3, nperseg=4)
  380. def test_real_onesided_even_32(self):
  381. x = np.zeros(16, 'f')
  382. x[0] = 1
  383. x[8] = 1
  384. f, p = welch(x, nperseg=8)
  385. assert_allclose(f, np.linspace(0, 0.5, 5))
  386. q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
  387. 0.11111111], 'f')
  388. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  389. assert_(p.dtype == q.dtype)
  390. def test_real_onesided_odd_32(self):
  391. x = np.zeros(16, 'f')
  392. x[0] = 1
  393. x[8] = 1
  394. f, p = welch(x, nperseg=9)
  395. assert_allclose(f, np.arange(5.0)/9.0)
  396. q = np.array([0.12477458, 0.23430935, 0.17072113, 0.17072116,
  397. 0.17072113], 'f')
  398. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  399. assert_(p.dtype == q.dtype)
  400. def test_real_twosided_32(self):
  401. x = np.zeros(16, 'f')
  402. x[0] = 1
  403. x[8] = 1
  404. f, p = welch(x, nperseg=8, return_onesided=False)
  405. assert_allclose(f, fftfreq(8, 1.0))
  406. q = np.array([0.08333333, 0.07638889, 0.11111111,
  407. 0.11111111, 0.11111111, 0.11111111, 0.11111111,
  408. 0.07638889], 'f')
  409. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  410. assert_(p.dtype == q.dtype)
  411. def test_complex_32(self):
  412. x = np.zeros(16, 'F')
  413. x[0] = 1.0 + 2.0j
  414. x[8] = 1.0 + 2.0j
  415. f, p = welch(x, nperseg=8, return_onesided=False)
  416. assert_allclose(f, fftfreq(8, 1.0))
  417. q = np.array([0.41666666, 0.38194442, 0.55555552, 0.55555552,
  418. 0.55555558, 0.55555552, 0.55555552, 0.38194442], 'f')
  419. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  420. assert_(p.dtype == q.dtype,
  421. 'dtype mismatch, %s, %s' % (p.dtype, q.dtype))
  422. def test_padded_freqs(self):
  423. x = np.zeros(12)
  424. nfft = 24
  425. f = fftfreq(nfft, 1.0)[:nfft//2+1]
  426. f[-1] *= -1
  427. fodd, _ = welch(x, nperseg=5, nfft=nfft)
  428. feven, _ = welch(x, nperseg=6, nfft=nfft)
  429. assert_allclose(f, fodd)
  430. assert_allclose(f, feven)
  431. nfft = 25
  432. f = fftfreq(nfft, 1.0)[:(nfft + 1)//2]
  433. fodd, _ = welch(x, nperseg=5, nfft=nfft)
  434. feven, _ = welch(x, nperseg=6, nfft=nfft)
  435. assert_allclose(f, fodd)
  436. assert_allclose(f, feven)
  437. def test_window_correction(self):
  438. A = 20
  439. fs = 1e4
  440. nperseg = int(fs//10)
  441. fsig = 300
  442. ii = int(fsig*nperseg//fs) # Freq index of fsig
  443. tt = np.arange(fs)/fs
  444. x = A*np.sin(2*np.pi*fsig*tt)
  445. for window in ['hann', 'bartlett', ('tukey', 0.1), 'flattop']:
  446. _, p_spec = welch(x, fs=fs, nperseg=nperseg, window=window,
  447. scaling='spectrum')
  448. freq, p_dens = welch(x, fs=fs, nperseg=nperseg, window=window,
  449. scaling='density')
  450. # Check peak height at signal frequency for 'spectrum'
  451. assert_allclose(p_spec[ii], A**2/2.0)
  452. # Check integrated spectrum RMS for 'density'
  453. assert_allclose(np.sqrt(np.trapz(p_dens, freq)), A*np.sqrt(2)/2,
  454. rtol=1e-3)
  455. def test_axis_rolling(self):
  456. np.random.seed(1234)
  457. x_flat = np.random.randn(1024)
  458. _, p_flat = welch(x_flat)
  459. for a in range(3):
  460. newshape = [1,]*3
  461. newshape[a] = -1
  462. x = x_flat.reshape(newshape)
  463. _, p_plus = welch(x, axis=a) # Positive axis index
  464. _, p_minus = welch(x, axis=a-x.ndim) # Negative axis index
  465. assert_equal(p_flat, p_plus.squeeze(), err_msg=a)
  466. assert_equal(p_flat, p_minus.squeeze(), err_msg=a-x.ndim)
  467. def test_average(self):
  468. x = np.zeros(16)
  469. x[0] = 1
  470. x[8] = 1
  471. f, p = welch(x, nperseg=8, average='median')
  472. assert_allclose(f, np.linspace(0, 0.5, 5))
  473. q = np.array([.1, .05, 0., 1.54074396e-33, 0.])
  474. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  475. assert_raises(ValueError, welch, x, nperseg=8,
  476. average='unrecognised-average')
  477. class TestCSD:
  478. def test_pad_shorter_x(self):
  479. x = np.zeros(8)
  480. y = np.zeros(12)
  481. f = np.linspace(0, 0.5, 7)
  482. c = np.zeros(7,dtype=np.complex128)
  483. f1, c1 = csd(x, y, nperseg=12)
  484. assert_allclose(f, f1)
  485. assert_allclose(c, c1)
  486. def test_pad_shorter_y(self):
  487. x = np.zeros(12)
  488. y = np.zeros(8)
  489. f = np.linspace(0, 0.5, 7)
  490. c = np.zeros(7,dtype=np.complex128)
  491. f1, c1 = csd(x, y, nperseg=12)
  492. assert_allclose(f, f1)
  493. assert_allclose(c, c1)
  494. def test_real_onesided_even(self):
  495. x = np.zeros(16)
  496. x[0] = 1
  497. x[8] = 1
  498. f, p = csd(x, x, nperseg=8)
  499. assert_allclose(f, np.linspace(0, 0.5, 5))
  500. q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
  501. 0.11111111])
  502. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  503. def test_real_onesided_odd(self):
  504. x = np.zeros(16)
  505. x[0] = 1
  506. x[8] = 1
  507. f, p = csd(x, x, nperseg=9)
  508. assert_allclose(f, np.arange(5.0)/9.0)
  509. q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
  510. 0.17072113])
  511. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  512. def test_real_twosided(self):
  513. x = np.zeros(16)
  514. x[0] = 1
  515. x[8] = 1
  516. f, p = csd(x, x, nperseg=8, return_onesided=False)
  517. assert_allclose(f, fftfreq(8, 1.0))
  518. q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
  519. 0.11111111, 0.11111111, 0.11111111, 0.07638889])
  520. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  521. def test_real_spectrum(self):
  522. x = np.zeros(16)
  523. x[0] = 1
  524. x[8] = 1
  525. f, p = csd(x, x, nperseg=8, scaling='spectrum')
  526. assert_allclose(f, np.linspace(0, 0.5, 5))
  527. q = np.array([0.015625, 0.02864583, 0.04166667, 0.04166667,
  528. 0.02083333])
  529. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  530. def test_integer_onesided_even(self):
  531. x = np.zeros(16, dtype=int)
  532. x[0] = 1
  533. x[8] = 1
  534. f, p = csd(x, x, nperseg=8)
  535. assert_allclose(f, np.linspace(0, 0.5, 5))
  536. q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
  537. 0.11111111])
  538. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  539. def test_integer_onesided_odd(self):
  540. x = np.zeros(16, dtype=int)
  541. x[0] = 1
  542. x[8] = 1
  543. f, p = csd(x, x, nperseg=9)
  544. assert_allclose(f, np.arange(5.0)/9.0)
  545. q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
  546. 0.17072113])
  547. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  548. def test_integer_twosided(self):
  549. x = np.zeros(16, dtype=int)
  550. x[0] = 1
  551. x[8] = 1
  552. f, p = csd(x, x, nperseg=8, return_onesided=False)
  553. assert_allclose(f, fftfreq(8, 1.0))
  554. q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
  555. 0.11111111, 0.11111111, 0.11111111, 0.07638889])
  556. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  557. def test_complex(self):
  558. x = np.zeros(16, np.complex128)
  559. x[0] = 1.0 + 2.0j
  560. x[8] = 1.0 + 2.0j
  561. f, p = csd(x, x, nperseg=8, return_onesided=False)
  562. assert_allclose(f, fftfreq(8, 1.0))
  563. q = np.array([0.41666667, 0.38194444, 0.55555556, 0.55555556,
  564. 0.55555556, 0.55555556, 0.55555556, 0.38194444])
  565. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  566. def test_unk_scaling(self):
  567. assert_raises(ValueError, csd, np.zeros(4, np.complex128),
  568. np.ones(4, np.complex128), scaling='foo', nperseg=4)
  569. def test_detrend_linear(self):
  570. x = np.arange(10, dtype=np.float64) + 0.04
  571. f, p = csd(x, x, nperseg=10, detrend='linear')
  572. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  573. def test_no_detrending(self):
  574. x = np.arange(10, dtype=np.float64) + 0.04
  575. f1, p1 = csd(x, x, nperseg=10, detrend=False)
  576. f2, p2 = csd(x, x, nperseg=10, detrend=lambda x: x)
  577. assert_allclose(f1, f2, atol=1e-15)
  578. assert_allclose(p1, p2, atol=1e-15)
  579. def test_detrend_external(self):
  580. x = np.arange(10, dtype=np.float64) + 0.04
  581. f, p = csd(x, x, nperseg=10,
  582. detrend=lambda seg: signal.detrend(seg, type='l'))
  583. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  584. def test_detrend_external_nd_m1(self):
  585. x = np.arange(40, dtype=np.float64) + 0.04
  586. x = x.reshape((2,2,10))
  587. f, p = csd(x, x, nperseg=10,
  588. detrend=lambda seg: signal.detrend(seg, type='l'))
  589. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  590. def test_detrend_external_nd_0(self):
  591. x = np.arange(20, dtype=np.float64) + 0.04
  592. x = x.reshape((2,1,10))
  593. x = np.moveaxis(x, 2, 0)
  594. f, p = csd(x, x, nperseg=10, axis=0,
  595. detrend=lambda seg: signal.detrend(seg, axis=0, type='l'))
  596. assert_allclose(p, np.zeros_like(p), atol=1e-15)
  597. def test_nd_axis_m1(self):
  598. x = np.arange(20, dtype=np.float64) + 0.04
  599. x = x.reshape((2,1,10))
  600. f, p = csd(x, x, nperseg=10)
  601. assert_array_equal(p.shape, (2, 1, 6))
  602. assert_allclose(p[0,0,:], p[1,0,:], atol=1e-13, rtol=1e-13)
  603. f0, p0 = csd(x[0,0,:], x[0,0,:], nperseg=10)
  604. assert_allclose(p0[np.newaxis,:], p[1,:], atol=1e-13, rtol=1e-13)
  605. def test_nd_axis_0(self):
  606. x = np.arange(20, dtype=np.float64) + 0.04
  607. x = x.reshape((10,2,1))
  608. f, p = csd(x, x, nperseg=10, axis=0)
  609. assert_array_equal(p.shape, (6,2,1))
  610. assert_allclose(p[:,0,0], p[:,1,0], atol=1e-13, rtol=1e-13)
  611. f0, p0 = csd(x[:,0,0], x[:,0,0], nperseg=10)
  612. assert_allclose(p0, p[:,1,0], atol=1e-13, rtol=1e-13)
  613. def test_window_external(self):
  614. x = np.zeros(16)
  615. x[0] = 1
  616. x[8] = 1
  617. f, p = csd(x, x, 10, 'hann', 8)
  618. win = signal.get_window('hann', 8)
  619. fe, pe = csd(x, x, 10, win, nperseg=None)
  620. assert_array_almost_equal_nulp(p, pe)
  621. assert_array_almost_equal_nulp(f, fe)
  622. assert_array_equal(fe.shape, (5,)) # because win length used as nperseg
  623. assert_array_equal(pe.shape, (5,))
  624. assert_raises(ValueError, csd, x, x,
  625. 10, win, nperseg=256) # because nperseg != win.shape[-1]
  626. win_err = signal.get_window('hann', 32)
  627. assert_raises(ValueError, csd, x, x,
  628. 10, win_err, nperseg=None) # because win longer than signal
  629. def test_empty_input(self):
  630. f, p = csd([],np.zeros(10))
  631. assert_array_equal(f.shape, (0,))
  632. assert_array_equal(p.shape, (0,))
  633. f, p = csd(np.zeros(10),[])
  634. assert_array_equal(f.shape, (0,))
  635. assert_array_equal(p.shape, (0,))
  636. for shape in [(0,), (3,0), (0,5,2)]:
  637. f, p = csd(np.empty(shape), np.empty(shape))
  638. assert_array_equal(f.shape, shape)
  639. assert_array_equal(p.shape, shape)
  640. f, p = csd(np.ones(10), np.empty((5,0)))
  641. assert_array_equal(f.shape, (5,0))
  642. assert_array_equal(p.shape, (5,0))
  643. f, p = csd(np.empty((5,0)), np.ones(10))
  644. assert_array_equal(f.shape, (5,0))
  645. assert_array_equal(p.shape, (5,0))
  646. def test_empty_input_other_axis(self):
  647. for shape in [(3,0), (0,5,2)]:
  648. f, p = csd(np.empty(shape), np.empty(shape), axis=1)
  649. assert_array_equal(f.shape, shape)
  650. assert_array_equal(p.shape, shape)
  651. f, p = csd(np.empty((10,10,3)), np.zeros((10,0,1)), axis=1)
  652. assert_array_equal(f.shape, (10,0,3))
  653. assert_array_equal(p.shape, (10,0,3))
  654. f, p = csd(np.empty((10,0,1)), np.zeros((10,10,3)), axis=1)
  655. assert_array_equal(f.shape, (10,0,3))
  656. assert_array_equal(p.shape, (10,0,3))
  657. def test_short_data(self):
  658. x = np.zeros(8)
  659. x[0] = 1
  660. #for string-like window, input signal length < nperseg value gives
  661. #UserWarning, sets nperseg to x.shape[-1]
  662. with suppress_warnings() as sup:
  663. sup.filter(UserWarning, "nperseg = 256 is greater than input length = 8, using nperseg = 8")
  664. f, p = csd(x, x, window='hann') # default nperseg
  665. f1, p1 = csd(x, x, window='hann', nperseg=256) # user-specified nperseg
  666. f2, p2 = csd(x, x, nperseg=8) # valid nperseg, doesn't give warning
  667. assert_allclose(f, f2)
  668. assert_allclose(p, p2)
  669. assert_allclose(f1, f2)
  670. assert_allclose(p1, p2)
  671. def test_window_long_or_nd(self):
  672. assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1,
  673. np.array([1,1,1,1,1]))
  674. assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1,
  675. np.arange(6).reshape((2,3)))
  676. def test_nondefault_noverlap(self):
  677. x = np.zeros(64)
  678. x[::8] = 1
  679. f, p = csd(x, x, nperseg=16, noverlap=4)
  680. q = np.array([0, 1./12., 1./3., 1./5., 1./3., 1./5., 1./3., 1./5.,
  681. 1./6.])
  682. assert_allclose(p, q, atol=1e-12)
  683. def test_bad_noverlap(self):
  684. assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1, 'hann',
  685. 2, 7)
  686. def test_nfft_too_short(self):
  687. assert_raises(ValueError, csd, np.ones(12), np.zeros(12), nfft=3,
  688. nperseg=4)
  689. def test_real_onesided_even_32(self):
  690. x = np.zeros(16, 'f')
  691. x[0] = 1
  692. x[8] = 1
  693. f, p = csd(x, x, nperseg=8)
  694. assert_allclose(f, np.linspace(0, 0.5, 5))
  695. q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
  696. 0.11111111], 'f')
  697. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  698. assert_(p.dtype == q.dtype)
  699. def test_real_onesided_odd_32(self):
  700. x = np.zeros(16, 'f')
  701. x[0] = 1
  702. x[8] = 1
  703. f, p = csd(x, x, nperseg=9)
  704. assert_allclose(f, np.arange(5.0)/9.0)
  705. q = np.array([0.12477458, 0.23430935, 0.17072113, 0.17072116,
  706. 0.17072113], 'f')
  707. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  708. assert_(p.dtype == q.dtype)
  709. def test_real_twosided_32(self):
  710. x = np.zeros(16, 'f')
  711. x[0] = 1
  712. x[8] = 1
  713. f, p = csd(x, x, nperseg=8, return_onesided=False)
  714. assert_allclose(f, fftfreq(8, 1.0))
  715. q = np.array([0.08333333, 0.07638889, 0.11111111,
  716. 0.11111111, 0.11111111, 0.11111111, 0.11111111,
  717. 0.07638889], 'f')
  718. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  719. assert_(p.dtype == q.dtype)
  720. def test_complex_32(self):
  721. x = np.zeros(16, 'F')
  722. x[0] = 1.0 + 2.0j
  723. x[8] = 1.0 + 2.0j
  724. f, p = csd(x, x, nperseg=8, return_onesided=False)
  725. assert_allclose(f, fftfreq(8, 1.0))
  726. q = np.array([0.41666666, 0.38194442, 0.55555552, 0.55555552,
  727. 0.55555558, 0.55555552, 0.55555552, 0.38194442], 'f')
  728. assert_allclose(p, q, atol=1e-7, rtol=1e-7)
  729. assert_(p.dtype == q.dtype,
  730. 'dtype mismatch, %s, %s' % (p.dtype, q.dtype))
  731. def test_padded_freqs(self):
  732. x = np.zeros(12)
  733. y = np.ones(12)
  734. nfft = 24
  735. f = fftfreq(nfft, 1.0)[:nfft//2+1]
  736. f[-1] *= -1
  737. fodd, _ = csd(x, y, nperseg=5, nfft=nfft)
  738. feven, _ = csd(x, y, nperseg=6, nfft=nfft)
  739. assert_allclose(f, fodd)
  740. assert_allclose(f, feven)
  741. nfft = 25
  742. f = fftfreq(nfft, 1.0)[:(nfft + 1)//2]
  743. fodd, _ = csd(x, y, nperseg=5, nfft=nfft)
  744. feven, _ = csd(x, y, nperseg=6, nfft=nfft)
  745. assert_allclose(f, fodd)
  746. assert_allclose(f, feven)
  747. def test_copied_data(self):
  748. x = np.random.randn(64)
  749. y = x.copy()
  750. _, p_same = csd(x, x, nperseg=8, average='mean',
  751. return_onesided=False)
  752. _, p_copied = csd(x, y, nperseg=8, average='mean',
  753. return_onesided=False)
  754. assert_allclose(p_same, p_copied)
  755. _, p_same = csd(x, x, nperseg=8, average='median',
  756. return_onesided=False)
  757. _, p_copied = csd(x, y, nperseg=8, average='median',
  758. return_onesided=False)
  759. assert_allclose(p_same, p_copied)
  760. class TestCoherence:
  761. def test_identical_input(self):
  762. x = np.random.randn(20)
  763. y = np.copy(x) # So `y is x` -> False
  764. f = np.linspace(0, 0.5, 6)
  765. C = np.ones(6)
  766. f1, C1 = coherence(x, y, nperseg=10)
  767. assert_allclose(f, f1)
  768. assert_allclose(C, C1)
  769. def test_phase_shifted_input(self):
  770. x = np.random.randn(20)
  771. y = -x
  772. f = np.linspace(0, 0.5, 6)
  773. C = np.ones(6)
  774. f1, C1 = coherence(x, y, nperseg=10)
  775. assert_allclose(f, f1)
  776. assert_allclose(C, C1)
  777. class TestSpectrogram:
  778. def test_average_all_segments(self):
  779. x = np.random.randn(1024)
  780. fs = 1.0
  781. window = ('tukey', 0.25)
  782. nperseg = 16
  783. noverlap = 2
  784. f, _, P = spectrogram(x, fs, window, nperseg, noverlap)
  785. fw, Pw = welch(x, fs, window, nperseg, noverlap)
  786. assert_allclose(f, fw)
  787. assert_allclose(np.mean(P, axis=-1), Pw)
  788. def test_window_external(self):
  789. x = np.random.randn(1024)
  790. fs = 1.0
  791. window = ('tukey', 0.25)
  792. nperseg = 16
  793. noverlap = 2
  794. f, _, P = spectrogram(x, fs, window, nperseg, noverlap)
  795. win = signal.get_window(('tukey', 0.25), 16)
  796. fe, _, Pe = spectrogram(x, fs, win, nperseg=None, noverlap=2)
  797. assert_array_equal(fe.shape, (9,)) # because win length used as nperseg
  798. assert_array_equal(Pe.shape, (9,73))
  799. assert_raises(ValueError, spectrogram, x,
  800. fs, win, nperseg=8) # because nperseg != win.shape[-1]
  801. win_err = signal.get_window(('tukey', 0.25), 2048)
  802. assert_raises(ValueError, spectrogram, x,
  803. fs, win_err, nperseg=None) # win longer than signal
  804. def test_short_data(self):
  805. x = np.random.randn(1024)
  806. fs = 1.0
  807. #for string-like window, input signal length < nperseg value gives
  808. #UserWarning, sets nperseg to x.shape[-1]
  809. f, _, p = spectrogram(x, fs, window=('tukey',0.25)) # default nperseg
  810. with suppress_warnings() as sup:
  811. sup.filter(UserWarning,
  812. "nperseg = 1025 is greater than input length = 1024, using nperseg = 1024")
  813. f1, _, p1 = spectrogram(x, fs, window=('tukey',0.25),
  814. nperseg=1025) # user-specified nperseg
  815. f2, _, p2 = spectrogram(x, fs, nperseg=256) # to compare w/default
  816. f3, _, p3 = spectrogram(x, fs, nperseg=1024) # compare w/user-spec'd
  817. assert_allclose(f, f2)
  818. assert_allclose(p, p2)
  819. assert_allclose(f1, f3)
  820. assert_allclose(p1, p3)
  821. class TestLombscargle:
  822. def test_frequency(self):
  823. """Test if frequency location of peak corresponds to frequency of
  824. generated input signal.
  825. """
  826. # Input parameters
  827. ampl = 2.
  828. w = 1.
  829. phi = 0.5 * np.pi
  830. nin = 100
  831. nout = 1000
  832. p = 0.7 # Fraction of points to select
  833. # Randomly select a fraction of an array with timesteps
  834. np.random.seed(2353425)
  835. r = np.random.rand(nin)
  836. t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
  837. # Plot a sine wave for the selected times
  838. x = ampl * np.sin(w*t + phi)
  839. # Define the array of frequencies for which to compute the periodogram
  840. f = np.linspace(0.01, 10., nout)
  841. # Calculate Lomb-Scargle periodogram
  842. P = lombscargle(t, x, f)
  843. # Check if difference between found frequency maximum and input
  844. # frequency is less than accuracy
  845. delta = f[1] - f[0]
  846. assert_(w - f[np.argmax(P)] < (delta/2.))
  847. def test_amplitude(self):
  848. # Test if height of peak in normalized Lomb-Scargle periodogram
  849. # corresponds to amplitude of the generated input signal.
  850. # Input parameters
  851. ampl = 2.
  852. w = 1.
  853. phi = 0.5 * np.pi
  854. nin = 100
  855. nout = 1000
  856. p = 0.7 # Fraction of points to select
  857. # Randomly select a fraction of an array with timesteps
  858. np.random.seed(2353425)
  859. r = np.random.rand(nin)
  860. t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
  861. # Plot a sine wave for the selected times
  862. x = ampl * np.sin(w*t + phi)
  863. # Define the array of frequencies for which to compute the periodogram
  864. f = np.linspace(0.01, 10., nout)
  865. # Calculate Lomb-Scargle periodogram
  866. pgram = lombscargle(t, x, f)
  867. # Normalize
  868. pgram = np.sqrt(4 * pgram / t.shape[0])
  869. # Check if difference between found frequency maximum and input
  870. # frequency is less than accuracy
  871. assert_approx_equal(np.max(pgram), ampl, significant=2)
  872. def test_precenter(self):
  873. # Test if precenter gives the same result as manually precentering.
  874. # Input parameters
  875. ampl = 2.
  876. w = 1.
  877. phi = 0.5 * np.pi
  878. nin = 100
  879. nout = 1000
  880. p = 0.7 # Fraction of points to select
  881. offset = 0.15 # Offset to be subtracted in pre-centering
  882. # Randomly select a fraction of an array with timesteps
  883. np.random.seed(2353425)
  884. r = np.random.rand(nin)
  885. t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
  886. # Plot a sine wave for the selected times
  887. x = ampl * np.sin(w*t + phi) + offset
  888. # Define the array of frequencies for which to compute the periodogram
  889. f = np.linspace(0.01, 10., nout)
  890. # Calculate Lomb-Scargle periodogram
  891. pgram = lombscargle(t, x, f, precenter=True)
  892. pgram2 = lombscargle(t, x - x.mean(), f, precenter=False)
  893. # check if centering worked
  894. assert_allclose(pgram, pgram2)
  895. def test_normalize(self):
  896. # Test normalize option of Lomb-Scarge.
  897. # Input parameters
  898. ampl = 2.
  899. w = 1.
  900. phi = 0.5 * np.pi
  901. nin = 100
  902. nout = 1000
  903. p = 0.7 # Fraction of points to select
  904. # Randomly select a fraction of an array with timesteps
  905. np.random.seed(2353425)
  906. r = np.random.rand(nin)
  907. t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
  908. # Plot a sine wave for the selected times
  909. x = ampl * np.sin(w*t + phi)
  910. # Define the array of frequencies for which to compute the periodogram
  911. f = np.linspace(0.01, 10., nout)
  912. # Calculate Lomb-Scargle periodogram
  913. pgram = lombscargle(t, x, f)
  914. pgram2 = lombscargle(t, x, f, normalize=True)
  915. # check if normalization works as expected
  916. assert_allclose(pgram * 2 / np.dot(x, x), pgram2)
  917. assert_approx_equal(np.max(pgram2), 1.0, significant=2)
  918. def test_wrong_shape(self):
  919. t = np.linspace(0, 1, 1)
  920. x = np.linspace(0, 1, 2)
  921. f = np.linspace(0, 1, 3)
  922. assert_raises(ValueError, lombscargle, t, x, f)
  923. def test_zero_division(self):
  924. t = np.zeros(1)
  925. x = np.zeros(1)
  926. f = np.zeros(1)
  927. assert_raises(ZeroDivisionError, lombscargle, t, x, f)
  928. def test_lombscargle_atan_vs_atan2(self):
  929. # https://github.com/scipy/scipy/issues/3787
  930. # This raised a ZeroDivisionError.
  931. t = np.linspace(0, 10, 1000, endpoint=False)
  932. x = np.sin(4*t)
  933. f = np.linspace(0, 50, 500, endpoint=False) + 0.1
  934. lombscargle(t, x, f*2*np.pi)
  935. class TestSTFT:
  936. def test_input_validation(self):
  937. def chk_VE(match):
  938. """Assert for a ValueError matching regexp `match`.
  939. This little wrapper allows a more concise code layout.
  940. """
  941. return pytest.raises(ValueError, match=match)
  942. # Checks for check_COLA():
  943. with chk_VE('nperseg must be a positive integer'):
  944. check_COLA('hann', -10, 0)
  945. with chk_VE('noverlap must be less than nperseg.'):
  946. check_COLA('hann', 10, 20)
  947. with chk_VE('window must be 1-D'):
  948. check_COLA(np.ones((2, 2)), 10, 0)
  949. with chk_VE('window must have length of nperseg'):
  950. check_COLA(np.ones(20), 10, 0)
  951. # Checks for check_NOLA():
  952. with chk_VE('nperseg must be a positive integer'):
  953. check_NOLA('hann', -10, 0)
  954. with chk_VE('noverlap must be less than nperseg'):
  955. check_NOLA('hann', 10, 20)
  956. with chk_VE('window must be 1-D'):
  957. check_NOLA(np.ones((2, 2)), 10, 0)
  958. with chk_VE('window must have length of nperseg'):
  959. check_NOLA(np.ones(20), 10, 0)
  960. with chk_VE('noverlap must be a nonnegative integer'):
  961. check_NOLA('hann', 64, -32)
  962. x = np.zeros(1024)
  963. z = stft(x)[2]
  964. # Checks for stft():
  965. with chk_VE('window must be 1-D'):
  966. stft(x, window=np.ones((2, 2)))
  967. with chk_VE('value specified for nperseg is different ' +
  968. 'from length of window'):
  969. stft(x, window=np.ones(10), nperseg=256)
  970. with chk_VE('nperseg must be a positive integer'):
  971. stft(x, nperseg=-256)
  972. with chk_VE('noverlap must be less than nperseg.'):
  973. stft(x, nperseg=256, noverlap=1024)
  974. with chk_VE('nfft must be greater than or equal to nperseg.'):
  975. stft(x, nperseg=256, nfft=8)
  976. # Checks for istft():
  977. with chk_VE('Input stft must be at least 2d!'):
  978. istft(x)
  979. with chk_VE('window must be 1-D'):
  980. istft(z, window=np.ones((2, 2)))
  981. with chk_VE('window must have length of 256'):
  982. istft(z, window=np.ones(10), nperseg=256)
  983. with chk_VE('nperseg must be a positive integer'):
  984. istft(z, nperseg=-256)
  985. with chk_VE('noverlap must be less than nperseg.'):
  986. istft(z, nperseg=256, noverlap=1024)
  987. with chk_VE('nfft must be greater than or equal to nperseg.'):
  988. istft(z, nperseg=256, nfft=8)
  989. with pytest.warns(UserWarning, match="NOLA condition failed, " +
  990. "STFT may not be invertible"):
  991. istft(z, nperseg=256, noverlap=0, window='hann')
  992. with chk_VE('Must specify differing time and frequency axes!'):
  993. istft(z, time_axis=0, freq_axis=0)
  994. # Checks for _spectral_helper():
  995. with chk_VE("Unknown value for mode foo, must be one of: " +
  996. r"\{'psd', 'stft'\}"):
  997. _spectral_helper(x, x, mode='foo')
  998. with chk_VE("x and y must be equal if mode is 'stft'"):
  999. _spectral_helper(x[:512], x[512:], mode='stft')
  1000. with chk_VE("Unknown boundary option 'foo', must be one of: " +
  1001. r"\['even', 'odd', 'constant', 'zeros', None\]"):
  1002. _spectral_helper(x, x, boundary='foo')
  1003. scaling = "not_valid"
  1004. with chk_VE(fr"Parameter {scaling=} not in \['spectrum', 'psd'\]!"):
  1005. stft(x, scaling=scaling)
  1006. with chk_VE(fr"Parameter {scaling=} not in \['spectrum', 'psd'\]!"):
  1007. istft(z, scaling=scaling)
  1008. def test_check_COLA(self):
  1009. settings = [
  1010. ('boxcar', 10, 0),
  1011. ('boxcar', 10, 9),
  1012. ('bartlett', 51, 26),
  1013. ('hann', 256, 128),
  1014. ('hann', 256, 192),
  1015. ('blackman', 300, 200),
  1016. (('tukey', 0.5), 256, 64),
  1017. ('hann', 256, 255),
  1018. ]
  1019. for setting in settings:
  1020. msg = '{0}, {1}, {2}'.format(*setting)
  1021. assert_equal(True, check_COLA(*setting), err_msg=msg)
  1022. def test_check_NOLA(self):
  1023. settings_pass = [
  1024. ('boxcar', 10, 0),
  1025. ('boxcar', 10, 9),
  1026. ('boxcar', 10, 7),
  1027. ('bartlett', 51, 26),
  1028. ('bartlett', 51, 10),
  1029. ('hann', 256, 128),
  1030. ('hann', 256, 192),
  1031. ('hann', 256, 37),
  1032. ('blackman', 300, 200),
  1033. ('blackman', 300, 123),
  1034. (('tukey', 0.5), 256, 64),
  1035. (('tukey', 0.5), 256, 38),
  1036. ('hann', 256, 255),
  1037. ('hann', 256, 39),
  1038. ]
  1039. for setting in settings_pass:
  1040. msg = '{0}, {1}, {2}'.format(*setting)
  1041. assert_equal(True, check_NOLA(*setting), err_msg=msg)
  1042. w_fail = np.ones(16)
  1043. w_fail[::2] = 0
  1044. settings_fail = [
  1045. (w_fail, len(w_fail), len(w_fail) // 2),
  1046. ('hann', 64, 0),
  1047. ]
  1048. for setting in settings_fail:
  1049. msg = '{0}, {1}, {2}'.format(*setting)
  1050. assert_equal(False, check_NOLA(*setting), err_msg=msg)
  1051. def test_average_all_segments(self):
  1052. np.random.seed(1234)
  1053. x = np.random.randn(1024)
  1054. fs = 1.0
  1055. window = 'hann'
  1056. nperseg = 16
  1057. noverlap = 8
  1058. # Compare twosided, because onesided welch doubles non-DC terms to
  1059. # account for power at negative frequencies. stft doesn't do this,
  1060. # because it breaks invertibility.
  1061. f, _, Z = stft(x, fs, window, nperseg, noverlap, padded=False,
  1062. return_onesided=False, boundary=None)
  1063. fw, Pw = welch(x, fs, window, nperseg, noverlap, return_onesided=False,
  1064. scaling='spectrum', detrend=False)
  1065. assert_allclose(f, fw)
  1066. assert_allclose(np.mean(np.abs(Z)**2, axis=-1), Pw)
  1067. def test_permute_axes(self):
  1068. np.random.seed(1234)
  1069. x = np.random.randn(1024)
  1070. fs = 1.0
  1071. window = 'hann'
  1072. nperseg = 16
  1073. noverlap = 8
  1074. f1, t1, Z1 = stft(x, fs, window, nperseg, noverlap)
  1075. f2, t2, Z2 = stft(x.reshape((-1, 1, 1)), fs, window, nperseg, noverlap,
  1076. axis=0)
  1077. t3, x1 = istft(Z1, fs, window, nperseg, noverlap)
  1078. t4, x2 = istft(Z2.T, fs, window, nperseg, noverlap, time_axis=0,
  1079. freq_axis=-1)
  1080. assert_allclose(f1, f2)
  1081. assert_allclose(t1, t2)
  1082. assert_allclose(t3, t4)
  1083. assert_allclose(Z1, Z2[:, 0, 0, :])
  1084. assert_allclose(x1, x2[:, 0, 0])
  1085. @pytest.mark.parametrize('scaling', ['spectrum', 'psd'])
  1086. def test_roundtrip_real(self, scaling):
  1087. np.random.seed(1234)
  1088. settings = [
  1089. ('boxcar', 100, 10, 0), # Test no overlap
  1090. ('boxcar', 100, 10, 9), # Test high overlap
  1091. ('bartlett', 101, 51, 26), # Test odd nperseg
  1092. ('hann', 1024, 256, 128), # Test defaults
  1093. (('tukey', 0.5), 1152, 256, 64), # Test Tukey
  1094. ('hann', 1024, 256, 255), # Test overlapped hann
  1095. ]
  1096. for window, N, nperseg, noverlap in settings:
  1097. t = np.arange(N)
  1098. x = 10*np.random.randn(t.size)
  1099. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1100. window=window, detrend=None, padded=False,
  1101. scaling=scaling)
  1102. tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
  1103. window=window, scaling=scaling)
  1104. msg = '{0}, {1}'.format(window, noverlap)
  1105. assert_allclose(t, tr, err_msg=msg)
  1106. assert_allclose(x, xr, err_msg=msg)
  1107. def test_roundtrip_not_nola(self):
  1108. np.random.seed(1234)
  1109. w_fail = np.ones(16)
  1110. w_fail[::2] = 0
  1111. settings = [
  1112. (w_fail, 256, len(w_fail), len(w_fail) // 2),
  1113. ('hann', 256, 64, 0),
  1114. ]
  1115. for window, N, nperseg, noverlap in settings:
  1116. msg = '{0}, {1}, {2}, {3}'.format(window, N, nperseg, noverlap)
  1117. assert not check_NOLA(window, nperseg, noverlap), msg
  1118. t = np.arange(N)
  1119. x = 10 * np.random.randn(t.size)
  1120. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1121. window=window, detrend=None, padded=True,
  1122. boundary='zeros')
  1123. with pytest.warns(UserWarning, match='NOLA'):
  1124. tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
  1125. window=window, boundary=True)
  1126. assert np.allclose(t, tr[:len(t)]), msg
  1127. assert not np.allclose(x, xr[:len(x)]), msg
  1128. def test_roundtrip_nola_not_cola(self):
  1129. np.random.seed(1234)
  1130. settings = [
  1131. ('boxcar', 100, 10, 3), # NOLA True, COLA False
  1132. ('bartlett', 101, 51, 37), # NOLA True, COLA False
  1133. ('hann', 1024, 256, 127), # NOLA True, COLA False
  1134. (('tukey', 0.5), 1152, 256, 14), # NOLA True, COLA False
  1135. ('hann', 1024, 256, 5), # NOLA True, COLA False
  1136. ]
  1137. for window, N, nperseg, noverlap in settings:
  1138. msg = '{0}, {1}, {2}'.format(window, nperseg, noverlap)
  1139. assert check_NOLA(window, nperseg, noverlap), msg
  1140. assert not check_COLA(window, nperseg, noverlap), msg
  1141. t = np.arange(N)
  1142. x = 10 * np.random.randn(t.size)
  1143. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1144. window=window, detrend=None, padded=True,
  1145. boundary='zeros')
  1146. tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
  1147. window=window, boundary=True)
  1148. msg = '{0}, {1}'.format(window, noverlap)
  1149. assert_allclose(t, tr[:len(t)], err_msg=msg)
  1150. assert_allclose(x, xr[:len(x)], err_msg=msg)
  1151. def test_roundtrip_float32(self):
  1152. np.random.seed(1234)
  1153. settings = [('hann', 1024, 256, 128)]
  1154. for window, N, nperseg, noverlap in settings:
  1155. t = np.arange(N)
  1156. x = 10*np.random.randn(t.size)
  1157. x = x.astype(np.float32)
  1158. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1159. window=window, detrend=None, padded=False)
  1160. tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
  1161. window=window)
  1162. msg = '{0}, {1}'.format(window, noverlap)
  1163. assert_allclose(t, t, err_msg=msg)
  1164. assert_allclose(x, xr, err_msg=msg, rtol=1e-4, atol=1e-5)
  1165. assert_(x.dtype == xr.dtype)
  1166. @pytest.mark.parametrize('scaling', ['spectrum', 'psd'])
  1167. def test_roundtrip_complex(self, scaling):
  1168. np.random.seed(1234)
  1169. settings = [
  1170. ('boxcar', 100, 10, 0), # Test no overlap
  1171. ('boxcar', 100, 10, 9), # Test high overlap
  1172. ('bartlett', 101, 51, 26), # Test odd nperseg
  1173. ('hann', 1024, 256, 128), # Test defaults
  1174. (('tukey', 0.5), 1152, 256, 64), # Test Tukey
  1175. ('hann', 1024, 256, 255), # Test overlapped hann
  1176. ]
  1177. for window, N, nperseg, noverlap in settings:
  1178. t = np.arange(N)
  1179. x = 10*np.random.randn(t.size) + 10j*np.random.randn(t.size)
  1180. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1181. window=window, detrend=None, padded=False,
  1182. return_onesided=False, scaling=scaling)
  1183. tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
  1184. window=window, input_onesided=False,
  1185. scaling=scaling)
  1186. msg = '{0}, {1}, {2}'.format(window, nperseg, noverlap)
  1187. assert_allclose(t, tr, err_msg=msg)
  1188. assert_allclose(x, xr, err_msg=msg)
  1189. # Check that asking for onesided switches to twosided
  1190. with suppress_warnings() as sup:
  1191. sup.filter(UserWarning,
  1192. "Input data is complex, switching to return_onesided=False")
  1193. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1194. window=window, detrend=None, padded=False,
  1195. return_onesided=True, scaling=scaling)
  1196. tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
  1197. window=window, input_onesided=False, scaling=scaling)
  1198. msg = '{0}, {1}, {2}'.format(window, nperseg, noverlap)
  1199. assert_allclose(t, tr, err_msg=msg)
  1200. assert_allclose(x, xr, err_msg=msg)
  1201. def test_roundtrip_boundary_extension(self):
  1202. np.random.seed(1234)
  1203. # Test against boxcar, since window is all ones, and thus can be fully
  1204. # recovered with no boundary extension
  1205. settings = [
  1206. ('boxcar', 100, 10, 0), # Test no overlap
  1207. ('boxcar', 100, 10, 9), # Test high overlap
  1208. ]
  1209. for window, N, nperseg, noverlap in settings:
  1210. t = np.arange(N)
  1211. x = 10*np.random.randn(t.size)
  1212. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1213. window=window, detrend=None, padded=True,
  1214. boundary=None)
  1215. _, xr = istft(zz, noverlap=noverlap, window=window, boundary=False)
  1216. for boundary in ['even', 'odd', 'constant', 'zeros']:
  1217. _, _, zz_ext = stft(x, nperseg=nperseg, noverlap=noverlap,
  1218. window=window, detrend=None, padded=True,
  1219. boundary=boundary)
  1220. _, xr_ext = istft(zz_ext, noverlap=noverlap, window=window,
  1221. boundary=True)
  1222. msg = '{0}, {1}, {2}'.format(window, noverlap, boundary)
  1223. assert_allclose(x, xr, err_msg=msg)
  1224. assert_allclose(x, xr_ext, err_msg=msg)
  1225. def test_roundtrip_padded_signal(self):
  1226. np.random.seed(1234)
  1227. settings = [
  1228. ('boxcar', 101, 10, 0),
  1229. ('hann', 1000, 256, 128),
  1230. ]
  1231. for window, N, nperseg, noverlap in settings:
  1232. t = np.arange(N)
  1233. x = 10*np.random.randn(t.size)
  1234. _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
  1235. window=window, detrend=None, padded=True)
  1236. tr, xr = istft(zz, noverlap=noverlap, window=window)
  1237. msg = '{0}, {1}'.format(window, noverlap)
  1238. # Account for possible zero-padding at the end
  1239. assert_allclose(t, tr[:t.size], err_msg=msg)
  1240. assert_allclose(x, xr[:x.size], err_msg=msg)
  1241. def test_roundtrip_padded_FFT(self):
  1242. np.random.seed(1234)
  1243. settings = [
  1244. ('hann', 1024, 256, 128, 512),
  1245. ('hann', 1024, 256, 128, 501),
  1246. ('boxcar', 100, 10, 0, 33),
  1247. (('tukey', 0.5), 1152, 256, 64, 1024),
  1248. ]
  1249. for window, N, nperseg, noverlap, nfft in settings:
  1250. t = np.arange(N)
  1251. x = 10*np.random.randn(t.size)
  1252. xc = x*np.exp(1j*np.pi/4)
  1253. # real signal
  1254. _, _, z = stft(x, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
  1255. window=window, detrend=None, padded=True)
  1256. # complex signal
  1257. _, _, zc = stft(xc, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
  1258. window=window, detrend=None, padded=True,
  1259. return_onesided=False)
  1260. tr, xr = istft(z, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
  1261. window=window)
  1262. tr, xcr = istft(zc, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
  1263. window=window, input_onesided=False)
  1264. msg = '{0}, {1}'.format(window, noverlap)
  1265. assert_allclose(t, tr, err_msg=msg)
  1266. assert_allclose(x, xr, err_msg=msg)
  1267. assert_allclose(xc, xcr, err_msg=msg)
  1268. def test_axis_rolling(self):
  1269. np.random.seed(1234)
  1270. x_flat = np.random.randn(1024)
  1271. _, _, z_flat = stft(x_flat)
  1272. for a in range(3):
  1273. newshape = [1,]*3
  1274. newshape[a] = -1
  1275. x = x_flat.reshape(newshape)
  1276. _, _, z_plus = stft(x, axis=a) # Positive axis index
  1277. _, _, z_minus = stft(x, axis=a-x.ndim) # Negative axis index
  1278. assert_equal(z_flat, z_plus.squeeze(), err_msg=a)
  1279. assert_equal(z_flat, z_minus.squeeze(), err_msg=a-x.ndim)
  1280. # z_flat has shape [n_freq, n_time]
  1281. # Test vs. transpose
  1282. _, x_transpose_m = istft(z_flat.T, time_axis=-2, freq_axis=-1)
  1283. _, x_transpose_p = istft(z_flat.T, time_axis=0, freq_axis=1)
  1284. assert_allclose(x_flat, x_transpose_m, err_msg='istft transpose minus')
  1285. assert_allclose(x_flat, x_transpose_p, err_msg='istft transpose plus')
  1286. def test_roundtrip_scaling(self):
  1287. """Verify behavior of scaling parameter. """
  1288. # Create 1024 sample cosine signal with amplitude 2:
  1289. X = np.zeros(513, dtype=complex)
  1290. X[256] = 1024
  1291. x = np.fft.irfft(X)
  1292. power_x = sum(x**2) / len(x) # power of signal x is 2
  1293. # Calculate magnitude-scaled STFT:
  1294. Zs = stft(x, boundary='even', scaling='spectrum')[2]
  1295. # Test round trip:
  1296. x1 = istft(Zs, boundary=True, scaling='spectrum')[1]
  1297. assert_allclose(x1, x)
  1298. # For a Hann-windowed 256 sample length FFT, we expect a peak at
  1299. # frequency 64 (since it is 1/4 the length of X) with a height of 1
  1300. # (half the amplitude). A Hann window of a perfectly centered sine has
  1301. # the magnitude [..., 0, 0, 0.5, 1, 0.5, 0, 0, ...].
  1302. # Note that in this case the 'even' padding works for the beginning
  1303. # but not for the end of the STFT.
  1304. assert_allclose(abs(Zs[63, :-1]), 0.5)
  1305. assert_allclose(abs(Zs[64, :-1]), 1)
  1306. assert_allclose(abs(Zs[65, :-1]), 0.5)
  1307. # All other values should be zero:
  1308. Zs[63:66, :-1] = 0
  1309. # Note since 'rtol' does not have influence here, atol needs to be set:
  1310. assert_allclose(Zs[:, :-1], 0, atol=np.finfo(Zs.dtype).resolution)
  1311. # Calculate two-sided psd-scaled STFT:
  1312. # - using 'even' padding since signal is axis symmetric - this ensures
  1313. # stationary behavior on the boundaries
  1314. # - using the two-sided transform allows determining the spectral
  1315. # power by `sum(abs(Zp[:, k])**2) / len(f)` for the k-th time slot.
  1316. Zp = stft(x, return_onesided=False, boundary='even', scaling='psd')[2]
  1317. # Calculate spectral power of Zd by summing over the frequency axis:
  1318. psd_Zp = np.sum(Zp.real**2 + Zp.imag**2, axis=0) / Zp.shape[0]
  1319. # Spectral power of Zp should be equal to the signal's power:
  1320. assert_allclose(psd_Zp, power_x)
  1321. # Test round trip:
  1322. x1 = istft(Zp, input_onesided=False, boundary=True, scaling='psd')[1]
  1323. assert_allclose(x1, x)
  1324. # The power of the one-sided psd-scaled STFT can be determined
  1325. # analogously (note that the two sides are not of equal shape):
  1326. Zp0 = stft(x, return_onesided=True, boundary='even', scaling='psd')[2]
  1327. # Since x is real, its Fourier transform is conjugate symmetric, i.e.,
  1328. # the missing 'second side' can be expressed through the 'first side':
  1329. Zp1 = np.conj(Zp0[-2:0:-1, :]) # 'second side' is conjugate reversed
  1330. assert_allclose(Zp[:129, :], Zp0)
  1331. assert_allclose(Zp[129:, :], Zp1)
  1332. # Calculate the spectral power:
  1333. s2 = (np.sum(Zp0.real ** 2 + Zp0.imag ** 2, axis=0) +
  1334. np.sum(Zp1.real ** 2 + Zp1.imag ** 2, axis=0))
  1335. psd_Zp01 = s2 / (Zp0.shape[0] + Zp1.shape[0])
  1336. assert_allclose(psd_Zp01, power_x)
  1337. # Test round trip:
  1338. x1 = istft(Zp0, input_onesided=True, boundary=True, scaling='psd')[1]
  1339. assert_allclose(x1, x)