{"spec_id":"smith-chart-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nsmith-chart-basic: Smith Chart for RF/Impedance\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-20\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    arrow,\n    coord_fixed,\n    element_blank,\n    element_rect,\n    element_text,\n    geom_path,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n)\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data — Smith chart grid\nZ0 = 50\nr_values = [0, 0.2, 0.5, 1, 2, 5]\ntheta = np.linspace(0, 2 * np.pi, 200)\n\nr_circle_data = []\nfor r in r_values:\n    cx = r / (r + 1)\n    radius = 1 / (r + 1)\n    x = cx + radius * np.cos(theta)\n    y = radius * np.sin(theta)\n    mask = x**2 + y**2 <= 1.001\n    r_circle_data.append(pd.DataFrame({\"x\": x[mask], \"y\": y[mask], \"grp\": f\"r_{r}\"}))\nr_circles_df = pd.concat(r_circle_data, ignore_index=True)\n\nx_values = [0.2, 0.5, 1, 2, 5]\nreactance_data = []\nfor xv in x_values:\n    radius_x = 1 / abs(xv)\n    t = np.linspace(0, 2 * np.pi, 500)\n    arc_x = 1 + radius_x * np.cos(t)\n    for sign, tag in [(1, \"p\"), (-1, \"n\")]:\n        arc_y = (sign / xv) + radius_x * np.sin(t)\n        mask = (arc_x**2 + arc_y**2 <= 1.001) & (arc_x >= -0.01)\n        if np.any(mask):\n            reactance_data.append(pd.DataFrame({\"x\": arc_x[mask], \"y\": arc_y[mask], \"grp\": f\"x_{tag}_{xv}\"}))\nreactance_df = pd.concat(reactance_data, ignore_index=True)\n\nboundary_theta = np.linspace(0, 2 * np.pi, 300)\nboundary_df = pd.DataFrame({\"x\": np.cos(boundary_theta), \"y\": np.sin(boundary_theta)})\naxis_df = pd.DataFrame({\"x\": [-1.0, 1.0], \"y\": [0.0, 0.0]})\n\n# Patch antenna S11 measurement from 1–6 GHz\nnp.random.seed(42)\nn_points = 50\nfreq_ghz = np.linspace(1, 6, n_points)\nz_real = 50 + 30 * np.sin(2 * np.pi * freq_ghz / 2.5) + np.random.randn(n_points) * 3\nz_imag = 20 * np.cos(2 * np.pi * freq_ghz / 3) + 15 * (freq_ghz - 3) + np.random.randn(n_points) * 2\n\nz_norm = (z_real + 1j * z_imag) / Z0\ngamma = (z_norm - 1) / (z_norm + 1)\nimpedance_df = pd.DataFrame({\"x\": np.real(gamma), \"y\": np.imag(gamma), \"freq\": freq_ghz})\n\n# Frequency labels at 4 key points with per-label nudges to prevent crowding\nlabel_freqs = [1.0, 2.5, 4.5, 6.0]\nlabel_idx = [int(np.argmin(np.abs(freq_ghz - f))) for f in label_freqs]\nlabels_df = impedance_df.iloc[label_idx].copy()\nlabels_df[\"label\"] = [f\"{f:.1f} GHz\" for f in label_freqs]\n\n# Per-label offsets: nudge 2.5/4.5 GHz apart horizontally; push 1.0 GHz up to clear Z=Z0 text\nx_nudges = np.array([0.00, 0.13, -0.13, 0.00])\ny_nudges = np.array([0.15, 0.15, 0.15, 0.14])\nlabels_df[\"x_label\"] = labels_df[\"x\"].values + x_nudges\nlabels_df[\"y_label\"] = labels_df[\"y\"].values + y_nudges\n\n# Resistance circle labels just above the real axis at each circle's leftmost point\nr_label_rows = [{\"x\": (r - 1) / (r + 1) + 0.05, \"y\": 0.09, \"label\": str(r)} for r in r_values]\nr_labels_df = pd.DataFrame(r_label_rows)\n\n# Mid-locus directional arrow (index 23→25) to indicate increasing frequency direction\narr_df = pd.DataFrame(\n    {\n        \"x\": [float(impedance_df.iloc[23][\"x\"])],\n        \"xend\": [float(impedance_df.iloc[25][\"x\"])],\n        \"y\": [float(impedance_df.iloc[23][\"y\"])],\n        \"yend\": [float(impedance_df.iloc[25][\"y\"])],\n    }\n)\n\n# Plot\nplot = (\n    ggplot()\n    + geom_path(aes(x=\"x\", y=\"y\", group=\"grp\"), data=r_circles_df, color=INK_SOFT, size=0.4, alpha=0.6)\n    + geom_path(aes(x=\"x\", y=\"y\", group=\"grp\"), data=reactance_df, color=INK_SOFT, size=0.4, alpha=0.4)\n    + geom_path(aes(x=\"x\", y=\"y\"), data=boundary_df, color=INK, size=1.0)\n    + geom_path(aes(x=\"x\", y=\"y\"), data=axis_df, color=INK_SOFT, size=0.5)\n    + geom_path(aes(x=\"x\", y=\"y\"), data=impedance_df, color=IMPRINT[0], size=1.5)\n    + geom_point(aes(x=\"x\", y=\"y\"), data=impedance_df, color=IMPRINT[0], size=2.5, alpha=0.65)\n    + geom_segment(\n        aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), data=arr_df, color=IMPRINT[0], size=2.0, arrow=arrow(length=0.10)\n    )\n    + geom_text(aes(x=\"x_label\", y=\"y_label\", label=\"label\"), data=labels_df, color=INK, size=9, fontweight=\"bold\")\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=r_labels_df, color=INK_MUTED, size=8)\n    + annotate(\"point\", x=0, y=0, color=IMPRINT[1], size=4)\n    + annotate(\"text\", x=0.15, y=-0.20, label=\"Z=Z₀\", color=IMPRINT[1], size=9)\n    + coord_fixed(ratio=1, xlim=(-1.3, 1.3), ylim=(-1.3, 1.3))\n    + scale_x_continuous(breaks=[])\n    + scale_y_continuous(breaks=[])\n    + labs(title=\"smith-chart-basic · python · plotnine · anyplot.ai\", x=\"\", y=\"\")\n    + theme(\n        figure_size=(6, 6),\n        plot_title=element_text(size=12, ha=\"center\", color=INK, fontweight=\"bold\"),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_blank(),\n        panel_grid_minor=element_blank(),\n        axis_text=element_blank(),\n        axis_ticks=element_blank(),\n        axis_title=element_text(color=INK),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=6, height=6, units=\"in\")\n"}