codex/amplitude-modulation-envelope-and-phase-reversal.py

amplitude-modulation-envelope-and-phase-reversal.py
"""Original AM model; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.

Run from _media; writes this script's basename as an opaque white PNG.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    cycles = np.linspace(0, 2, 8001)
    theta = 2 * np.pi * cycles
    fig, axes = plt.subplots(2, 1, figsize=(10, 6.4), sharex=True)
    for ax, m in zip(axes, (0.5, 1.4), strict=True):
        q = 1 + m * np.cos(theta)
        signal = q * np.cos(20 * theta)
        envelope = np.abs(q)
        ax.fill_between(cycles, -2.65, 2.65, where=q < 0,
                        color="#f4e1f0", interpolate=True)
        ax.plot(cycles, signal, color="#266ba3", lw=1.0, label=r"voltage $s/A$")
        ax.plot(cycles, envelope, color="#d87516", lw=2.2, label=r"bounds $\pm |q|$")
        ax.plot(cycles, -envelope, color="#d87516", lw=2.2)
        ax.plot(cycles, q, color="#863a96", lw=1.8, ls=(0, (5, 3)),
                label=r"signed factor $q=1+m\cos(\Omega t)$")
        ax.axhline(0, color="#777777", lw=0.7)
        ax.set_ylim(-2.65, 2.65)
        ax.set_xlim(0, 2)
        ax.set_yticks([-2, -1, 0, 1, 2])
        ax.set_ylabel("Voltage / carrier peak A")
        ax.set_title(f"m = {m:g}" + (" : positive factor, faithful envelope" if m < 1
                                      else " : envelope folds at sign changes"), loc="left")
        ax.grid(alpha=0.16)
        if m > 1:
            ax.annotate(r"$q<0$: carrier phase flips by $\pi$",
                        xy=(0.50, -0.4), xytext=(0.50, -1.35), fontsize=10, ha="center",
                        arrowprops={"arrowstyle": "->", "color": "#863a96"},
                        bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.92},
                        color="#702980")
    axes[0].legend(loc="lower center", ncols=3, fontsize=9, framealpha=1)
    axes[1].set_xticks(np.arange(0, 2.01, 0.25))
    axes[1].set_xlabel(r"Time in message cycles: $\Omega t/(2\pi)$")
    fig.suptitle("Amplitude modulation: signed factor versus nonnegative envelope", fontsize=14)
    fig.text(0.5, 0.018, r"Original linear model; $\omega_c/\Omega=20$. Calculated curves, not measured traces.",
             ha="center", fontsize=10)
    fig.subplots_adjust(left=0.095, right=0.985, top=0.875, bottom=0.125, hspace=0.30)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=120,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()