codex/ideal-converter-midpoint-quantization.py

ideal-converter-midpoint-quantization.py
"""Plot an original ideal 3-bit converter model into the current directory.

Build with ``make _media/ideal-converter-midpoint-quantization.png``.
The midpoint reconstruction convention is explicit; no hardware data is used.
"""
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    bits, full_scale = 3, 1.0
    levels = 2**bits
    step = full_scale / levels
    edges = np.linspace(0.0, full_scale, levels + 1)
    midpoints = (np.arange(levels) + .5) * step
    fig, axes = plt.subplots(1, 2, figsize=(9, 4), layout="constrained")
    fig.patch.set_facecolor("white")
    left, right = axes
    left.plot([0, full_scale], [0, full_scale], "--", color="#c16b29",
              label="Equal input and output")
    left.stairs(midpoints, edges, baseline=None, color="#176b91", lw=2,
                label="Midpoint reconstruction")
    for code, value in enumerate(midpoints):
        left.text(value, value + .025, f"{code:03b}", color="#176b91",
                  ha="center", va="bottom", fontsize=9)
    left.set(xlim=(0, full_scale), ylim=(0, full_scale + .04),
             xlabel="Input V (V)", ylabel="Reconstructed value (V)",
             title="Eight input intervals and their codes")
    left.legend(loc="lower right", fontsize=8)
    for lo, hi, midpoint in zip(edges[:-1], edges[1:], midpoints):
        right.plot([lo, hi], [midpoint - lo, midpoint - hi],
                   color="#176b91", lw=2)
        right.plot(lo, step / 2, "o", color="#176b91", ms=3)
        right.plot(hi, -step / 2, "o", markerfacecolor="white",
                   markeredgecolor="#176b91", ms=3)
    for bound in (-step / 2, step / 2):
        right.axhline(bound, ls="--", lw=1, color="#c16b29")
    right.set(xlim=(0, full_scale), ylim=(-.08, .08), xlabel="Input V (V)",
              ylabel="Reconstruction error (V)", title=r"Error bound: $|e|\leq\Delta/2$")
    right.set_yticks([-.0625, 0, .0625], ["−0.0625", "0", "+0.0625"])
    for ax in axes:
        ax.grid(alpha=.2)
    fig.suptitle("Ideal 3-bit model: full scale = 1 V; step Δ = 0.125 V", fontsize=12)
    fig.savefig(Path("ideal-converter-midpoint-quantization.png"), dpi=100,
                facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()