L iYddlZddlmZmZmZmZmZmZmZm Z m Z m Z m Z m Z mZmZgdZddZddZ ddZdddd Zdd Zdd Zd Zdefd Zy)N)asarrayzerosplacenanmodpiextractlogsqrtexpcossinpolyvalpolyint)sawtoothsquare gausspulsechirp sweep_poly unit_impulsec$t|t|}}t|||z z}t|||z z}t|jd}|dkD|dkz}t||tt |dt z}d|z ||dzt zkz}t||}t||}t|||t |zz dz d|z d|z z} t| |}t| |}t|| t |dzz|z t d|z zz |S)a Return a periodic sawtooth or triangle waveform. The sawtooth waveform has a period ``2*pi``, rises from -1 to 1 on the interval 0 to ``width*2*pi``, then drops from 1 to -1 on the interval ``width*2*pi`` to ``2*pi``. `width` must be in the interval [0, 1]. Note that this is not band-limited. It produces an infinite number of harmonics, which are aliased back and forth across the frequency spectrum. Parameters ---------- t : array_like Time. width : array_like, optional Width of the rising ramp as a proportion of the total cycle. Default is 1, producing a rising ramp, while 0 produces a falling ramp. `width` = 0.5 produces a triangle wave. If an array, causes wave shape to change over time, and must be the same length as t. Returns ------- y : ndarray Output array containing the sawtooth waveform. Examples -------- A 5 Hz waveform sampled at 500 Hz for 1 second: >>> import numpy as np >>> from scipy import signal >>> import matplotlib.pyplot as plt >>> t = np.linspace(0, 1, 500) >>> plt.plot(t, signal.sawtooth(2 * np.pi * 5 * t)) ddtyper)rrshaperrrrr ) twidthwymask1tmodmask2tsubwsubmask3s ]/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/scipy/signal/_waveforms.pyrrs!N 1:wu~qAQU AQU A aggS!AUq1u E !UC q!b&>DY4!a%"*, -E 5$ D 5! D !UDBI&*+ Y1u9 %E 5$ D 5! D !UR4!8_t+a$h@A Hcvt|t|}}t|||z z}t|||z z}t|jd}|dkD|dkz}t||tt |dt z}d|z ||dzt zkz}t||dd|z d|z z}t||d|S)aB Return a periodic square-wave waveform. The square wave has a period ``2*pi``, has value +1 from 0 to ``2*pi*duty`` and -1 from ``2*pi*duty`` to ``2*pi``. `duty` must be in the interval [0,1]. Note that this is not band-limited. It produces an infinite number of harmonics, which are aliased back and forth across the frequency spectrum. Parameters ---------- t : array_like The input time array. duty : array_like, optional Duty cycle. Default is 0.5 (50% duty cycle). If an array, causes wave shape to change over time, and must be the same length as t. Returns ------- y : ndarray Output array containing the square waveform. Examples -------- A 5 Hz waveform sampled at 500 Hz for 1 second: >>> import numpy as np >>> from scipy import signal >>> import matplotlib.pyplot as plt >>> t = np.linspace(0, 1, 500, endpoint=False) >>> plt.plot(t, signal.square(2 * np.pi * 5 * t)) >>> plt.ylim(-2, 2) A pulse-width modulated sine wave: >>> plt.figure() >>> sig = np.sin(2 * np.pi * t) >>> pwm = signal.square(2 * np.pi * 30 * t, duty=(sig + 1)/2) >>> plt.subplot(2, 1, 1) >>> plt.plot(t, sig) >>> plt.subplot(2, 1, 2) >>> plt.plot(t, pwm) >>> plt.ylim(-1.5, 1.5) rrrrr)rrrrrrr)rdutyr r!r"r#r$r's r(rrTsb 1:wt}qAQU AQU A aggS!AUq1u E !UC q!b&>D Y4!a%"*, -E !UAY1u9 %E !UB Hr)Fc\|dkrtd|dd|dkrtd|dd|dk\rtd|ddtd |d z }t|z|zd z d t|zz }t |t rG|d k(r7|dk\r tdtd |d z } t t|  |z Stdt| |z|z} | td tz|z|zz} | td tz|z|zz} |s|s| S|s|r| | fS|r|s| | fS|r|r| | | fSyy)a. Return a Gaussian modulated sinusoid: ``exp(-a t^2) exp(1j*2*pi*fc*t).`` If `retquad` is True, then return the real and imaginary parts (in-phase and quadrature). If `retenv` is True, then return the envelope (unmodulated signal). Otherwise, return the real part of the modulated sinusoid. Parameters ---------- t : ndarray or the string 'cutoff' Input array. fc : float, optional Center frequency (e.g. Hz). Default is 1000. bw : float, optional Fractional bandwidth in frequency domain of pulse (e.g. Hz). Default is 0.5. bwr : float, optional Reference level at which fractional bandwidth is calculated (dB). Default is -6. tpr : float, optional If `t` is 'cutoff', then the function returns the cutoff time for when the pulse amplitude falls below `tpr` (in dB). Default is -60. retquad : bool, optional If True, return the quadrature (imaginary) as well as the real part of the signal. Default is False. retenv : bool, optional If True, return the envelope of the signal. Default is False. Returns ------- yI : ndarray Real part of signal. Always returned. yQ : ndarray Imaginary part of signal. Only returned if `retquad` is True. yenv : ndarray Envelope of signal. Only returned if `retenv` is True. Examples -------- Plot real component, imaginary component, and envelope for a 5 Hz pulse, sampled at 100 Hz for 2 seconds: >>> import numpy as np >>> from scipy import signal >>> import matplotlib.pyplot as plt >>> t = np.linspace(-1, 1, 2 * 100, endpoint=False) >>> i, q, e = signal.gausspulse(t, fc=5, retquad=True, retenv=True) >>> plt.plot(t, i, t, q, t, e, '--') rzCenter frequency (fc=z.2fz) must be >=0.zFractional bandwidth (bw=z) must be > 0.z#Reference level for bandwidth (bwr=z) must be < 0 dBg$@g4@rg@cutoffz.Reference level for time cutoff must be < 0 dBz'If `t` is a string, it must be 'cutoff'N) ValueErrorpowrr isinstancestrr r r r) rfcbwbwrtprretquadretenvrefatrefyenvyIyQs r(rrsp Av0CGHH Qw4RHNKLL ax>s3iH**+ + dC$J C r'B,1 c#h/A!S =ax "-..tS4Z(DT Q' 'FG G rAvz?D AFRK!O$ $B AFRK!O$ $B 6 v4xv2v 62t|wr))complexct||||||tj|z}|rtjd|zStj|S)aFrequency-swept cosine generator. In the following, 'Hz' should be interpreted as 'cycles per unit'; there is no requirement here that the unit is one second. The important distinction is that the units of rotation are cycles, not radians. Likewise, `t` could be a measurement of space instead of time. Parameters ---------- t : array_like Times at which to evaluate the waveform. f0 : float Frequency (e.g. Hz) at time t=0. t1 : float Time at which `f1` is specified. f1 : float Frequency (e.g. Hz) of the waveform at time `t1`. method : {'linear', 'quadratic', 'logarithmic', 'hyperbolic'}, optional Kind of frequency sweep. If not given, `linear` is assumed. See Notes below for more details. phi : float, optional Phase offset, in degrees. Default is 0. vertex_zero : bool, optional This parameter is only used when `method` is 'quadratic'. It determines whether the vertex of the parabola that is the graph of the frequency is at t=0 or t=t1. complex : bool, optional This parameter creates a complex-valued analytic signal instead of a real-valued signal. It allows the use of complex baseband (in communications domain). Default is False. .. versionadded:: 1.15.0 Returns ------- y : ndarray A numpy array containing the signal evaluated at `t` with the requested time-varying frequency. More precisely, the function returns ``exp(1j*phase + 1j*(pi/180)*phi) if complex else cos(phase + (pi/180)*phi)`` where `phase` is the integral (from 0 to `t`) of ``2*pi*f(t)``. The instantaneous frequency ``f(t)`` is defined below. See Also -------- sweep_poly Notes ----- There are four possible options for the parameter `method`, which have a (long) standard form and some allowed abbreviations. The formulas for the instantaneous frequency :math:`f(t)` of the generated signal are as follows: 1. Parameter `method` in ``('linear', 'lin', 'li')``: .. math:: f(t) = f_0 + \beta\, t \quad\text{with}\quad \beta = \frac{f_1 - f_0}{t_1} Frequency :math:`f(t)` varies linearly over time with a constant rate :math:`\beta`. 2. Parameter `method` in ``('quadratic', 'quad', 'q')``: .. math:: f(t) = \begin{cases} f_0 + \beta\, t^2 & \text{if vertex_zero is True,}\\ f_1 + \beta\, (t_1 - t)^2 & \text{otherwise,} \end{cases} \quad\text{with}\quad \beta = \frac{f_1 - f_0}{t_1^2} The graph of the frequency f(t) is a parabola through :math:`(0, f_0)` and :math:`(t_1, f_1)`. By default, the vertex of the parabola is at :math:`(0, f_0)`. If `vertex_zero` is ``False``, then the vertex is at :math:`(t_1, f_1)`. To use a more general quadratic function, or an arbitrary polynomial, use the function `scipy.signal.sweep_poly`. 3. Parameter `method` in ``('logarithmic', 'log', 'lo')``: .. math:: f(t) = f_0 \left(\frac{f_1}{f_0}\right)^{t/t_1} :math:`f_0` and :math:`f_1` must be nonzero and have the same sign. This signal is also known as a geometric or exponential chirp. 4. Parameter `method` in ``('hyperbolic', 'hyp')``: .. math:: f(t) = \frac{\alpha}{\beta\, t + \gamma} \quad\text{with}\quad \alpha = f_0 f_1 t_1, \ \beta = f_0 - f_1, \ \gamma = f_1 t_1 :math:`f_0` and :math:`f_1` must be nonzero. Examples -------- For the first example, a linear chirp ranging from 6 Hz to 1 Hz over 10 seconds is plotted: >>> import numpy as np >>> from matplotlib.pyplot import tight_layout >>> from scipy.signal import chirp, square, ShortTimeFFT >>> from scipy.signal.windows import gaussian >>> import matplotlib.pyplot as plt ... >>> N, T = 1000, 0.01 # number of samples and sampling interval for 10 s signal >>> t = np.arange(N) * T # timestamps ... >>> x_lin = chirp(t, f0=6, f1=1, t1=10, method='linear') ... >>> fg0, ax0 = plt.subplots() >>> ax0.set_title(r"Linear Chirp from $f(0)=6\,$Hz to $f(10)=1\,$Hz") >>> ax0.set(xlabel="Time $t$ in Seconds", ylabel=r"Amplitude $x_\text{lin}(t)$") >>> ax0.plot(t, x_lin) >>> plt.show() The following four plots each show the short-time Fourier transform of a chirp ranging from 45 Hz to 5 Hz with different values for the parameter `method` (and `vertex_zero`): >>> x_qu0 = chirp(t, f0=45, f1=5, t1=N*T, method='quadratic', vertex_zero=True) >>> x_qu1 = chirp(t, f0=45, f1=5, t1=N*T, method='quadratic', vertex_zero=False) >>> x_log = chirp(t, f0=45, f1=5, t1=N*T, method='logarithmic') >>> x_hyp = chirp(t, f0=45, f1=5, t1=N*T, method='hyperbolic') ... >>> win = gaussian(50, std=12, sym=True) >>> SFT = ShortTimeFFT(win, hop=2, fs=1/T, mfft=800, scale_to='magnitude') >>> ts = ("'quadratic', vertex_zero=True", "'quadratic', vertex_zero=False", ... "'logarithmic'", "'hyperbolic'") >>> fg1, ax1s = plt.subplots(2, 2, sharex='all', sharey='all', ... figsize=(6, 5), layout="constrained") >>> for x_, ax_, t_ in zip([x_qu0, x_qu1, x_log, x_hyp], ax1s.ravel(), ts): ... aSx = abs(SFT.stft(x_)) ... im_ = ax_.imshow(aSx, origin='lower', aspect='auto', extent=SFT.extent(N), ... cmap='plasma') ... ax_.set_title(t_) ... if t_ == "'hyperbolic'": ... fg1.colorbar(im_, ax=ax1s, label='Magnitude $|S_z(t,f)|$') >>> _ = fg1.supxlabel("Time $t$ in Seconds") # `_ =` is needed to pass doctests >>> _ = fg1.supylabel("Frequency $f$ in Hertz") >>> plt.show() Finally, the short-time Fourier transform of a complex-valued linear chirp ranging from -30 Hz to 30 Hz is depicted: >>> z_lin = chirp(t, f0=-30, f1=30, t1=N*T, method="linear", complex=True) >>> SFT.fft_mode = 'centered' # needed to work with complex signals >>> aSz = abs(SFT.stft(z_lin)) ... >>> fg2, ax2 = plt.subplots() >>> ax2.set_title(r"Linear Chirp from $-30\,$Hz to $30\,$Hz") >>> ax2.set(xlabel="Time $t$ in Seconds", ylabel="Frequency $f$ in Hertz") >>> im2 = ax2.imshow(aSz, origin='lower', aspect='auto', ... extent=SFT.extent(N), cmap='viridis') >>> fg2.colorbar(im2, label='Magnitude $|S_z(t,f)|$') >>> plt.show() Note that using negative frequencies makes only sense with complex-valued signals. Furthermore, the magnitude of the complex exponential function is one whereas the magnitude of the real-valued cosine function is only 1/2. y?) _chirp_phasenpdeg2radr r ) rf0t1f1methodphi vertex_zeror?phases r(rrsHL BB A#556H LE - -RB!G$ Fb1fta1f}q'889E> L;Fb1ftQ1}rQw/F'G!'KKLE: L7 / / 7c>?@ @ 8FRK!OE, L)BG $DFTMB&#b2gq2v*>*DEE& L# ( ( 7bAg() ) 8FRK!OE L38rBw'DFtebj)Cq1T6z0B,CCE L<>> import numpy as np >>> from scipy.signal import sweep_poly >>> p = np.poly1d([0.025, -0.36, 1.25, 2.0]) >>> t = np.linspace(0, 10, 5001) >>> w = sweep_poly(t, p) Plot it: >>> import matplotlib.pyplot as plt >>> plt.subplot(2, 1, 1) >>> plt.plot(t, w) >>> plt.title("Sweep Poly\nwith frequency " + ... "$f(t) = 0.025t^3 - 0.36t^2 + 1.25t + 2$") >>> plt.subplot(2, 1, 2) >>> plt.plot(t, p(t), 'r', label='f(t)') >>> plt.legend() >>> plt.xlabel('t') >>> plt.tight_layout() >>> plt.show() )_sweep_poly_phaserr )rpolyrHrJs r(rrs-x a &E28OC us{ r)cHt|}dtzt||z}|S)z Calculate the phase used by sweep_poly to generate its output. See `sweep_poly` for a description of the arguments. r)rrr)rr_intpolyrJs r(r^r^;s'dmG FWWa( (E Lr)ct||}tj|}|dt|z}n/|dk(rt |dz}nt |ds|ft|z}d||<|S)a Unit impulse signal (discrete delta function) or unit basis vector. Parameters ---------- shape : int or tuple of int Number of samples in the output (1-D), or a tuple that represents the shape of the output (N-D). idx : None or int or tuple of int or 'mid', optional Index at which the value is 1. If None, defaults to the 0th element. If ``idx='mid'``, the impulse will be centered at ``shape // 2`` in all dimensions. If an int, the impulse will be at `idx` in all dimensions. dtype : data-type, optional The desired data-type for the array, e.g., ``numpy.int8``. Default is ``numpy.float64``. Returns ------- y : ndarray Output array containing an impulse signal. Notes ----- In digital signal processing literature the unit impulse signal is often represented by the Kronecker delta. [1]_ I.e., a signal :math:`u_k[n]`, which is zero everywhere except being one at the :math:`k`-th sample, can be expressed as .. math:: u_k[n] = \delta[n-k] \equiv \delta_{n,k}\ . Furthermore, the unit impulse is frequently interpreted as the discrete-time version of the continuous-time Dirac distribution. [2]_ References ---------- .. [1] "Kronecker delta", *Wikipedia*, https://en.wikipedia.org/wiki/Kronecker_delta#Digital_signal_processing .. [2] "Dirac delta function" *Wikipedia*, https://en.wikipedia.org/wiki/Dirac_delta_function#Relationship_to_the_Kronecker_delta .. versionadded:: 0.19.0 Examples -------- An impulse at the 0th element (:math:`\\delta[n]`): >>> from scipy import signal >>> signal.unit_impulse(8) array([ 1., 0., 0., 0., 0., 0., 0., 0.]) Impulse offset by 2 samples (:math:`\\delta[n-2]`): >>> signal.unit_impulse(7, 2) array([ 0., 0., 1., 0., 0., 0., 0.]) 2-dimensional impulse, centered: >>> signal.unit_impulse((3, 3), 'mid') array([[ 0., 0., 0.], [ 0., 1., 0.], [ 0., 0., 0.]]) Impulse at (2, 2), using broadcasting: >>> signal.unit_impulse((4, 4), 2) array([[ 0., 0., 0., 0.], [ 0., 0., 0., 0.], [ 0., 0., 1., 0.], [ 0., 0., 0., 0.]]) Plot the impulse response of a 4th-order Butterworth lowpass filter: >>> imp = signal.unit_impulse(100, 'mid') >>> b, a = signal.butter(4, 0.2) >>> response = signal.lfilter(b, a, imp) >>> import numpy as np >>> import matplotlib.pyplot as plt >>> plt.plot(np.arange(-50, 50), imp) >>> plt.plot(np.arange(-50, 50), response) >>> plt.margins(0.1, 0.1) >>> plt.xlabel('Time [samples]') >>> plt.ylabel('Amplitude') >>> plt.grid(True) >>> plt.show() rmidr__iter__r)rrB atleast_1dlentuplehasattr)ridxrouts r(rrHsov u C MM% E {SZ EQJ S* %fs5z!CH Jr))r)rO)irOiiFF)rLrT)rLTrc)numpyrBrrrrrrr r r r r rrr__all__rrrrrArr^rXrr)r(rosr$$$$ A HC L=B^Bg:g:T1h_D !gr)