Chris Parmer — home

Smith chart

scikit-rf ring-slot resonator, W-band 75–110 GHz

Example from the compendium of canonical charts

Smith chart — scikit-rf ring-slot resonator, W-band 75–110 GHz

Python Code

"""Smith chart — scikit-rf ring-slot resonator, W-band 75–110 GHz."""


import numpy as np
import pandas as pd
import plotly.graph_objects as go
import requests, warnings, io

URL = "https://raw.githubusercontent.com/scikit-rf/scikit-rf/master/skrf/data/ring%20slot%20measured.s1p"


def parse_s1p(text):
    """Parse Touchstone S1P: skip ! and # comment lines, read freq Re Im."""
    rows = []
    for line in text.splitlines():
        line = line.strip()
        if not line or line.startswith("!") or line.startswith("#"):
            continue
        parts = line.split()
        if len(parts) >= 3:
            try:
                rows.append([float(p) for p in parts[:3]])
            except ValueError:
                pass
    return pd.DataFrame(rows, columns=["freq_ghz", "re", "im"])


def generate():
    print("fetching ring-slot resonator S1P …")
    warnings.filterwarnings("ignore")
    requests.packages.urllib3.disable_warnings()
    r = requests.get(URL, verify=False, timeout=30)
    r.raise_for_status()
    df = parse_s1p(r.text)
    print(f"  {len(df)} frequency points, {df['freq_ghz'].min():.1f}–{df['freq_ghz'].max():.1f} GHz")

    # S1P RI format: re + j*im are reflection coefficient Γ
    # Clip to unit circle: passive devices have |Γ| ≤ 1
    gamma = df["re"].values + 1j * df["im"].values
    mag   = np.abs(gamma)
    mask  = mag <= 1.0
    gamma = gamma[mask]
    mag   = mag[mask]
    df    = df[mask].copy().reset_index(drop=True)
    print(f"  {len(df)} points inside unit disk")

    # Convert Γ → normalized impedance Z = (1 + Γ) / (1 - Γ)
    # go.Scattersmith expects Z (real, imag), not Γ
    denom = 1 - gamma
    denom[np.abs(denom) < 1e-9] = 1e-9   # avoid division by zero at Γ=1
    z = (1 + gamma) / denom
    df["z_re"] = z.real
    df["z_im"] = z.imag

    # Find best match (minimum |S11|)
    best_idx  = np.argmin(mag)
    best_freq = df.loc[best_idx, "freq_ghz"]
    best_mag  = float(mag[best_idx])
    return_loss = -20 * np.log10(best_mag + 1e-9)
    vswr = (1 + best_mag) / max(1 - best_mag, 1e-9)

    print(f"  best match: {best_freq:.2f} GHz, |S11|={best_mag:.3f}, RL={return_loss:.1f}dB, VSWR={vswr:.2f}")

    fig = go.Figure(go.Scattersmith(
        real=df["z_re"],
        imag=df["z_im"],
        mode="markers+lines",
        marker=dict(
            color=df["freq_ghz"],
            colorscale=[[0, TEAL], [0.5, VIOLET], [1, PINK]],
            size=5,
            colorbar=dict(title="GHz", thickness=12),
            showscale=True,
        ),
        line=dict(color=VIOLET, width=1.5),
        text=[f"{f:.2f} GHz" for f in df["freq_ghz"]],
        hovertemplate="%{text}<br>Re=%{real:.3f}, Im=%{imag:.3f}<extra></extra>",
        showlegend=False,
    ))

    # Highlight the best-match point in a contrasting colour (green against the
    # cyan→violet→pink frequency scale) so the annotation has a visible referent.
    fig.add_trace(go.Scattersmith(
        real=[df.loc[best_idx, "z_re"]],
        imag=[df.loc[best_idx, "z_im"]],
        mode="markers",
        marker=dict(color=GREEN, size=11, line=dict(color="white", width=1.5)),
        showlegend=False,
        text=[f"{best_freq:.2f} GHz"],
        hovertemplate="Best match — %{text}<br>Re=%{real:.3f}, Im=%{imag:.3f}<extra></extra>",
    ))

    fig.add_annotation(
        x=0.72, y=0.92, xref="paper", yref="paper", xanchor="right", yanchor="top",
        text=f"Best match: {best_freq:.2f} GHz<br>RL = {return_loss:.1f} dB<br>VSWR = {vswr:.2f}",
        showarrow=False,
        bgcolor="rgba(255,255,255,0.85)",
        bordercolor="#ccc",
        font=dict(size=10),
    )

    fig.update_layout(
        title=dict(text="Smith Chart — Ring-Slot Resonator, W-band (75–110 GHz)", x=0.5),
        margin=dict(t=60, b=40, l=40, r=80),
        height=500,
    )
    return fig


if __name__ == "__main__":
    generate()

Made with Plotly