{"spec_id":"smith-chart-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nsmith-chart-basic: Smith Chart for RF/Impedance\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-05-20\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\nBRAND = \"#009E73\"  # Okabe-Ito position 1 — impedance locus\nACCENT = \"#C475FD\"  # Okabe-Ito position 2 — matched condition marker\n\n# Smith chart grid color (slightly stronger than RULE for chart structure)\nSMITH_GRID = \"rgba(74,74,68,0.30)\" if THEME == \"light\" else \"rgba(184,183,176,0.30)\"\n\n# Reference impedance\nZ0 = 50  # ohms\n\n# Generate sample impedance data (antenna-like frequency sweep, 1–6 GHz)\nnp.random.seed(42)\nfreq = np.linspace(1e9, 6e9, 50)\n\n# Simulate realistic antenna impedance trajectory:\n# starting inductive, sweeping through resonance to capacitive\nt = np.linspace(0, 2 * np.pi, 50)\nz_real = 25 + 40 * np.sin(t / 2) ** 2 + 5 * np.random.randn(50)\nz_imag = 60 * np.cos(t) + 10 * np.sin(2 * t)\n\n# Normalize impedance and calculate reflection coefficient (Γ)\nz_norm = (z_real + 1j * z_imag) / Z0\ngamma = (z_norm - 1) / (z_norm + 1)\ngamma_real = gamma.real\ngamma_imag = gamma.imag\n\nfig = go.Figure()\n\n# Smith chart grid — constant resistance circles\nr_values = [0, 0.2, 0.5, 1, 2, 5]\ntheta_grid = np.linspace(0, 2 * np.pi, 200)\n\nfor r in r_values:\n    center_x = r / (r + 1)\n    radius = 1 / (r + 1)\n    circle_x = center_x + radius * np.cos(theta_grid)\n    circle_y = radius * np.sin(theta_grid)\n    mask = circle_x**2 + circle_y**2 <= 1.01\n    fig.add_trace(\n        go.Scatter(\n            x=np.where(mask, circle_x, np.nan),\n            y=np.where(mask, circle_y, np.nan),\n            mode=\"lines\",\n            line=dict(color=SMITH_GRID, width=1),\n            hoverinfo=\"skip\",\n            showlegend=False,\n        )\n    )\n\n# Smith chart grid — constant reactance arcs (positive and negative)\nx_values = [0.2, 0.5, 1, 2, 5]\narc_theta = np.linspace(-np.pi, np.pi, 400)\n\nfor x in x_values:\n    center_y = 1 / x\n    radius = 1 / x\n    arc_x = 1 + radius * np.cos(arc_theta)\n    arc_y = center_y + radius * np.sin(arc_theta)\n    mask = (arc_x**2 + arc_y**2 <= 1.01) & (arc_x >= -1)\n    arc_x_clipped = np.where(mask, arc_x, np.nan)\n    arc_y_clipped = np.where(mask, arc_y, np.nan)\n    for sign in (1, -1):\n        fig.add_trace(\n            go.Scatter(\n                x=arc_x_clipped,\n                y=sign * arc_y_clipped,\n                mode=\"lines\",\n                line=dict(color=SMITH_GRID, width=1),\n                hoverinfo=\"skip\",\n                showlegend=False,\n            )\n        )\n\n# Real axis\nfig.add_trace(\n    go.Scatter(\n        x=[-1, 1], y=[0, 0], mode=\"lines\", line=dict(color=SMITH_GRID, width=1), hoverinfo=\"skip\", showlegend=False\n    )\n)\n\n# Unit circle boundary (|Γ| = 1)\nboundary_theta = np.linspace(0, 2 * np.pi, 300)\nfig.add_trace(\n    go.Scatter(\n        x=np.cos(boundary_theta),\n        y=np.sin(boundary_theta),\n        mode=\"lines\",\n        line=dict(color=INK_SOFT, width=2),\n        hoverinfo=\"skip\",\n        showlegend=False,\n    )\n)\n\n# Impedance locus\nfreq_ghz = freq / 1e9\nhover_text = [f\"{f:.2f} GHz<br>Z = {z_real[i]:.1f} + j{z_imag[i]:.1f} Ω\" for i, f in enumerate(freq_ghz)]\nfreq_normalized = np.linspace(0, 1, len(freq))\nfig.add_trace(\n    go.Scatter(\n        x=gamma_real,\n        y=gamma_imag,\n        mode=\"lines+markers\",\n        line=dict(color=BRAND, width=4.0),\n        marker=dict(\n            size=7,\n            color=freq_normalized,\n            colorscale=\"viridis\",\n            showscale=True,\n            colorbar=dict(\n                title=dict(text=\"GHz\", font=dict(size=10, color=INK), side=\"top\"),\n                tickvals=[0, 0.5, 1],\n                ticktext=[\"1\", \"3.5\", \"6\"],\n                len=0.4,\n                thickness=12,\n                x=1.02,\n                tickfont=dict(size=9, color=INK_SOFT),\n                bgcolor=ELEVATED_BG,\n                bordercolor=INK_SOFT,\n                borderwidth=1,\n            ),\n            line=dict(color=PAGE_BG, width=1.5),\n        ),\n        name=\"Impedance Locus\",\n        text=hover_text,\n        hoverinfo=\"text\",\n    )\n)\n\n# Frequency labels at key points along the locus\nlabel_configs = [\n    (0, 50, -30),  # 1.0 GHz — right side\n    (16, -50, -30),  # 2.6 GHz\n    (32, 50, 30),  # 4.3 GHz\n    (49, -50, -60),  # 6.0 GHz — left side (opposite of 1.0 GHz to avoid overlap)\n]\nfor idx, ax_offset, ay_offset in label_configs:\n    fig.add_annotation(\n        x=gamma_real[idx],\n        y=gamma_imag[idx],\n        text=f\"{freq_ghz[idx]:.1f} GHz\",\n        showarrow=True,\n        arrowhead=2,\n        arrowsize=1,\n        arrowwidth=1.5,\n        arrowcolor=BRAND,\n        ax=ax_offset,\n        ay=ay_offset,\n        font=dict(size=11, color=INK),\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n        borderpad=3,\n    )\n\n# Matched condition marker (Z = Z₀, Γ = 0)\nfig.add_trace(\n    go.Scatter(\n        x=[0],\n        y=[0],\n        mode=\"markers\",\n        marker=dict(size=14, color=ACCENT, symbol=\"x\", line=dict(color=ACCENT, width=3)),\n        name=\"Matched (Z = Z₀)\",\n        hoverinfo=\"name\",\n    )\n)\n\n# Resistance labels along the real axis at each circle center\nr_labels = [(0, \"0\"), (0.2, \"0.2\"), (0.5, \"0.5\"), (1, \"1\"), (2, \"2\"), (5, \"5\")]\nfor r, label in r_labels:\n    x_pos = r / (r + 1)  # center of each constant-resistance circle\n    fig.add_annotation(x=x_pos, y=0, text=label, showarrow=False, font=dict(size=10, color=INK_SOFT), yshift=-16)\n\n# Reactance labels near chart boundary\nreactance_labels = [(0.85, 0.52, \"+j1\"), (0.85, -0.52, \"−j1\"), (0.60, 0.80, \"+j0.5\"), (0.60, -0.80, \"−j0.5\")]\nfor lx, ly, label in reactance_labels:\n    fig.add_annotation(x=lx, y=ly, text=label, showarrow=False, font=dict(size=10, color=INK_SOFT))\n\nfig.update_layout(\n    title=dict(\n        text=\"smith-chart-basic · python · plotly · anyplot.ai\", font=dict(size=16, color=INK), x=0.5, xanchor=\"center\"\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    xaxis=dict(\n        title=dict(text=\"Re(Γ)\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        range=[-1.15, 1.15],\n        scaleanchor=\"y\",\n        scaleratio=1,\n        showgrid=False,\n        zeroline=False,\n        linecolor=INK_SOFT,\n        tickcolor=INK_SOFT,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Im(Γ)\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        range=[-1.15, 1.15],\n        showgrid=False,\n        zeroline=False,\n        linecolor=INK_SOFT,\n        tickcolor=INK_SOFT,\n    ),\n    legend=dict(\n        x=0.02, y=0.02, font=dict(size=10, color=INK_SOFT), bgcolor=ELEVATED_BG, bordercolor=INK_SOFT, borderwidth=1\n    ),\n    margin=dict(l=70, r=120, t=80, b=70),\n)\n\nfig.write_image(f\"plot-{THEME}.png\", width=600, height=600, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}