codexgalvanometer-calibrated-deflection-current.py
galvanometer-calibrated-deflection-current.py
"""Plot an illustrative calibration; Python 3.14, matplotlib 3.10.7, NumPy 2.3.5."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white"})
turns, area, field, stiffness = 120, 3.00e-4, 0.250, 3.00e-6
sensitivity = turns * area * field / stiffness
current = np.linspace(-60, 60, 481) # microamperes
degrees = np.rad2deg(sensitivity * current * 1e-6)
resolution = 0.10 # chosen readout tolerance, not measured noise
unresolved_current = np.deg2rad(resolution) / sensitivity * 1e6
fig, ax = plt.subplots(figsize=(8.6, 5.3))
ax.plot(current, degrees, color="#2166ac", lw=2, label="Calculated linear calibration")
selected = np.array([-40, -20, 0, 20, 40])
ax.scatter(selected, np.rad2deg(sensitivity * selected * 1e-6), s=42,
color="#2166ac", zorder=3, label="Illustrative settings (not measurements)")
ax.axhspan(-resolution, resolution, color="#e5c565", alpha=0.65,
label="Chosen ±0.10° zero tolerance")
ax.axhline(0, color="#666666", linewidth=0.8)
ax.axvline(0, color="#666666", linewidth=0.8)
ax.axvline(-60, ls=":", color="#777777")
ax.axvline(60, ls=":", color="#777777")
ax.set(xlim=(-65, 65), ylim=(-11.4, 11.4),
xlabel="Signed coil current I (µA)", ylabel="Settled deflection θ (degrees)",
title="Moving-coil deflection: calibration and reversal")
ax.set_xticks(np.arange(-60, 61, 20))
ax.grid(alpha=0.18)
ax.annotate("+40 µA → +6.88°", xy=(40, np.rad2deg(0.12)), xytext=(12, 9.5),
arrowprops={"arrowstyle": "->", "color": "#2166ac"}, color="#2166ac")
ax.annotate("−40 µA → −6.88°", xy=(-40, -np.rad2deg(0.12)), xytext=(-55, -10.2),
arrowprops={"arrowstyle": "->", "color": "#2166ac"}, color="#2166ac")
ax.legend(loc="upper left", fontsize=9, framealpha=1)
inset = ax.inset_axes([0.63, 0.12, 0.33, 0.31])
near_zero = np.linspace(-1.5, 1.5, 101)
inset.axhspan(-resolution, resolution, color="#e5c565", alpha=0.65)
inset.plot(near_zero, np.rad2deg(sensitivity * near_zero * 1e-6), color="#2166ac", lw=2)
for value in [-unresolved_current, unresolved_current]:
inset.axvline(value, color="#84691d", ls="--", lw=1)
inset.axhline(0, color="#666666", lw=0.7)
inset.axvline(0, color="#666666", lw=0.7)
inset.set(xlim=(-1.5, 1.5), ylim=(-0.26, 0.26),
title="Zero band: ±0.582 µA", xlabel="I (µA)", ylabel="θ (°)")
inset.set_xticks([-1, 0, 1])
inset.set_yticks([-0.2, 0, 0.2])
inset.tick_params(labelsize=8)
inset.xaxis.label.set_size(9)
inset.yaxis.label.set_size(9)
inset.title.set_size(10)
fig.text(0.5, 0.048, "N = 120 A = 3.00 × 10⁻⁴ m² B = 0.250 T k = 3.00 × 10⁻⁶ N m/rad",
ha="center", fontsize=10)
fig.text(0.5, 0.014, "Sᵢ = 3000 rad/A • chosen full scale: ±0.180 rad at ±60 µA • model, not experimental data",
ha="center", fontsize=10)
fig.subplots_adjust(left=0.10, right=0.98, top=0.89, bottom=0.19)
fig.savefig(Path(__file__).stem + ".png", dpi=130, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()