Skip to content

Commit c575c1e

Browse files
feat(seaborn): implement smith-chart-basic (#3867)
## Implementation: `smith-chart-basic` - seaborn Implements the **seaborn** version of `smith-chart-basic`. **File:** `plots/smith-chart-basic/implementations/seaborn.py` **Parent Issue:** #3792 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/21047488885)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 5817ed9 commit c575c1e

File tree

2 files changed

+389
-0
lines changed

2 files changed

+389
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
""" pyplots.ai
2+
smith-chart-basic: Smith Chart for RF/Impedance
3+
Library: seaborn 0.13.2 | Python 3.13.11
4+
Quality: 90/100 | Created: 2026-01-15
5+
"""
6+
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
import pandas as pd
10+
import seaborn as sns
11+
12+
13+
# Set seaborn style for better aesthetics
14+
sns.set_style("whitegrid")
15+
sns.set_context("talk", font_scale=1.2)
16+
17+
# Reference impedance (ohms)
18+
z0 = 50
19+
20+
# Generate frequency sweep data (1 GHz to 6 GHz, 50 points)
21+
np.random.seed(42)
22+
n_points = 50
23+
freq_ghz = np.linspace(1, 6, n_points)
24+
25+
# Simulate a realistic impedance locus (antenna-like behavior)
26+
# Create a spiral pattern that doesn't close on itself (avoids label overlap)
27+
t = np.linspace(0, 1.8 * np.pi, n_points)
28+
# Spiral-like pattern typical of antenna impedance vs frequency with drift
29+
z_real = 50 + 30 * np.sin(t) + 15 * np.cos(2 * t) + 10 * (t / (2 * np.pi))
30+
z_imag = 40 * np.sin(1.5 * t) + 20 * np.cos(t) - 15 * (t / (2 * np.pi))
31+
32+
# Normalize impedance to reference
33+
z_norm = (z_real + 1j * z_imag) / z0
34+
35+
# Calculate reflection coefficient (Gamma)
36+
gamma = (z_norm - 1) / (z_norm + 1)
37+
gamma_real = gamma.real
38+
gamma_imag = gamma.imag
39+
40+
# Create figure (square for Smith chart)
41+
fig, ax = plt.subplots(figsize=(12, 12))
42+
43+
# Draw Smith chart grid
44+
# Constant resistance circles
45+
r_values = [0, 0.2, 0.5, 1, 2, 5]
46+
theta = np.linspace(0, 2 * np.pi, 200)
47+
48+
for r in r_values:
49+
# Circle center and radius in Gamma plane
50+
center = r / (r + 1)
51+
radius = 1 / (r + 1)
52+
# Parametric circle
53+
circle_x = center + radius * np.cos(theta)
54+
circle_y = radius * np.sin(theta)
55+
# Clip to unit circle
56+
mask = circle_x**2 + circle_y**2 <= 1.001
57+
ax.plot(circle_x[mask], circle_y[mask], color="#306998", linewidth=1)
58+
# Add r value labels on the real axis (inside unit circle)
59+
if r > 0:
60+
label_x = r / (r + 1) - 1 / (r + 1) + 0.02 # Left edge of circle
61+
if label_x > -0.95:
62+
ax.text(label_x, 0.03, f"r={r}", fontsize=10, color="#306998", va="bottom")
63+
64+
# Constant reactance arcs
65+
x_values = [0.2, 0.5, 1, 2, 5]
66+
67+
for x in x_values:
68+
# Positive reactance arc (inductive)
69+
center_y = 1 / x
70+
radius = 1 / x
71+
arc_theta = np.linspace(-np.pi / 2, np.pi / 2, 200)
72+
arc_x = 1 + radius * np.cos(arc_theta)
73+
arc_y = center_y + radius * np.sin(arc_theta)
74+
mask = (arc_x**2 + arc_y**2 <= 1.001) & (arc_x >= -0.001)
75+
ax.plot(arc_x[mask], arc_y[mask], color="#D4A017", linewidth=1.5)
76+
77+
# Negative reactance arc (capacitive)
78+
arc_y_neg = -center_y + radius * np.sin(arc_theta)
79+
mask_neg = (arc_x**2 + arc_y_neg**2 <= 1.001) & (arc_x >= -0.001)
80+
ax.plot(arc_x[mask_neg], arc_y_neg[mask_neg], color="#D4A017", linewidth=1.5)
81+
82+
# Add x value labels inside the unit circle boundary
83+
if x <= 2:
84+
label_angle = np.arctan(1 / x)
85+
label_x_pos = 0.85 * np.cos(label_angle)
86+
label_y_pos = 0.85 * np.sin(label_angle)
87+
ax.text(label_x_pos, label_y_pos + 0.03, f"x={x}", fontsize=10, color="#D4A017", va="bottom", ha="center")
88+
ax.text(label_x_pos, -label_y_pos - 0.03, f"x=-{x}", fontsize=10, color="#D4A017", va="top", ha="center")
89+
90+
# Draw unit circle (|Gamma| = 1 boundary)
91+
unit_theta = np.linspace(0, 2 * np.pi, 200)
92+
ax.plot(np.cos(unit_theta), np.sin(unit_theta), color="#306998", linewidth=2.5)
93+
94+
# Draw horizontal axis (real axis)
95+
ax.axhline(0, color="#306998", linewidth=1.5, alpha=0.6)
96+
97+
# Create DataFrame for seaborn plotting
98+
df_locus = pd.DataFrame({"gamma_real": gamma_real, "gamma_imag": gamma_imag, "freq_ghz": freq_ghz})
99+
100+
# Plot impedance locus using seaborn lineplot for the trajectory
101+
sns.lineplot(
102+
data=df_locus, x="gamma_real", y="gamma_imag", color="#E74C3C", linewidth=3, ax=ax, legend=False, sort=False
103+
)
104+
105+
# Add markers at key frequency points using seaborn scatterplot
106+
key_indices = [0, n_points // 4, n_points // 2, 3 * n_points // 4, n_points - 1]
107+
df_markers = df_locus.iloc[key_indices].copy()
108+
sns.scatterplot(
109+
data=df_markers,
110+
x="gamma_real",
111+
y="gamma_imag",
112+
s=200,
113+
color="#E74C3C",
114+
edgecolor="white",
115+
linewidth=2,
116+
ax=ax,
117+
zorder=10,
118+
legend=False,
119+
)
120+
121+
# Label key frequency points with smart positioning to avoid overlap
122+
label_offsets = {
123+
0: (12, -18),
124+
n_points // 4: (15, 12),
125+
n_points // 2: (12, -18),
126+
3 * n_points // 4: (-60, 10),
127+
n_points - 1: (-60, -15),
128+
}
129+
130+
for idx in key_indices:
131+
offset = label_offsets.get(idx, (10, 10))
132+
ax.annotate(
133+
f"{freq_ghz[idx]:.1f} GHz",
134+
(gamma_real[idx], gamma_imag[idx]),
135+
textcoords="offset points",
136+
xytext=offset,
137+
fontsize=14,
138+
fontweight="bold",
139+
color="#2C3E50",
140+
)
141+
142+
# Mark the center (matched condition)
143+
ax.scatter([0], [0], s=150, color="#27AE60", marker="+", linewidths=3, zorder=10)
144+
ax.annotate(
145+
"Z₀ (50Ω)", (0, 0), textcoords="offset points", xytext=(-40, -20), fontsize=14, color="#27AE60", fontweight="bold"
146+
)
147+
148+
# Add VSWR circle (|Gamma| = 0.5, VSWR = 3:1)
149+
vswr_radius = 0.5
150+
vswr_circle_x = vswr_radius * np.cos(unit_theta)
151+
vswr_circle_y = vswr_radius * np.sin(unit_theta)
152+
ax.plot(vswr_circle_x, vswr_circle_y, "--", color="#9B59B6", linewidth=2)
153+
ax.annotate("VSWR 3:1", (0.35, 0.35), fontsize=12, color="#9B59B6", fontweight="bold")
154+
155+
# Styling
156+
ax.set_xlim(-1.15, 1.15)
157+
ax.set_ylim(-1.15, 1.15)
158+
ax.set_aspect("equal")
159+
ax.set_xlabel("Real(Γ)", fontsize=20)
160+
ax.set_ylabel("Imag(Γ)", fontsize=20)
161+
ax.set_title("smith-chart-basic · seaborn · pyplots.ai", fontsize=24, fontweight="bold", pad=20)
162+
ax.tick_params(axis="both", labelsize=16)
163+
ax.grid(False) # Turn off default grid, we drew our own Smith grid
164+
165+
# Add legend with matching line styles
166+
ax.plot([], [], color="#306998", linewidth=1, label="Constant R circles")
167+
ax.plot([], [], color="#D4A017", linewidth=1.5, label="Constant X arcs")
168+
ax.plot([], [], color="#E74C3C", linewidth=3, label="Impedance locus")
169+
ax.plot([], [], color="#9B59B6", linewidth=2, linestyle="--", label="VSWR 3:1 circle")
170+
ax.legend(loc="upper left", fontsize=14, framealpha=0.9)
171+
172+
plt.tight_layout()
173+
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
library: seaborn
2+
specification_id: smith-chart-basic
3+
created: '2026-01-15T21:52:07Z'
4+
updated: '2026-01-15T22:10:29Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 21047488885
7+
issue: 3792
8+
python_version: 3.13.11
9+
library_version: 0.13.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/seaborn/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/seaborn/plot_thumb.png
12+
preview_html: null
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent Smith chart grid implementation with proper constant R circles and X
17+
arcs
18+
- Smart frequency label positioning with manual offsets prevents overlap
19+
- Complete feature set including VSWR circle and center marking
20+
- Professional color scheme with good accessibility
21+
- Title format exactly matches specification
22+
weaknesses:
23+
- Heavy reliance on matplotlib for grid drawing; seaborn only used for final locus
24+
overlay
25+
- Canvas utilization could be improved (square 12x12 on 16:9 target)
26+
image_description: The plot displays a Smith chart for RF/impedance visualization.
27+
The chart features a square 12x12 figure with proper aspect ratio. The outer boundary
28+
is a dark blue unit circle representing |Γ|=1 (total reflection). Multiple constant
29+
resistance circles (r=0.2, 0.5, 1, 2, 5) are drawn in blue, centered along the
30+
horizontal real axis. Constant reactance arcs in gold/yellow (x=±0.2, ±0.5, ±1,
31+
±2) curve from the right edge. A red impedance locus curve traces the frequency
32+
sweep from 1.0 GHz to 6.0 GHz, with five key frequency markers (1.0, 3.2, 4.8,
33+
3.6, 6.0 GHz) labeled in dark blue text. A dashed purple VSWR 3:1 circle (|Γ|=0.5)
34+
is included. The center point is marked with a green '+' symbol and labeled 'Z₀
35+
(50Ω)'. A legend in the upper left shows all four element types. Axes are labeled
36+
'Real(Γ)' and 'Imag(Γ)' with appropriate font sizes.
37+
criteria_checklist:
38+
visual_quality:
39+
score: 36
40+
max: 40
41+
items:
42+
- id: VQ-01
43+
name: Text Legibility
44+
score: 10
45+
max: 10
46+
passed: true
47+
comment: Title at 24pt, axis labels at 20pt, tick labels at 16pt, frequency
48+
annotations at 14pt bold - all clearly readable
49+
- id: VQ-02
50+
name: No Overlap
51+
score: 8
52+
max: 8
53+
passed: true
54+
comment: Smart label positioning with custom offsets prevents all text overlap
55+
- id: VQ-03
56+
name: Element Visibility
57+
score: 6
58+
max: 8
59+
passed: true
60+
comment: Impedance locus clearly visible with linewidth=3, markers s=200;
61+
grid lines could be slightly thinner
62+
- id: VQ-04
63+
name: Color Accessibility
64+
score: 5
65+
max: 5
66+
passed: true
67+
comment: Blue/gold/red/purple color scheme is colorblind-safe, high contrast
68+
- id: VQ-05
69+
name: Layout Balance
70+
score: 3
71+
max: 5
72+
passed: true
73+
comment: Square 12x12 figure with xlim/ylim ±1.15 provides good margins, moderate
74+
canvas utilization
75+
- id: VQ-06
76+
name: Axis Labels
77+
score: 2
78+
max: 2
79+
passed: true
80+
comment: Real(Γ) and Imag(Γ) are descriptive for Smith chart context
81+
- id: VQ-07
82+
name: Grid & Legend
83+
score: 2
84+
max: 2
85+
passed: true
86+
comment: Default grid disabled, custom Smith grid drawn with appropriate line
87+
weights, legend well-placed
88+
spec_compliance:
89+
score: 25
90+
max: 25
91+
items:
92+
- id: SC-01
93+
name: Plot Type
94+
score: 8
95+
max: 8
96+
passed: true
97+
comment: Correct Smith chart with constant resistance circles and reactance
98+
arcs
99+
- id: SC-02
100+
name: Data Mapping
101+
score: 5
102+
max: 5
103+
passed: true
104+
comment: Impedance correctly normalized to Z₀=50Ω, gamma calculated properly
105+
- id: SC-03
106+
name: Required Features
107+
score: 5
108+
max: 5
109+
passed: true
110+
comment: 'All spec features: grid circles/arcs, impedance locus, frequency
111+
labels, VSWR circle, center marking'
112+
- id: SC-04
113+
name: Data Range
114+
score: 3
115+
max: 3
116+
passed: true
117+
comment: Unit circle boundary properly shown with ±1.15 limits
118+
- id: SC-05
119+
name: Legend Accuracy
120+
score: 2
121+
max: 2
122+
passed: true
123+
comment: Legend correctly identifies all four element types
124+
- id: SC-06
125+
name: Title Format
126+
score: 2
127+
max: 2
128+
passed: true
129+
comment: 'Uses exact format: smith-chart-basic · seaborn · pyplots.ai'
130+
data_quality:
131+
score: 18
132+
max: 20
133+
items:
134+
- id: DQ-01
135+
name: Feature Coverage
136+
score: 7
137+
max: 8
138+
passed: true
139+
comment: Shows spiral impedance locus typical of antenna behavior, crosses
140+
multiple grid regions
141+
- id: DQ-02
142+
name: Realistic Context
143+
score: 7
144+
max: 7
145+
passed: true
146+
comment: RF antenna impedance sweep 1-6 GHz is a realistic VNA measurement
147+
scenario
148+
- id: DQ-03
149+
name: Appropriate Scale
150+
score: 4
151+
max: 5
152+
passed: true
153+
comment: Z₀=50Ω standard, impedance values reasonable for antenna (30-80Ω
154+
range)
155+
code_quality:
156+
score: 8
157+
max: 10
158+
items:
159+
- id: CQ-01
160+
name: KISS Structure
161+
score: 3
162+
max: 3
163+
passed: true
164+
comment: 'Linear flow: imports → data → Smith grid → seaborn plots → styling
165+
→ save'
166+
- id: CQ-02
167+
name: Reproducibility
168+
score: 3
169+
max: 3
170+
passed: true
171+
comment: np.random.seed(42) set
172+
- id: CQ-03
173+
name: Clean Imports
174+
score: 0
175+
max: 2
176+
passed: false
177+
comment: sns and pd imports present but seaborn barely used beyond lineplot/scatterplot
178+
- id: CQ-04
179+
name: No Deprecated API
180+
score: 1
181+
max: 1
182+
passed: true
183+
comment: All APIs current
184+
- id: CQ-05
185+
name: Output Correct
186+
score: 1
187+
max: 1
188+
passed: true
189+
comment: Saves as plot.png with dpi=300
190+
library_features:
191+
score: 3
192+
max: 5
193+
items:
194+
- id: LF-01
195+
name: Distinctive Features
196+
score: 3
197+
max: 5
198+
passed: true
199+
comment: Uses sns.lineplot and sns.scatterplot with DataFrames, sns.set_style/set_context
200+
for theming, but heavy matplotlib reliance for grid drawing
201+
verdict: APPROVED
202+
impl_tags:
203+
dependencies: []
204+
techniques:
205+
- annotations
206+
- patches
207+
- manual-ticks
208+
- custom-legend
209+
patterns:
210+
- data-generation
211+
- iteration-over-groups
212+
- explicit-figure
213+
dataprep: []
214+
styling:
215+
- edge-highlighting
216+
- grid-styling

0 commit comments

Comments
 (0)