Skip to content

Commit 1abf743

Browse files
committed
Fix some bugs, merge splitminor and splitgrid, use tooltip boundingbox for deleting markers, save markers data on a dictionary. Add Constant gain and constant noise circles. Add Stability regions.
1 parent b857712 commit 1abf743

15 files changed

+3522
-52
lines changed

Images/ConstantCircles.svg

Lines changed: 1161 additions & 0 deletions
Loading

Images/NFCircles.png

183 KB
Loading

Images/NGCircles.png

124 KB
Loading

Images/StableRegionsLines.svg

Lines changed: 729 additions & 0 deletions
Loading

Images/bothregions.png

210 KB
Loading

Images/bothregions.svg

Lines changed: 1268 additions & 0 deletions
Loading

Images/smithplot_color.png

-104 KB
Binary file not shown.

Images/typeandcolor.png

-382 KB
Binary file not shown.

README.md

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,125 @@ Interactive data markers can be added to your Smith chart using the `datamarkers
124124

125125
![datamarkergif](Images/datamarkers.gif)
126126

127+
128+
## Stability, Gain and Noise Circles
129+
130+
SmithChart.jl allows visualization of constant gain circles, constant noise circles, and stability regions, essential for amplifier design and stability analysis. These features can be useful for tasks such as designing low-noise amplifiers (LNAs), power amplifiers, and ensuring the stability of circuits over a range of frequencies and impedances.
131+
132+
This example shows how to use the `NFCircle` and `CGCircle`. This functions returns an array of `Point2f` that can be used with Makie functions like `lines!` or `poly!`.
133+
134+
```julia
135+
using CairoMakie
136+
f = Figure(size = (1200, 660))
137+
sc = SmithAxis(f[1,1], cutgrid = true, title = "Constant NF Circles")
138+
139+
NF_to_F(nf) = 10.0^(nf/10.0)
140+
Γopt = 0.5 * cis(130 * pi / 180)
141+
NFmin = 1.6 # dB
142+
Fmin = NF_to_F(NFmin)
143+
F2dB = NF_to_F(2.0)
144+
F2_5dB = NF_to_F(2.5)
145+
F3dB = NF_to_F(3.0)
146+
nf2 = NFCircle(F2dB, Fmin, Γopt, 20.0, 50.0, 361)
147+
nf2_5 = NFCircle(F2_5dB, Fmin, Γopt, 20.0, 50.0, 361)
148+
nf3 = NFCircle(F3dB, Fmin, Γopt, 20.0, 50.0, 361)
149+
150+
smithscatter!(sc, [Γopt], reflection = true, color = :blue, linewidth = 1.9)
151+
text!(sc, "Γopt", position = Point2f(real(Γopt), imag(Γopt)), offset = (3, 3), color = :blue, font = :bold)
152+
text!(sc, "$NFmin dB", position = Point2f(real(Γopt), imag(Γopt)), offset = (0, -16), color = :blue, font = :bold)
153+
154+
lines!(sc, nf2, color = :green, linewidth = 1.9)
155+
text!(sc, "2.0 dB", position = nf2[45], offset = (2, 0), color = :green, font = :bold)
156+
157+
lines!(sc, nf2_5, color = :purple, linewidth = 1.9)
158+
text!(sc, "2.5 dB", position = nf2_5[120], offset = (-35, 2), color = :purple, font = :bold)
159+
160+
lines!(sc, nf3, color = :orange, linewidth = 1.9)
161+
text!(sc, "3.0 dB", position = nf3[260], offset = (2, 2), color = :orange, font = :bold)
162+
163+
# https://www.allaboutcircuits.com/technical-articles/designing-a-unilateral-rf-amplifier-for-a-specified-gain/
164+
sc = SmithAxis(f[1,2], cutgrid = true, title = "Constant Gs Circles")
165+
166+
S11 = 0.533 * cis(176.6 / 180 * π)
167+
S22 = 0.604 * cis(-58.0 / 180 * π)
168+
Go = abs2(S11)
169+
Gs_max = 1 / (1 - abs2(S11))
170+
gain(dB) = 10.0^(dB/10.0)
171+
172+
g1 = gain(0.0) / Gs_max
173+
g2 = gain(0.5) / Gs_max
174+
g3 = gain(1.0) / Gs_max
175+
g4 = gain(1.4) / Gs_max
176+
177+
c1 = CGCircle(g1, S11, 361)
178+
c2 = CGCircle(g2, S11, 361)
179+
c3 = CGCircle(g3, S11, 361)
180+
c4 = CGCircle(g4, S11, 361)
181+
182+
smithscatter!(sc, [conj(S11)], reflection = true, color = :blue, linewidth = 1.9)
183+
text!(sc, "S11*", position = Point2f(real(S11), -imag(S11)),
184+
offset = (-5, 7), color = :blue, font = :bold, fontsize = 9)
185+
186+
poly!(sc, c1, color = (:green, 0.1), strokecolor = :green, strokewidth = 1.9)
187+
text!(sc, "0.0 dB", position = c1[125], offset = (17, 0), color = :green, font = :bold)
188+
189+
lines!(sc, c2, color = :red, linewidth = 1.9)
190+
text!(sc, "0.5 dB", position = c2[110], offset = (-27, 3), color = :red, font = :bold)
191+
192+
lines!(sc, c3, color = :magenta, linewidth = 1.9)
193+
text!(sc, "1.0 dB", position = c3[260], offset = (2, 0), color = :magenta, font = :bold)
194+
195+
lines!(sc, c4, color = :purple, linewidth = 1.9)
196+
text!(sc, "1.4 dB", position = c3[45], offset = (2, 2), color = :purple, font = :bold)
197+
```
198+
199+
![Ccircles](Images/ConstantCircles.svg)
200+
201+
When calculating the stability regions, you can select whether you want to display the stable or unstable region. To do this, modify the keyword `stable` in the `StabilityCircle` function.
202+
203+
```julia
204+
using CairoMakie
205+
f = Figure(size = (800, 500))
206+
Label(f[0, 1:2] , "Stable Regions", fontsize = 24, font = :bold)
207+
S11, S12, S21, S22 = [0.438868-0.778865im 1.4+0.2im; 0.1+0.43im 0.692125-0.361834im]
208+
A = StabilityCircle(S11, S12, S21, S22, :source, 361; stable = false)
209+
B = StabilityCircle(S11, S12, S21, S22, :source, 361; stable = true)
210+
211+
ax = SmithAxis(f[1,1], subtitle = "StabilityCircle(... ; stable = false)")
212+
region = poly!(ax, A, strokecolor = :black, strokewidth = 1.2, color = Pattern('x', linecolor = :red, width = 1.3, background_color = (:red, 0.1)))
213+
translate!(region, (0, 0, -2))
214+
text!(ax, "Unstable Input Region", position = Point2f(-0.1, -0.5), font = :bold, color = :red, fontsize = 13)
215+
B = StabilityCircle(S11, S12, S21, S22, :source, 361; stable = true);
216+
ax = SmithAxis(f[1,2], subtitle = "StabilityCircle(... ; stable = true)")
217+
region = poly!(ax, B, strokecolor = :black, strokewidth = 1.2, color = Pattern('\\', linecolor = :blue, width = 1.3, background_color = (:blue, 0.1)))
218+
translate!(region, (0, 0, -2))
219+
text!(ax, "Stable Input region", position = Point2f(-0.5, -0.5), font = :bold, color = :blue, fontsize = 13)
220+
```
221+
![stabilityregions1](Images/stableregions.svg)
222+
223+
It is possible to obtain the input or output stability regions with `StabilityCircle(S11, S12, S21, S22, :source, npoints)` or `StabilityCircle(S11, S12, S21, S22, :load, npoints)`.
224+
225+
```julia
226+
f = Figure()
227+
S11, S12, S21, S22 = [ 0.0967927-0.604297im 0.0255292+0.0394621im ; -8.4396+11.0786im 0.552226-0.425271im]
228+
A = StabilityCircle(S11, S12, S21, S22, :source, 361; stable = true);
229+
B = StabilityCircle(S11, S12, S21, S22, :load, 361; stable = true);
230+
ax = SmithAxis(f[1,1], title = "Input and Output stability regions")
231+
color1 = Pattern('/', linecolor = :blue, width = 1.3, background_color = :transparent)
232+
color2 = Pattern('\\', linecolor = :green, width = 1.3, background_color = :transparent)
233+
region = poly!(ax, A, strokecolor = :black, strokewidth = 1.2, color = color1)
234+
translate!(region, (0, 0, -2))
235+
region = poly!(ax, B, strokecolor = :black, strokewidth = 1.2, color = color2)
236+
translate!(region, (0, 0, -2))
237+
text!(ax, "Stable Input", position = Point2f(-0.85, 0.2), font = :bold, color = :blue, rotation = 25*pi/180, fontsize = 12)
238+
text!(ax, "Stable Output", position = Point2f(0.3, 0.5), font = :bold, color = :green, rotation = -18*pi/180, fontsize = 12)
239+
```
240+
241+
![stabilityregions2](Images/StableRegionsLines.svg)
242+
243+
244+
![ZoomGif](Images/SmithChart_zoom.gif)
245+
127246
## Dynamic Annotation Update
128247

129248
You can activate a experimental dynamic curve annotation with the keyword `textupdate = true`
@@ -133,8 +252,6 @@ fig = Figure(size = (800,600))
133252
ax = SmithAxis(fig[1, 1]; subgrid = true, cutgrid = true, zoomupdate = true, textupdate = true, threshold = (150, 150))
134253
```
135254

136-
![ZoomGif](Images/SmithChart_zoom.gif)
137-
138255
## Other Keywords
139256

140257
How some keywords modify visual aspects of the Smith Chart.
@@ -159,6 +276,6 @@ There are multiple keywords to modify the position of the ticks. Some of them ar
159276

160277
### Subgrid split
161278

162-
The `splitminor` keyword controls the number of cuts of a space between ticks when there is no zoom. See also `splitgrid` to control the number of cuts while zooming.
279+
The `splitgrid` keyword controls the number of cuts for each zoomlevel. The value should be a
163280

164281
![keywordexample](Images/splitminor.png)

src/Functions.jl

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,119 @@ function rectangular_shape(s = 1e5)
170170
end
171171

172172

173+
174+
"""
175+
NFCircle(F, Fmin, Γopt, Rn, Zo)
176+
177+
Computes the points of the constant Noise Figure Circle.
178+
179+
- F: noise factor
180+
- Fmin: minimum noise factor
181+
- Γopt: optimimum source reflection coefficient related to Zopt or Yopt.
182+
- Rn: noise resistance parameter
183+
- Zo: Reference Impedance
184+
- Np: Number of points
185+
"""
186+
function NFCircle(F, Fmin, Γopt, Rn, Zo, Np)
187+
N = (F - Fmin)/(4*Rn/Zo) * abs2(1 + Γopt)
188+
center = Γopt / (N + 1)
189+
rad = sqrt(N*(N+1-abs2(Γopt)))/(N+1)
190+
x, y = real(center), imag(center)
191+
return [Point2f(rad * cos(th) + x, rad * sin(th) + y) for th in range(-π, π, Np)]
192+
end
193+
194+
"""
195+
CGCircle(gi, Sii)
196+
197+
Computes the points of the Constant Gain Circle.
198+
199+
- gi: It's gsource or gload and it's value is G / Gmax
200+
- Sii: Reflection S parameter. S11 for Gs and S22 for Gl.
201+
- Np: Number of points
202+
203+
"""
204+
function CGCircle(gi, Sii, Np)
205+
center = (gi * conj(Sii)) / (1 - abs2(Sii)*(1 - gi))
206+
rad = (sqrt(1-gi) * (1 - abs2(Sii))) / (1 - abs2(Sii)*(1 - gi))
207+
x, y = real(center), imag(center)
208+
return [Point2f(rad * cos(th) + x, rad * sin(th) + y) for th in range(-π, π, Np)]
209+
end
210+
211+
# Stability Circunferences
212+
213+
"""
214+
StabilityCircle(S11, S12, S21, S22, inout::Symbol, Np; stable = false)
215+
216+
Computes the region of stability (or unstability) and returns a Makie.Polygon.
217+
218+
- Sii: S-parameter
219+
- inout: a symbol selecting source or load regions. Valid values are :load or :source.
220+
- Np: Number of points.
221+
- stable: Selects if the region corresponds to the stable (true) or unstable (false) region.
222+
"""
223+
function StabilityCircle(S11, S12, S21, S22, inout::Symbol, Np; stable = false)
224+
if inout == :load
225+
S11, S22 = S22, S11
226+
end
227+
Δ = S11 * S22 - S12 * S21
228+
center = conj(S22 - Δ*conj(S11)) / (abs2(S22) - abs2(Δ))
229+
rad = abs(S12 * S21) / (abs2(S22) - abs2(Δ))
230+
x, y = real(center), imag(center)
231+
points = [Point2f(rad * cos(th) + x, rad * sin(th) + y) for th in range(-π, π, Np)]
232+
circshape = circular_shape(Np)
233+
# Check if:
234+
if sqrt(sum(abs2, center)) + rad <= 1 # Stability Circle INSIDE Smith Chart
235+
if abs(S11) < 1
236+
outpolygon = stable == true ? Makie.Polygon(circshape, [points]) : Makie.Polygon(points)
237+
else
238+
outpolygon = stable == true ? Makie.Polygon(points) : Makie.Polygon(circshape, [points])
239+
end
240+
elseif sqrt(sum(abs2, center)) + 1 <= rad # Smith Chart INSIDE Stability Circle (Unconditionally Stable)
241+
outpolygon = stable == true ? Makie.Polygon(circshape) : Makie.Polygon([Point2f(NaN) for _ in 1:3])
242+
elseif sqrt(sum(abs2, center)) - rad > 1 # Stability Circle OUTSIDE Smith Chart (Unconditionally Stable)
243+
outpolygon = stable == true ? Makie.Polygon(circshape) : Makie.Polygon([Point2f(NaN) for _ in 1:3])
244+
else
245+
Cr = real(center)
246+
Ci = imag(center)
247+
#Γi = (k - (Cr * Γr)) / Ci
248+
#Γr^2 + (Γi)^2 = 1
249+
#Γr^2 + ((k - (Cr * Γr))/Ci)^2 = 1
250+
#Ci^2*Γr^2 + k^2 + Cr^2*Γr^2 - 2*k*Cr*Γr - Ci^2 = 0
251+
#Ci^2 * Γr^2 + Cr*Γr^2 - 2*k*Cr*Γr - (Ci^2 + k^2) = 0
252+
#(Ci^2 + Cr^2) * Γr^2 - 2*k*Cr*Γr + (-Ci^2 + k^2) = 0
253+
k = (Cr^2 + Ci^2 - rad^2 + 1)/2
254+
a = (Ci^2 + Cr^2)
255+
b = -(2*k*Cr)
256+
c = (-Ci^2 + k^2)
257+
v = sqrt(b^2-4*a*c)
258+
Γr = [(-b + v)/(2*a), (-b - v)/(2*a)]
259+
Γi = @. (k - (Cr * Γr)) / Ci
260+
P = Point2f.(Γr, Γi)
261+
v = map(x -> x - Point2f(Cr, Ci), P)
262+
angles_stability = map(x->atan(x[2], x[1]), v)
263+
angles_smith = map(x->atan(x[2], x[1]), P)
264+
# Case 1: |S11| < 1
265+
ids = sortperm(angles_smith)
266+
sort!(angles_smith)
267+
angles_stability = angles_stability[reverse(ids)]
268+
# Case 2: |S11| > 1
269+
if ((abs(S11) > 1) & (stable == false)) | ((abs(S11) < 1) & (stable == true))
270+
reverse!(angles_smith)
271+
reverse!(angles_stability)
272+
angles_smith[end] += 2*pi
273+
end
274+
stability_points = [Point2f(rad * cos(th) + x, rad * sin(th) + y)
275+
for th in range(angles_stability[1], angles_stability[2], Np)]
276+
smith_points = [Point2f(cos(th), sin(th))
277+
for th in range(angles_smith[1], angles_smith[2], Np)]
278+
279+
outpolygon = Makie.Polygon(vcat(smith_points, stability_points))
280+
end
281+
282+
return outpolygon
283+
end
284+
285+
286+
287+
# Forbidden regions
288+

0 commit comments

Comments
 (0)