waveforms.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. # Author: Travis Oliphant
  2. # 2003
  3. #
  4. # Feb. 2010: Updated by Warren Weckesser:
  5. # Rewrote much of chirp()
  6. # Added sweep_poly()
  7. from __future__ import division, print_function, absolute_import
  8. import numpy as np
  9. from numpy import asarray, zeros, place, nan, mod, pi, extract, log, sqrt, \
  10. exp, cos, sin, polyval, polyint
  11. from scipy._lib.six import string_types
  12. __all__ = ['sawtooth', 'square', 'gausspulse', 'chirp', 'sweep_poly',
  13. 'unit_impulse']
  14. def sawtooth(t, width=1):
  15. """
  16. Return a periodic sawtooth or triangle waveform.
  17. The sawtooth waveform has a period ``2*pi``, rises from -1 to 1 on the
  18. interval 0 to ``width*2*pi``, then drops from 1 to -1 on the interval
  19. ``width*2*pi`` to ``2*pi``. `width` must be in the interval [0, 1].
  20. Note that this is not band-limited. It produces an infinite number
  21. of harmonics, which are aliased back and forth across the frequency
  22. spectrum.
  23. Parameters
  24. ----------
  25. t : array_like
  26. Time.
  27. width : array_like, optional
  28. Width of the rising ramp as a proportion of the total cycle.
  29. Default is 1, producing a rising ramp, while 0 produces a falling
  30. ramp. `width` = 0.5 produces a triangle wave.
  31. If an array, causes wave shape to change over time, and must be the
  32. same length as t.
  33. Returns
  34. -------
  35. y : ndarray
  36. Output array containing the sawtooth waveform.
  37. Examples
  38. --------
  39. A 5 Hz waveform sampled at 500 Hz for 1 second:
  40. >>> from scipy import signal
  41. >>> import matplotlib.pyplot as plt
  42. >>> t = np.linspace(0, 1, 500)
  43. >>> plt.plot(t, signal.sawtooth(2 * np.pi * 5 * t))
  44. """
  45. t, w = asarray(t), asarray(width)
  46. w = asarray(w + (t - t))
  47. t = asarray(t + (w - w))
  48. if t.dtype.char in ['fFdD']:
  49. ytype = t.dtype.char
  50. else:
  51. ytype = 'd'
  52. y = zeros(t.shape, ytype)
  53. # width must be between 0 and 1 inclusive
  54. mask1 = (w > 1) | (w < 0)
  55. place(y, mask1, nan)
  56. # take t modulo 2*pi
  57. tmod = mod(t, 2 * pi)
  58. # on the interval 0 to width*2*pi function is
  59. # tmod / (pi*w) - 1
  60. mask2 = (1 - mask1) & (tmod < w * 2 * pi)
  61. tsub = extract(mask2, tmod)
  62. wsub = extract(mask2, w)
  63. place(y, mask2, tsub / (pi * wsub) - 1)
  64. # on the interval width*2*pi to 2*pi function is
  65. # (pi*(w+1)-tmod) / (pi*(1-w))
  66. mask3 = (1 - mask1) & (1 - mask2)
  67. tsub = extract(mask3, tmod)
  68. wsub = extract(mask3, w)
  69. place(y, mask3, (pi * (wsub + 1) - tsub) / (pi * (1 - wsub)))
  70. return y
  71. def square(t, duty=0.5):
  72. """
  73. Return a periodic square-wave waveform.
  74. The square wave has a period ``2*pi``, has value +1 from 0 to
  75. ``2*pi*duty`` and -1 from ``2*pi*duty`` to ``2*pi``. `duty` must be in
  76. the interval [0,1].
  77. Note that this is not band-limited. It produces an infinite number
  78. of harmonics, which are aliased back and forth across the frequency
  79. spectrum.
  80. Parameters
  81. ----------
  82. t : array_like
  83. The input time array.
  84. duty : array_like, optional
  85. Duty cycle. Default is 0.5 (50% duty cycle).
  86. If an array, causes wave shape to change over time, and must be the
  87. same length as t.
  88. Returns
  89. -------
  90. y : ndarray
  91. Output array containing the square waveform.
  92. Examples
  93. --------
  94. A 5 Hz waveform sampled at 500 Hz for 1 second:
  95. >>> from scipy import signal
  96. >>> import matplotlib.pyplot as plt
  97. >>> t = np.linspace(0, 1, 500, endpoint=False)
  98. >>> plt.plot(t, signal.square(2 * np.pi * 5 * t))
  99. >>> plt.ylim(-2, 2)
  100. A pulse-width modulated sine wave:
  101. >>> plt.figure()
  102. >>> sig = np.sin(2 * np.pi * t)
  103. >>> pwm = signal.square(2 * np.pi * 30 * t, duty=(sig + 1)/2)
  104. >>> plt.subplot(2, 1, 1)
  105. >>> plt.plot(t, sig)
  106. >>> plt.subplot(2, 1, 2)
  107. >>> plt.plot(t, pwm)
  108. >>> plt.ylim(-1.5, 1.5)
  109. """
  110. t, w = asarray(t), asarray(duty)
  111. w = asarray(w + (t - t))
  112. t = asarray(t + (w - w))
  113. if t.dtype.char in ['fFdD']:
  114. ytype = t.dtype.char
  115. else:
  116. ytype = 'd'
  117. y = zeros(t.shape, ytype)
  118. # width must be between 0 and 1 inclusive
  119. mask1 = (w > 1) | (w < 0)
  120. place(y, mask1, nan)
  121. # on the interval 0 to duty*2*pi function is 1
  122. tmod = mod(t, 2 * pi)
  123. mask2 = (1 - mask1) & (tmod < w * 2 * pi)
  124. place(y, mask2, 1)
  125. # on the interval duty*2*pi to 2*pi function is
  126. # (pi*(w+1)-tmod) / (pi*(1-w))
  127. mask3 = (1 - mask1) & (1 - mask2)
  128. place(y, mask3, -1)
  129. return y
  130. def gausspulse(t, fc=1000, bw=0.5, bwr=-6, tpr=-60, retquad=False,
  131. retenv=False):
  132. """
  133. Return a Gaussian modulated sinusoid:
  134. ``exp(-a t^2) exp(1j*2*pi*fc*t).``
  135. If `retquad` is True, then return the real and imaginary parts
  136. (in-phase and quadrature).
  137. If `retenv` is True, then return the envelope (unmodulated signal).
  138. Otherwise, return the real part of the modulated sinusoid.
  139. Parameters
  140. ----------
  141. t : ndarray or the string 'cutoff'
  142. Input array.
  143. fc : int, optional
  144. Center frequency (e.g. Hz). Default is 1000.
  145. bw : float, optional
  146. Fractional bandwidth in frequency domain of pulse (e.g. Hz).
  147. Default is 0.5.
  148. bwr : float, optional
  149. Reference level at which fractional bandwidth is calculated (dB).
  150. Default is -6.
  151. tpr : float, optional
  152. If `t` is 'cutoff', then the function returns the cutoff
  153. time for when the pulse amplitude falls below `tpr` (in dB).
  154. Default is -60.
  155. retquad : bool, optional
  156. If True, return the quadrature (imaginary) as well as the real part
  157. of the signal. Default is False.
  158. retenv : bool, optional
  159. If True, return the envelope of the signal. Default is False.
  160. Returns
  161. -------
  162. yI : ndarray
  163. Real part of signal. Always returned.
  164. yQ : ndarray
  165. Imaginary part of signal. Only returned if `retquad` is True.
  166. yenv : ndarray
  167. Envelope of signal. Only returned if `retenv` is True.
  168. See Also
  169. --------
  170. scipy.signal.morlet
  171. Examples
  172. --------
  173. Plot real component, imaginary component, and envelope for a 5 Hz pulse,
  174. sampled at 100 Hz for 2 seconds:
  175. >>> from scipy import signal
  176. >>> import matplotlib.pyplot as plt
  177. >>> t = np.linspace(-1, 1, 2 * 100, endpoint=False)
  178. >>> i, q, e = signal.gausspulse(t, fc=5, retquad=True, retenv=True)
  179. >>> plt.plot(t, i, t, q, t, e, '--')
  180. """
  181. if fc < 0:
  182. raise ValueError("Center frequency (fc=%.2f) must be >=0." % fc)
  183. if bw <= 0:
  184. raise ValueError("Fractional bandwidth (bw=%.2f) must be > 0." % bw)
  185. if bwr >= 0:
  186. raise ValueError("Reference level for bandwidth (bwr=%.2f) must "
  187. "be < 0 dB" % bwr)
  188. # exp(-a t^2) <-> sqrt(pi/a) exp(-pi^2/a * f^2) = g(f)
  189. ref = pow(10.0, bwr / 20.0)
  190. # fdel = fc*bw/2: g(fdel) = ref --- solve this for a
  191. #
  192. # pi^2/a * fc^2 * bw^2 /4=-log(ref)
  193. a = -(pi * fc * bw) ** 2 / (4.0 * log(ref))
  194. if isinstance(t, string_types):
  195. if t == 'cutoff': # compute cut_off point
  196. # Solve exp(-a tc**2) = tref for tc
  197. # tc = sqrt(-log(tref) / a) where tref = 10^(tpr/20)
  198. if tpr >= 0:
  199. raise ValueError("Reference level for time cutoff must "
  200. "be < 0 dB")
  201. tref = pow(10.0, tpr / 20.0)
  202. return sqrt(-log(tref) / a)
  203. else:
  204. raise ValueError("If `t` is a string, it must be 'cutoff'")
  205. yenv = exp(-a * t * t)
  206. yI = yenv * cos(2 * pi * fc * t)
  207. yQ = yenv * sin(2 * pi * fc * t)
  208. if not retquad and not retenv:
  209. return yI
  210. if not retquad and retenv:
  211. return yI, yenv
  212. if retquad and not retenv:
  213. return yI, yQ
  214. if retquad and retenv:
  215. return yI, yQ, yenv
  216. def chirp(t, f0, t1, f1, method='linear', phi=0, vertex_zero=True):
  217. """Frequency-swept cosine generator.
  218. In the following, 'Hz' should be interpreted as 'cycles per unit';
  219. there is no requirement here that the unit is one second. The
  220. important distinction is that the units of rotation are cycles, not
  221. radians. Likewise, `t` could be a measurement of space instead of time.
  222. Parameters
  223. ----------
  224. t : array_like
  225. Times at which to evaluate the waveform.
  226. f0 : float
  227. Frequency (e.g. Hz) at time t=0.
  228. t1 : float
  229. Time at which `f1` is specified.
  230. f1 : float
  231. Frequency (e.g. Hz) of the waveform at time `t1`.
  232. method : {'linear', 'quadratic', 'logarithmic', 'hyperbolic'}, optional
  233. Kind of frequency sweep. If not given, `linear` is assumed. See
  234. Notes below for more details.
  235. phi : float, optional
  236. Phase offset, in degrees. Default is 0.
  237. vertex_zero : bool, optional
  238. This parameter is only used when `method` is 'quadratic'.
  239. It determines whether the vertex of the parabola that is the graph
  240. of the frequency is at t=0 or t=t1.
  241. Returns
  242. -------
  243. y : ndarray
  244. A numpy array containing the signal evaluated at `t` with the
  245. requested time-varying frequency. More precisely, the function
  246. returns ``cos(phase + (pi/180)*phi)`` where `phase` is the integral
  247. (from 0 to `t`) of ``2*pi*f(t)``. ``f(t)`` is defined below.
  248. See Also
  249. --------
  250. sweep_poly
  251. Notes
  252. -----
  253. There are four options for the `method`. The following formulas give
  254. the instantaneous frequency (in Hz) of the signal generated by
  255. `chirp()`. For convenience, the shorter names shown below may also be
  256. used.
  257. linear, lin, li:
  258. ``f(t) = f0 + (f1 - f0) * t / t1``
  259. quadratic, quad, q:
  260. The graph of the frequency f(t) is a parabola through (0, f0) and
  261. (t1, f1). By default, the vertex of the parabola is at (0, f0).
  262. If `vertex_zero` is False, then the vertex is at (t1, f1). The
  263. formula is:
  264. if vertex_zero is True:
  265. ``f(t) = f0 + (f1 - f0) * t**2 / t1**2``
  266. else:
  267. ``f(t) = f1 - (f1 - f0) * (t1 - t)**2 / t1**2``
  268. To use a more general quadratic function, or an arbitrary
  269. polynomial, use the function `scipy.signal.waveforms.sweep_poly`.
  270. logarithmic, log, lo:
  271. ``f(t) = f0 * (f1/f0)**(t/t1)``
  272. f0 and f1 must be nonzero and have the same sign.
  273. This signal is also known as a geometric or exponential chirp.
  274. hyperbolic, hyp:
  275. ``f(t) = f0*f1*t1 / ((f0 - f1)*t + f1*t1)``
  276. f0 and f1 must be nonzero.
  277. Examples
  278. --------
  279. The following will be used in the examples:
  280. >>> from scipy.signal import chirp, spectrogram
  281. >>> import matplotlib.pyplot as plt
  282. For the first example, we'll plot the waveform for a linear chirp
  283. from 6 Hz to 1 Hz over 10 seconds:
  284. >>> t = np.linspace(0, 10, 5001)
  285. >>> w = chirp(t, f0=6, f1=1, t1=10, method='linear')
  286. >>> plt.plot(t, w)
  287. >>> plt.title("Linear Chirp, f(0)=6, f(10)=1")
  288. >>> plt.xlabel('t (sec)')
  289. >>> plt.show()
  290. For the remaining examples, we'll use higher frequency ranges,
  291. and demonstrate the result using `scipy.signal.spectrogram`.
  292. We'll use a 10 second interval sampled at 8000 Hz.
  293. >>> fs = 8000
  294. >>> T = 10
  295. >>> t = np.linspace(0, T, T*fs, endpoint=False)
  296. Quadratic chirp from 1500 Hz to 250 Hz over 10 seconds
  297. (vertex of the parabolic curve of the frequency is at t=0):
  298. >>> w = chirp(t, f0=1500, f1=250, t1=10, method='quadratic')
  299. >>> ff, tt, Sxx = spectrogram(w, fs=fs, noverlap=256, nperseg=512,
  300. ... nfft=2048)
  301. >>> plt.pcolormesh(tt, ff[:513], Sxx[:513], cmap='gray_r')
  302. >>> plt.title('Quadratic Chirp, f(0)=1500, f(10)=250')
  303. >>> plt.xlabel('t (sec)')
  304. >>> plt.ylabel('Frequency (Hz)')
  305. >>> plt.grid()
  306. >>> plt.show()
  307. Quadratic chirp from 1500 Hz to 250 Hz over 10 seconds
  308. (vertex of the parabolic curve of the frequency is at t=10):
  309. >>> w = chirp(t, f0=1500, f1=250, t1=10, method='quadratic',
  310. ... vertex_zero=False)
  311. >>> ff, tt, Sxx = spectrogram(w, fs=fs, noverlap=256, nperseg=512,
  312. ... nfft=2048)
  313. >>> plt.pcolormesh(tt, ff[:513], Sxx[:513], cmap='gray_r')
  314. >>> plt.title('Quadratic Chirp, f(0)=1500, f(10)=250\\n' +
  315. ... '(vertex_zero=False)')
  316. >>> plt.xlabel('t (sec)')
  317. >>> plt.ylabel('Frequency (Hz)')
  318. >>> plt.grid()
  319. >>> plt.show()
  320. Logarithmic chirp from 1500 Hz to 250 Hz over 10 seconds:
  321. >>> w = chirp(t, f0=1500, f1=250, t1=10, method='logarithmic')
  322. >>> ff, tt, Sxx = spectrogram(w, fs=fs, noverlap=256, nperseg=512,
  323. ... nfft=2048)
  324. >>> plt.pcolormesh(tt, ff[:513], Sxx[:513], cmap='gray_r')
  325. >>> plt.title('Logarithmic Chirp, f(0)=1500, f(10)=250')
  326. >>> plt.xlabel('t (sec)')
  327. >>> plt.ylabel('Frequency (Hz)')
  328. >>> plt.grid()
  329. >>> plt.show()
  330. Hyperbolic chirp from 1500 Hz to 250 Hz over 10 seconds:
  331. >>> w = chirp(t, f0=1500, f1=250, t1=10, method='hyperbolic')
  332. >>> ff, tt, Sxx = spectrogram(w, fs=fs, noverlap=256, nperseg=512,
  333. ... nfft=2048)
  334. >>> plt.pcolormesh(tt, ff[:513], Sxx[:513], cmap='gray_r')
  335. >>> plt.title('Hyperbolic Chirp, f(0)=1500, f(10)=250')
  336. >>> plt.xlabel('t (sec)')
  337. >>> plt.ylabel('Frequency (Hz)')
  338. >>> plt.grid()
  339. >>> plt.show()
  340. """
  341. # 'phase' is computed in _chirp_phase, to make testing easier.
  342. phase = _chirp_phase(t, f0, t1, f1, method, vertex_zero)
  343. # Convert phi to radians.
  344. phi *= pi / 180
  345. return cos(phase + phi)
  346. def _chirp_phase(t, f0, t1, f1, method='linear', vertex_zero=True):
  347. """
  348. Calculate the phase used by `chirp` to generate its output.
  349. See `chirp` for a description of the arguments.
  350. """
  351. t = asarray(t)
  352. f0 = float(f0)
  353. t1 = float(t1)
  354. f1 = float(f1)
  355. if method in ['linear', 'lin', 'li']:
  356. beta = (f1 - f0) / t1
  357. phase = 2 * pi * (f0 * t + 0.5 * beta * t * t)
  358. elif method in ['quadratic', 'quad', 'q']:
  359. beta = (f1 - f0) / (t1 ** 2)
  360. if vertex_zero:
  361. phase = 2 * pi * (f0 * t + beta * t ** 3 / 3)
  362. else:
  363. phase = 2 * pi * (f1 * t + beta * ((t1 - t) ** 3 - t1 ** 3) / 3)
  364. elif method in ['logarithmic', 'log', 'lo']:
  365. if f0 * f1 <= 0.0:
  366. raise ValueError("For a logarithmic chirp, f0 and f1 must be "
  367. "nonzero and have the same sign.")
  368. if f0 == f1:
  369. phase = 2 * pi * f0 * t
  370. else:
  371. beta = t1 / log(f1 / f0)
  372. phase = 2 * pi * beta * f0 * (pow(f1 / f0, t / t1) - 1.0)
  373. elif method in ['hyperbolic', 'hyp']:
  374. if f0 == 0 or f1 == 0:
  375. raise ValueError("For a hyperbolic chirp, f0 and f1 must be "
  376. "nonzero.")
  377. if f0 == f1:
  378. # Degenerate case: constant frequency.
  379. phase = 2 * pi * f0 * t
  380. else:
  381. # Singular point: the instantaneous frequency blows up
  382. # when t == sing.
  383. sing = -f1 * t1 / (f0 - f1)
  384. phase = 2 * pi * (-sing * f0) * log(np.abs(1 - t/sing))
  385. else:
  386. raise ValueError("method must be 'linear', 'quadratic', 'logarithmic',"
  387. " or 'hyperbolic', but a value of %r was given."
  388. % method)
  389. return phase
  390. def sweep_poly(t, poly, phi=0):
  391. """
  392. Frequency-swept cosine generator, with a time-dependent frequency.
  393. This function generates a sinusoidal function whose instantaneous
  394. frequency varies with time. The frequency at time `t` is given by
  395. the polynomial `poly`.
  396. Parameters
  397. ----------
  398. t : ndarray
  399. Times at which to evaluate the waveform.
  400. poly : 1-D array_like or instance of numpy.poly1d
  401. The desired frequency expressed as a polynomial. If `poly` is
  402. a list or ndarray of length n, then the elements of `poly` are
  403. the coefficients of the polynomial, and the instantaneous
  404. frequency is
  405. ``f(t) = poly[0]*t**(n-1) + poly[1]*t**(n-2) + ... + poly[n-1]``
  406. If `poly` is an instance of numpy.poly1d, then the
  407. instantaneous frequency is
  408. ``f(t) = poly(t)``
  409. phi : float, optional
  410. Phase offset, in degrees, Default: 0.
  411. Returns
  412. -------
  413. sweep_poly : ndarray
  414. A numpy array containing the signal evaluated at `t` with the
  415. requested time-varying frequency. More precisely, the function
  416. returns ``cos(phase + (pi/180)*phi)``, where `phase` is the integral
  417. (from 0 to t) of ``2 * pi * f(t)``; ``f(t)`` is defined above.
  418. See Also
  419. --------
  420. chirp
  421. Notes
  422. -----
  423. .. versionadded:: 0.8.0
  424. If `poly` is a list or ndarray of length `n`, then the elements of
  425. `poly` are the coefficients of the polynomial, and the instantaneous
  426. frequency is:
  427. ``f(t) = poly[0]*t**(n-1) + poly[1]*t**(n-2) + ... + poly[n-1]``
  428. If `poly` is an instance of `numpy.poly1d`, then the instantaneous
  429. frequency is:
  430. ``f(t) = poly(t)``
  431. Finally, the output `s` is:
  432. ``cos(phase + (pi/180)*phi)``
  433. where `phase` is the integral from 0 to `t` of ``2 * pi * f(t)``,
  434. ``f(t)`` as defined above.
  435. Examples
  436. --------
  437. Compute the waveform with instantaneous frequency::
  438. f(t) = 0.025*t**3 - 0.36*t**2 + 1.25*t + 2
  439. over the interval 0 <= t <= 10.
  440. >>> from scipy.signal import sweep_poly
  441. >>> p = np.poly1d([0.025, -0.36, 1.25, 2.0])
  442. >>> t = np.linspace(0, 10, 5001)
  443. >>> w = sweep_poly(t, p)
  444. Plot it:
  445. >>> import matplotlib.pyplot as plt
  446. >>> plt.subplot(2, 1, 1)
  447. >>> plt.plot(t, w)
  448. >>> plt.title("Sweep Poly\\nwith frequency " +
  449. ... "$f(t) = 0.025t^3 - 0.36t^2 + 1.25t + 2$")
  450. >>> plt.subplot(2, 1, 2)
  451. >>> plt.plot(t, p(t), 'r', label='f(t)')
  452. >>> plt.legend()
  453. >>> plt.xlabel('t')
  454. >>> plt.tight_layout()
  455. >>> plt.show()
  456. """
  457. # 'phase' is computed in _sweep_poly_phase, to make testing easier.
  458. phase = _sweep_poly_phase(t, poly)
  459. # Convert to radians.
  460. phi *= pi / 180
  461. return cos(phase + phi)
  462. def _sweep_poly_phase(t, poly):
  463. """
  464. Calculate the phase used by sweep_poly to generate its output.
  465. See `sweep_poly` for a description of the arguments.
  466. """
  467. # polyint handles lists, ndarrays and instances of poly1d automatically.
  468. intpoly = polyint(poly)
  469. phase = 2 * pi * polyval(intpoly, t)
  470. return phase
  471. def unit_impulse(shape, idx=None, dtype=float):
  472. """
  473. Unit impulse signal (discrete delta function) or unit basis vector.
  474. Parameters
  475. ----------
  476. shape : int or tuple of int
  477. Number of samples in the output (1-D), or a tuple that represents the
  478. shape of the output (N-D).
  479. idx : None or int or tuple of int or 'mid', optional
  480. Index at which the value is 1. If None, defaults to the 0th element.
  481. If ``idx='mid'``, the impulse will be centered at ``shape // 2`` in
  482. all dimensions. If an int, the impulse will be at `idx` in all
  483. dimensions.
  484. dtype : data-type, optional
  485. The desired data-type for the array, e.g., `numpy.int8`. Default is
  486. `numpy.float64`.
  487. Returns
  488. -------
  489. y : ndarray
  490. Output array containing an impulse signal.
  491. Notes
  492. -----
  493. The 1D case is also known as the Kronecker delta.
  494. .. versionadded:: 0.19.0
  495. Examples
  496. --------
  497. An impulse at the 0th element (:math:`\\delta[n]`):
  498. >>> from scipy import signal
  499. >>> signal.unit_impulse(8)
  500. array([ 1., 0., 0., 0., 0., 0., 0., 0.])
  501. Impulse offset by 2 samples (:math:`\\delta[n-2]`):
  502. >>> signal.unit_impulse(7, 2)
  503. array([ 0., 0., 1., 0., 0., 0., 0.])
  504. 2-dimensional impulse, centered:
  505. >>> signal.unit_impulse((3, 3), 'mid')
  506. array([[ 0., 0., 0.],
  507. [ 0., 1., 0.],
  508. [ 0., 0., 0.]])
  509. Impulse at (2, 2), using broadcasting:
  510. >>> signal.unit_impulse((4, 4), 2)
  511. array([[ 0., 0., 0., 0.],
  512. [ 0., 0., 0., 0.],
  513. [ 0., 0., 1., 0.],
  514. [ 0., 0., 0., 0.]])
  515. Plot the impulse response of a 4th-order Butterworth lowpass filter:
  516. >>> imp = signal.unit_impulse(100, 'mid')
  517. >>> b, a = signal.butter(4, 0.2)
  518. >>> response = signal.lfilter(b, a, imp)
  519. >>> import matplotlib.pyplot as plt
  520. >>> plt.plot(np.arange(-50, 50), imp)
  521. >>> plt.plot(np.arange(-50, 50), response)
  522. >>> plt.margins(0.1, 0.1)
  523. >>> plt.xlabel('Time [samples]')
  524. >>> plt.ylabel('Amplitude')
  525. >>> plt.grid(True)
  526. >>> plt.show()
  527. """
  528. out = zeros(shape, dtype)
  529. shape = np.atleast_1d(shape)
  530. if idx is None:
  531. idx = (0,) * len(shape)
  532. elif idx == 'mid':
  533. idx = tuple(shape // 2)
  534. elif not hasattr(idx, "__iter__"):
  535. idx = (idx,) * len(shape)
  536. out[idx] = 1
  537. return out