Skip to content

Commit 0d3d5b6

Browse files
feat(pygal): implement smith-chart-basic (#3873)
## Implementation: `smith-chart-basic` - pygal Implements the **pygal** version of `smith-chart-basic`. **File:** `plots/smith-chart-basic/implementations/pygal.py` **Parent Issue:** #3792 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/21047489081)* --------- 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 c575c1e commit 0d3d5b6

File tree

2 files changed

+345
-0
lines changed

2 files changed

+345
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
""" pyplots.ai
2+
smith-chart-basic: Smith Chart for RF/Impedance
3+
Library: pygal 3.1.0 | Python 3.13.11
4+
Quality: 86/100 | Created: 2026-01-15
5+
"""
6+
7+
import numpy as np
8+
import pygal
9+
from pygal.style import Style
10+
11+
12+
# Reference impedance
13+
Z0 = 50 # ohms
14+
15+
# Grid parameters
16+
r_values = [0, 0.2, 0.5, 1, 2, 5] # Constant resistance circles
17+
x_values = [0.2, 0.5, 1, 2, 5] # Constant reactance arcs
18+
19+
# Custom style - Python Blue boundary, gray grid, prominent data
20+
custom_style = Style(
21+
background="white",
22+
plot_background="white",
23+
foreground="#333333",
24+
foreground_strong="#333333",
25+
foreground_subtle="#888888",
26+
colors=("#E74C3C", "#9B59B6", "#F39C12", "#2ECC71", "#306998", "#BBBBBB"),
27+
title_font_size=56,
28+
label_font_size=24,
29+
major_label_font_size=22,
30+
legend_font_size=28,
31+
value_font_size=20,
32+
stroke_width=2,
33+
opacity=1.0,
34+
opacity_hover=1.0,
35+
)
36+
37+
# Create XY chart for Smith chart - square format better for circular chart
38+
chart = pygal.XY(
39+
width=3600,
40+
height=3600,
41+
style=custom_style,
42+
title="smith-chart-basic · pygal · pyplots.ai",
43+
show_legend=True,
44+
legend_at_bottom=True,
45+
legend_at_bottom_columns=4,
46+
show_x_guides=False,
47+
show_y_guides=False,
48+
x_title="Reflection Coefficient Real Part",
49+
y_title="Reflection Coefficient Imaginary Part",
50+
dots_size=6,
51+
range=(-1.15, 1.15),
52+
xrange=(-1.15, 1.15),
53+
margin=40,
54+
)
55+
56+
# Generate unit circle boundary (|gamma| = 1) - Python Blue (structural, no legend)
57+
theta = np.linspace(0, 2 * np.pi, 200)
58+
unit_circle = [(np.cos(t), np.sin(t)) for t in theta]
59+
chart.add("", unit_circle, show_dots=False, stroke="#306998", stroke_width=4)
60+
61+
# Generate constant resistance circles as a single combined grid element (no legend)
62+
all_r_points = []
63+
for r in r_values:
64+
x_range = np.concatenate([np.linspace(-50, -0.01, 100), np.linspace(0.01, 50, 100)])
65+
for x in x_range:
66+
z_norm = complex(r, x)
67+
gamma = (z_norm - 1) / (z_norm + 1)
68+
if abs(gamma) <= 1.001:
69+
all_r_points.append((gamma.real, gamma.imag))
70+
all_r_points.append((None, None))
71+
chart.add("", all_r_points, show_dots=False, stroke="#BBBBBB", stroke_width=1.5, stroke_dasharray="8,4")
72+
73+
# Generate constant reactance arcs combined (no legend)
74+
all_x_points = []
75+
for x in x_values:
76+
# Positive reactance arc
77+
for r in np.linspace(0.001, 50, 100):
78+
z_norm = complex(r, x)
79+
gamma = (z_norm - 1) / (z_norm + 1)
80+
if abs(gamma) <= 1.001:
81+
all_x_points.append((gamma.real, gamma.imag))
82+
all_x_points.append((None, None))
83+
# Negative reactance arc
84+
for r in np.linspace(0.001, 50, 100):
85+
z_norm = complex(r, -x)
86+
gamma = (z_norm - 1) / (z_norm + 1)
87+
if abs(gamma) <= 1.001:
88+
all_x_points.append((gamma.real, gamma.imag))
89+
all_x_points.append((None, None))
90+
chart.add("", all_x_points, show_dots=False, stroke="#BBBBBB", stroke_width=1.5, stroke_dasharray="8,4")
91+
92+
# Add horizontal axis (real axis, no legend)
93+
chart.add("", [(-1, 0), (1, 0)], show_dots=False, stroke="#555555", stroke_width=3)
94+
95+
# Generate example impedance data - antenna impedance sweep 1-6 GHz
96+
np.random.seed(42)
97+
num_points = 50
98+
frequencies = np.linspace(1e9, 6e9, num_points) # 1-6 GHz
99+
100+
# Simulate antenna impedance that varies with frequency
101+
base_r = 25 + 50 * np.exp(-frequencies / 3e9)
102+
base_x = 30 * np.sin(2 * np.pi * frequencies / 2e9) + 20 * np.cos(frequencies / 1e9)
103+
z_real = base_r + np.random.randn(num_points) * 3
104+
z_imag = base_x + np.random.randn(num_points) * 5
105+
106+
# Normalize impedance and convert to reflection coefficient
107+
z_complex = z_real + 1j * z_imag
108+
z_normalized = z_complex / Z0
109+
gamma = (z_normalized - 1) / (z_normalized + 1)
110+
111+
# Create impedance locus data points
112+
impedance_locus = [(g.real, g.imag) for g in gamma]
113+
114+
# Add impedance locus curve - Red for visibility
115+
chart.add("Antenna Z(f)", impedance_locus, show_dots=True, stroke_width=4, dots_size=5)
116+
117+
# Add labeled frequency markers at key points
118+
# 1 GHz marker
119+
chart.add("1 GHz", [(gamma[0].real, gamma[0].imag)], show_dots=True, dots_size=16, stroke=False)
120+
# 3.5 GHz marker (midpoint)
121+
chart.add(
122+
"3.5 GHz", [(gamma[num_points // 2].real, gamma[num_points // 2].imag)], show_dots=True, dots_size=16, stroke=False
123+
)
124+
# 6 GHz marker
125+
chart.add("6 GHz", [(gamma[-1].real, gamma[-1].imag)], show_dots=True, dots_size=16, stroke=False)
126+
127+
# Save outputs
128+
chart.render_to_file("plot.html")
129+
chart.render_to_png("plot.png")
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
library: pygal
2+
specification_id: smith-chart-basic
3+
created: '2026-01-15T21:55:08Z'
4+
updated: '2026-01-15T22:14:54Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 21047489081
7+
issue: 3792
8+
python_version: 3.13.11
9+
library_version: 3.1.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/pygal/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/pygal/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/pygal/plot.html
13+
quality_score: 86
14+
review:
15+
strengths:
16+
- Mathematically correct Smith chart implementation with proper impedance-to-reflection
17+
coefficient conversion
18+
- Good use of the square format (3600x3600) appropriate for circular chart geometry
19+
- Realistic RF engineering scenario with antenna impedance sweep
20+
- Clear visual distinction between grid elements (gray dashed) and data (red)
21+
- Frequency markers well-differentiated with distinct colors (purple, yellow, green)
22+
- Proper normalization to 50 ohm reference impedance
23+
weaknesses:
24+
- Legend at bottom is truncated/cut off, making frequency marker labels partially
25+
unreadable
26+
- Legend text appears too small relative to the chart size
27+
- Grid lines and data elements could be more visually prominent given the large
28+
canvas size
29+
image_description: 'The plot displays a Smith chart for RF/impedance visualization.
30+
A blue circular boundary represents the unit circle (|Γ|=1). Gray dashed lines
31+
show the constant resistance circles and constant reactance arcs forming the characteristic
32+
Smith chart grid pattern. A dark gray horizontal line represents the real axis.
33+
The main data series "Antenna Z(f)" is plotted in red/coral color as a connected
34+
locus curve with small dots at each data point, showing the impedance trajectory
35+
across frequency. Three frequency marker points are highlighted: a purple/violet
36+
dot at "1 GHz" (upper left region around -0.1, 0.3), a yellow/gold dot at "3.5
37+
GHz" (right center area around 0.1, 0.1), and a green dot at "6 GHz" (lower center
38+
around 0.05, -0.45). The title "smith-chart-basic · pygal · pyplots.ai" appears
39+
at top center. Axis labels read "Reflection Coefficient Real Part" (x-axis) and
40+
"Reflection Coefficient Imaginary Part" (y-axis). A legend at the bottom shows
41+
all series. The chart uses a square 1:1 aspect ratio appropriate for the circular
42+
Smith chart geometry.'
43+
criteria_checklist:
44+
visual_quality:
45+
score: 33
46+
max: 40
47+
items:
48+
- id: VQ-01
49+
name: Text Legibility
50+
score: 8
51+
max: 10
52+
passed: true
53+
comment: Title and axis labels are readable but legend text at bottom is quite
54+
small and partially cut off
55+
- id: VQ-02
56+
name: No Overlap
57+
score: 8
58+
max: 8
59+
passed: true
60+
comment: No overlapping text elements
61+
- id: VQ-03
62+
name: Element Visibility
63+
score: 7
64+
max: 8
65+
passed: true
66+
comment: Data points and grid are visible; impedance locus could use slightly
67+
larger markers
68+
- id: VQ-04
69+
name: Color Accessibility
70+
score: 5
71+
max: 5
72+
passed: true
73+
comment: 'Good color choices: blue boundary, gray grid, red/coral data, distinct
74+
frequency markers'
75+
- id: VQ-05
76+
name: Layout Balance
77+
score: 3
78+
max: 5
79+
passed: true
80+
comment: Chart is well-centered but legend at bottom is cramped and partially
81+
truncated
82+
- id: VQ-06
83+
name: Axis Labels
84+
score: 2
85+
max: 2
86+
passed: true
87+
comment: Descriptive labels for reflection coefficient real and imaginary
88+
parts
89+
- id: VQ-07
90+
name: Grid & Legend
91+
score: 0
92+
max: 2
93+
passed: false
94+
comment: Grid is good but legend is cut off at the bottom, making some labels
95+
unreadable
96+
spec_compliance:
97+
score: 23
98+
max: 25
99+
items:
100+
- id: SC-01
101+
name: Plot Type
102+
score: 8
103+
max: 8
104+
passed: true
105+
comment: Correct Smith chart with circular boundary, resistance circles, and
106+
reactance arcs
107+
- id: SC-02
108+
name: Data Mapping
109+
score: 5
110+
max: 5
111+
passed: true
112+
comment: Impedance correctly normalized and converted to reflection coefficient
113+
- id: SC-03
114+
name: Required Features
115+
score: 4
116+
max: 5
117+
passed: true
118+
comment: Has grid, impedance locus, and frequency markers; VSWR circles optional
119+
- id: SC-04
120+
name: Data Range
121+
score: 3
122+
max: 3
123+
passed: true
124+
comment: Full Smith chart range displayed (-1.15 to 1.15)
125+
- id: SC-05
126+
name: Legend Accuracy
127+
score: 1
128+
max: 2
129+
passed: true
130+
comment: Legend labels correct but partially cut off
131+
- id: SC-06
132+
name: Title Format
133+
score: 2
134+
max: 2
135+
passed: true
136+
comment: 'Correct format: smith-chart-basic · pygal · pyplots.ai'
137+
data_quality:
138+
score: 18
139+
max: 20
140+
items:
141+
- id: DQ-01
142+
name: Feature Coverage
143+
score: 7
144+
max: 8
145+
passed: true
146+
comment: Shows impedance variation with both inductive and capacitive regions
147+
- id: DQ-02
148+
name: Realistic Context
149+
score: 7
150+
max: 7
151+
passed: true
152+
comment: Antenna impedance sweep 1-6 GHz is a realistic RF engineering scenario
153+
- id: DQ-03
154+
name: Appropriate Scale
155+
score: 4
156+
max: 5
157+
passed: true
158+
comment: Values are reasonable though trajectory is clustered in the center
159+
code_quality:
160+
score: 9
161+
max: 10
162+
items:
163+
- id: CQ-01
164+
name: KISS Structure
165+
score: 3
166+
max: 3
167+
passed: true
168+
comment: 'Simple linear structure: imports → data → plot → save'
169+
- id: CQ-02
170+
name: Reproducibility
171+
score: 3
172+
max: 3
173+
passed: true
174+
comment: Uses np.random.seed(42)
175+
- id: CQ-03
176+
name: Clean Imports
177+
score: 2
178+
max: 2
179+
passed: true
180+
comment: Only necessary imports (numpy, pygal, Style)
181+
- id: CQ-04
182+
name: No Deprecated API
183+
score: 1
184+
max: 1
185+
passed: true
186+
comment: Uses current pygal API
187+
- id: CQ-05
188+
name: Output Correct
189+
score: 0
190+
max: 1
191+
passed: false
192+
comment: Legend truncation indicates a rendering issue
193+
library_features:
194+
score: 3
195+
max: 5
196+
items:
197+
- id: LF-01
198+
name: Distinctive Features
199+
score: 3
200+
max: 5
201+
passed: true
202+
comment: Uses pygal XY chart and custom Style, but legend truncation suggests
203+
suboptimal configuration
204+
verdict: APPROVED
205+
impl_tags:
206+
dependencies: []
207+
techniques:
208+
- patches
209+
- html-export
210+
patterns:
211+
- data-generation
212+
- iteration-over-groups
213+
dataprep:
214+
- normalization
215+
styling:
216+
- grid-styling

0 commit comments

Comments
 (0)