{
    "nbformat": 4,
    "nbformat_minor": 5,
    "metadata": {
        "kernelspec": {
            "display_name": "Python",
            "language": "python",
            "name": "python"
        },
        "language_info": {
            "name": "python"
        }
    },
    "cells": [
        {
            "id": "a2d75e29",
            "cell_type": "markdown",
            "source": "<div class='alert alert-warning'>\n\nSciPy's interactive examples with Jupyterlite are experimental and may not always work as expected. Execution of cells containing imports may result in large downloads (up to 60MB of content for the first import from SciPy). Load times when importing from SciPy may take roughly 10-20 seconds. If you notice any problems, feel free to open an [issue](https://github.com/scipy/scipy/issues/new/choose).\n\n</div>",
            "metadata": {}
        },
        {
            "id": "d53b8241",
            "cell_type": "markdown",
            "source": "The following plot illustrates the envelope of a signal with variable frequency and\na low-frequency drift. To separate the drift from the envelope, a 4 Hz highpass\nfilter is used. The low-pass residuum of the input bandpass filter is utilized to\ndetermine an asymmetric upper and lower bound to enclose the signal. Due to the\nsmoothness of the resulting envelope, it is down-sampled from 500 to 40 samples.\nNote that the instantaneous amplitude ``a_x`` and the computed envelope ``x_env``\nare not perfectly identical. This is due to the signal not being perfectly periodic\nas well as the existence of some spectral overlapping of ``x_carrier`` and\n``x_drift``. Hence, they cannot be completely separated by a bandpass filter.\n",
            "metadata": {}
        },
        {
            "id": "e616130f",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "import matplotlib.pyplot as plt\nimport numpy as np\nfrom scipy.signal.windows import gaussian\nfrom scipy.signal import envelope\n\nn, n_out = 500, 40  # number of signal samples and envelope samples\nT = 2 / n  # sampling interval for 2 s duration\nt = np.arange(n) * T  # time stamps\na_x = gaussian(len(t), 0.4/T)  # instantaneous amplitude\nphi_x = 30*np.pi*t + 35*np.cos(2*np.pi*0.25*t)  # instantaneous phase\nx_carrier = a_x * np.cos(phi_x)\nx_drift = 0.3 * gaussian(len(t), 0.4/T)  # drift\nx = x_carrier + x_drift\n\nbp_in = (int(4 * (n*T)), None)  # 4 Hz highpass input filter\nx_env, x_res = envelope(x, bp_in, n_out=n_out)\nt_out = np.arange(n_out) * (n / n_out) * T\n\nfg0, ax0 = plt.subplots(1, 1, tight_layout=True)\nax0.set_title(r\"$4\\,$Hz Highpass Envelope of Drifting Signal\")\nax0.set(xlabel=\"Time in seconds\", xlim=(0, n*T), ylabel=\"Amplitude\")\nax0.plot(t, x, 'C0-', alpha=0.5, label=\"Signal\")\nax0.plot(t, x_drift, 'C2--', alpha=0.25, label=\"Drift\")\nax0.plot(t_out, x_res+x_env, 'C1.-', alpha=0.5, label=\"Envelope\")\nax0.plot(t_out, x_res-x_env, 'C1.-', alpha=0.5, label=None)\nax0.grid(True)\nax0.legend()\nplt.show()",
            "outputs": []
        },
        {
            "id": "32e20ab4",
            "cell_type": "markdown",
            "source": "The second example provides a geometric envelope interpretation of complex-valued\nsignals: The following two plots show the complex-valued signal as a blue\n3d-trajectory and the envelope as an orange round tube with varying diameter, i.e.,\nas $|a(t)| \\exp(j\\rho(t))$, with $\\rho(t)\\in[-\\pi,\\pi]$. Also, the\nprojection into the 2d real and imaginary coordinate planes of trajectory and tube\nis depicted. Every point of the complex-valued signal touches the tube's surface.\n\nThe left plot shows an analytic signal, i.e, the phase difference between\nimaginary and real part is always 90 degrees, resulting in a spiraling trajectory.\nIt can be seen that in this case the real part has also the expected envelope,\ni.e., representing the absolute value of the instantaneous amplitude.\n\nThe right plot shows the real part of that analytic signal being interpreted\nas a complex-vauled signal, i.e., having zero imaginary part. There the resulting\nenvelope is not as smooth as in the analytic case and the instantaneous amplitude\nin the real plane is not recovered. If ``z_re`` had been passed as a real-valued\nsignal, i.e., as ``z_re = z.real`` instead of ``z_re = z.real + 0j``, the result\nwould have been identical to the left plot. The reason for this is that real-valued\nsignals are interpreted as being the real part of a complex-valued analytic signal.\n",
            "metadata": {}
        },
        {
            "id": "fe33d90b",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "import matplotlib.pyplot as plt\nimport numpy as np\nfrom scipy.signal.windows import gaussian\nfrom scipy.signal import envelope\n\nn, T = 1000, 1/1000  # number of samples and sampling interval\nt = np.arange(n) * T  # time stamps for 1 s duration\nf_c = 3  # Carrier frequency for signal\nz = gaussian(len(t), 0.3/T) * np.exp(2j*np.pi*f_c*t)  # analytic signal\nz_re = z.real + 0j  # complex signal with zero imaginary part\n\ne_a, e_r = (envelope(z_, (None, None), residual=None) for z_ in (z, z_re))\n\n# Generate grids to visualize envelopes as 2d and 3d surfaces:\nE2d_t, E2_amp = np.meshgrid(t, [-1, 1])\nE2d_1 = np.ones_like(E2_amp)\nE3d_t, E3d_phi = np.meshgrid(t, np.linspace(-np.pi, np.pi, 300))\nma = 1.8  # maximum axis values in real and imaginary direction\n\nfg0 = plt.figure(figsize=(6.2, 4.))\nax00 = fg0.add_subplot(1, 2, 1, projection='3d')\nax01 = fg0.add_subplot(1, 2, 2, projection='3d', sharex=ax00,\n                       sharey=ax00, sharez=ax00)\nax00.set_title(\"Analytic Signal\")\nax00.set(xlim=(0, 1), ylim=(-ma, ma), zlim=(-ma, ma))\nax01.set_title(\"Real-valued Signal\")\nfor z_, e_, ax_ in zip((z, z.real), (e_a, e_r), (ax00, ax01)):\n    ax_.set(xlabel=\"Time $t$\", ylabel=\"Real Amp. $x(t)$\",\n            zlabel=\"Imag. Amp. $y(t)$\")\n    ax_.plot(t, z_.real, 'C0-', zs=-ma, zdir='z', alpha=0.5, label=\"Real\")\n    ax_.plot_surface(E2d_t, e_*E2_amp, -ma*E2d_1, color='C1', alpha=0.25)\n    ax_.plot(t, z_.imag, 'C0-', zs=+ma, zdir='y', alpha=0.5, label=\"Imag.\")\n    ax_.plot_surface(E2d_t, ma*E2d_1, e_*E2_amp, color='C1', alpha=0.25)\n    ax_.plot(t, z_.real, z_.imag, 'C0-', label=\"Signal\")\n    ax_.plot_surface(E3d_t, e_*np.cos(E3d_phi), e_*np.sin(E3d_phi),\n                     color='C1', alpha=0.5, shade=True, label=\"Envelope\")\n    ax_.view_init(elev=22.7, azim=-114.3)\nfg0.subplots_adjust(left=0.08, right=0.97, wspace=0.15)\nplt.show()",
            "outputs": []
        }
    ]
}