Skip to content

Instantly share code, notes, and snippets.

@0x9900
Last active March 12, 2024 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0x9900/6e2279367e591122cc92d7406b9274b0 to your computer and use it in GitHub Desktop.
Save 0x9900/6e2279367e591122cc92d7406b9274b0 to your computer and use it in GitHub Desktop.
Graph SWR smith chart and return loss
#!/usr/bin/env python3.10
#
#
import argparse
import os
import re
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
import skrf as rf
plt.style.use("dark_background")
__version__ = "0.0.1"
DPI = 120
LINE_COLOR = "yellow"
SWR_COLOR = "cadetblue"
METADATA = {
"Author": "Fred W6BSD",
"Software": "RigExpert.py",
"Version": __version__,
}
my_params = {
"axes.grid": True,
"axes.grid.which": "both",
"axes.labelsize": 14,
"axes.linewidth": 1.5,
"axes.spines.bottom": True,
"axes.spines.left": True,
"axes.spines.right": False,
"axes.spines.top": False,
"figure.dpi": DPI,
"grid.color": "cyan",
"grid.linewidth": .33,
"legend.fontsize": 14,
"lines.linewidth": 1.5,
"xtick.labelsize": 12,
"ytick.labelsize": 12,
}
plt.rcParams.update(my_params)
BANDS = (
(14000, 14350, '20m'),
(7000, 7300, '40m'),
(10100, 10150, '30m'),
(3500, 4000, '80m'),
(21000, 21450, '15m'),
(18068, 18168, '17m'),
(28000, 29700, '10m'),
(50000, 54000, '6m'),
(24890, 24990, '12m'),
(1800, 2000, '160m'),
(144000, 148000, '2m'),
(5258, 5450, '60m'),
(420000, 450000, '0.70m'),
(219000, 225000, '1.25m'),
(1240000, 1300000, '0.23m'),
(10000000, 10500000, '0.02m'),
)
def slugify(text):
return re.sub(r'[\W_]+', '-', text.lower())
class DrawAll:
def __init__(self, filename, title):
self.filename = filename
self.title = title
self.dut = rf.Network(filename)
self.dut.frequency.unit = 'MHz'
fig, self.axs = plt.subplots(2, 2, figsize=(16,14))
fig.suptitle(title)
def smith(self):
self.dut.s11.plot_s_smith(ax=self.axs[0,0], show_legend=True, color=LINE_COLOR,
label="Complex Impedence")
def vswr(self):
self.dut.s11.plot_s_vswr(ax=self.axs[0,1], color=LINE_COLOR, label='VSWR')
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.axs[0,1].set_xlim(fmin, fmax)
max_vswr = (1+self.dut.s_mag.max())/(1-self.dut.s_mag.max())
self.axs[0,1].set_ylim(1, 3 if max_vswr < 3 else max_vswr * 1.2 if max_vswr < 30 else 30)
self.axs[0,1].axhline(y=2, linewidth=1.25, zorder=9, color=SWR_COLOR, linestyle="-.")
self.axs[0,1].axhline(y=3, linewidth=1.25, zorder=9, color=SWR_COLOR, linestyle="-.")
for low, high, _l in BANDS:
self.axs[0,1].axvspan(low*1000, high*1000, facecolor='cyan', alpha=0.15)
def return_loss(self):
self.dut.s11.plot_s_db(ax=self.axs[1,0], color=LINE_COLOR, label="Return Loss")
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.axs[1,0].set_xlim(fmin, fmax)
self.axs[1,0].set_ylim(top=0)
self.axs[1,0].axhline(y=-6, linewidth=1.5, zorder=9, color=SWR_COLOR, linestyle="-.")
self.axs[1,0].axhline(y=-10, linewidth=1.5, zorder=9, color=SWR_COLOR, linestyle="-.")
for low, high, label in BANDS:
self.axs[1,0].axvspan(low*1000, high*1000, facecolor='cyan', alpha=0.15)
def phase(self):
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.dut.s11.plot_s_deg(ax=self.axs[1,1], color=LINE_COLOR, label="Phase")
self.axs[1,1].set_xlim(fmin, fmax)
for low, high, _ in BANDS:
self.axs[1,1].axvspan(low*1000, high*1000, facecolor='lightgray', alpha=0.15)
def graph(self):
self.smith()
self.vswr()
self.return_loss()
self.phase()
dname, fname = os.path.split(self.filename)
fname, _ = os.path.splitext(fname)
image_name = os.path.join(dname, f'{slugify(self.title)}-{fname}-all.png')
print(image_name)
METADATA["Title"] = self.title
METADATA["Creation Time"] = datetime.now().isoformat()
plt.savefig(image_name, transparent=False, metadata=METADATA)
class Draw:
def __init__(self, filename, title):
self.filename = filename
self.title = title
self.dut = rf.Network(filename)
self.dut.frequency.unit = 'MHz'
fig = plt.figure(figsize=(10, 6))
self.ax = fig.gca()
fig.suptitle(title)
self.dname, fname = os.path.split(self.filename)
self.fname, _ = os.path.splitext(fname)
def smith(self):
self.dut.s11.plot_s_smith(ax=self.ax, show_legend=True, color=LINE_COLOR,
label='Smith Chart')
image_name = os.path.join(self.dname, f'{slugify(self.title)}-{self.fname}-smith.png')
print(image_name)
METADATA["Title"] = self.title
METADATA["Creation Time"] = datetime.now().isoformat()
plt.savefig(image_name, transparent=False, metadata=METADATA)
def vswr(self):
self.dut.s11.plot_s_vswr(ax=self.ax, color=LINE_COLOR, label='VSWR')
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.ax.set_xlim(fmin, fmax)
max_vswr = (1+self.dut.s_mag.max())/(1-self.dut.s_mag.max())
self.ax.set_ylim(1, 3 if max_vswr < 3 else max_vswr * 1.2 if max_vswr < 30 else 30)
self.ax.axhline(y=2, linewidth=1.25, zorder=9, color=SWR_COLOR, linestyle="-.")
self.ax.axhline(y=3, linewidth=1.25, zorder=9, color=SWR_COLOR, linestyle="-.")
for low, high, _l in BANDS:
self.ax.axvspan(low*1000, high*1000, facecolor='cyan', alpha=0.15)
image_name = os.path.join(self.dname, f'{slugify(self.title)}-{self.fname}-vswr.png')
print(image_name)
METADATA["Title"] = self.title
METADATA["Creation Time"] = datetime.now().isoformat()
plt.savefig(image_name, transparent=False, metadata=METADATA)
def return_loss(self):
self.dut.s11.plot_s_db(ax=self.ax, color=LINE_COLOR, label="Return Loss")
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.ax.set_xlim(fmin, fmax)
self.ax.set_ylim(top=0)
self.ax.axhline(y=-6, linewidth=1.5, zorder=9, color=SWR_COLOR, linestyle="-.")
self.ax.axhline(y=-10, linewidth=1.5, zorder=9, color=SWR_COLOR, linestyle="-.")
for low, high, label in BANDS:
self.ax.axvspan(low*1000, high*1000, facecolor='cyan', alpha=0.15)
image_name = os.path.join(self.dname, f'{slugify(self.title)}-{self.fname}-rloss.png')
print(image_name)
METADATA["Title"] = self.title
METADATA["Creation Time"] = datetime.now().isoformat()
plt.savefig(image_name, transparent=False, metadata=METADATA)
def phase(self):
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.dut.s11.plot_s_deg(ax=self.ax, color=LINE_COLOR, label="Phase")
self.ax.set_xlim(fmin, fmax)
for low, high, _ in BANDS:
self.ax.axvspan(low*1000, high*1000, facecolor='lightgray', alpha=0.15)
image_name = os.path.join(self.dname, f'{slugify(self.title)}-{self.fname}-phase.png')
print(image_name)
METADATA["Title"] = self.title
METADATA["Creation Time"] = datetime.now().isoformat()
plt.savefig(image_name, transparent=False, metadata=METADATA)
def impedance(self):
fmin = self.dut.frequency.f.min()
fmax = self.dut.frequency.f.max()
self.dut.s11.plot_z_re(ax=self.ax, color=LINE_COLOR, label="| Z |")
self.ax.set_xlim(fmin, fmax)
for low, high, _ in BANDS:
self.ax.axvspan(low*1000, high*1000, facecolor='lightgray', alpha=0.15)
image_name = os.path.join(self.dname, f'{slugify(self.title)}-{self.fname}-impedance.png')
print(image_name)
METADATA["Title"] = self.title
METADATA["Creation Time"] = datetime.now().isoformat()
plt.savefig(image_name, transparent=False, metadata=METADATA)
def main():
parser = argparse.ArgumentParser(description="RigExpert")
parser.add_argument('-f', '--s1p-file', required=True)
parser.add_argument('-t', '--title', default='RigExpert')
d_group = parser.add_mutually_exclusive_group(required=True)
d_group.add_argument('-a', '--all', action="store_true", default=False)
d_group.add_argument('-s', '--vswr', action='store_true', default=False)
d_group.add_argument('-p', '--phase', action='store_true', default=False)
d_group.add_argument('-l', '--rloss', action='store_true', default=False)
d_group.add_argument('-w', '--smith', action='store_true', default=False)
d_group.add_argument('-z', '--impedance', action='store_true', default=False)
opts = parser.parse_args()
if opts.all:
draw = DrawAll(opts.s1p_file, opts.title)
draw.graph()
return
draw = Draw(opts.s1p_file, opts.title)
if opts.vswr:
draw.vswr()
elif opts.phase:
draw.phase()
elif opts.rloss:
draw.return_loss()
elif opts.smith:
draw.smith()
elif opts.impedance:
draw.impedance()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment