Skip to content

Instantly share code, notes, and snippets.

@rezamarzban
Last active December 11, 2025 11:32
Show Gist options
  • Select an option

  • Save rezamarzban/379337326ae86d0d0d06b26c987606db to your computer and use it in GitHub Desktop.

Select an option

Save rezamarzban/379337326ae86d0d0d06b26c987606db to your computer and use it in GitHub Desktop.
Local vswr
import cmath
import math
# Define the chain as a simple list of tuples: ('type', value, 'unit')
# Types: 'R' for resistor, 'L' for inductor, 'C' for capacitor, 'Source' for AC Source
# Units for R/Source: 'ohm', 'kohm', 'mohm'
# Units for L: 'nH', 'uH', 'mH', 'H'
# Units for C: 'fF', 'pF', 'nF', 'uF', 'F'
# Example (AC Source with 0 impedance included as first element):
chain = [
('Source', 0, 'ohm'), # AC Source with no impedance
('R', 100, 'ohm'), # 100 ohms
('R', 50, 'ohm'), # 50 ohms
('L', 1, 'uH'), # 1 uH
('C', 100, 'pF'), # 100 pF
('R', 33, 'ohm'), # 33 ohms
# Add more: ('L', 2.5, 'mH'), ('C', 0.1, 'uF'), etc.
]
# Get scale factor for units
def get_scale(unit, comp_type):
unit = unit.lower()
if comp_type in ['R', 'Source']:
scales = {'ohm': 1, 'kohm': 1e3, 'mohm': 1e6}
elif comp_type == 'L':
scales = {'nh': 1e-9, 'uh': 1e-6, 'mh': 1e-3, 'h': 1}
elif comp_type == 'C':
scales = {'ff': 1e-15, 'pf': 1e-12, 'nf': 1e-9, 'uf': 1e-6, 'f': 1}
return scales.get(unit, 1) # Default 1 if unknown
# Component impedance calculator
def get_impedance(comp_type, value, unit, omega):
scale = get_scale(unit, comp_type)
base_value = value * scale
if comp_type in ['R', 'Source']:
return base_value + 0j
elif comp_type == 'L':
return 1j * omega * base_value
elif comp_type == 'C':
if omega * base_value == 0:
return 0j
return -1j / (omega * base_value)
else:
raise ValueError("Unknown type: use 'R', 'L', 'C', or 'Source'")
# Z_right (toward GND) at each node
def compute_z_right(chain, omega):
z_right = [0j] * (len(chain) + 1)
z = 0j
z_right[len(chain)] = z
for i in range(len(chain) - 1, -1, -1):
comp_type, value, unit = chain[i]
z_comp = get_impedance(comp_type, value, unit, omega)
z = z_comp + z
z_right[i] = z
return z_right
# Z_left (toward source) at each node
def compute_z_left(chain, omega):
z_left = [0j] * (len(chain) + 1)
z = 0j # Start with 0 since source is now in chain
z_left[0] = z
for i in range(len(chain)):
comp_type, value, unit = chain[i]
z_comp = get_impedance(comp_type, value, unit, omega)
z = z + z_comp
z_left[i + 1] = z
return z_left
# Reflection coefficient Gamma
def compute_gamma(z_left, z_right):
if z_left + z_right == 0:
return 0
return (z_right - z_left) / (z_right + z_left)
# VSWR from |Gamma|
def compute_vswr(gamma):
mag_gamma = abs(gamma)
if mag_gamma >= 1:
return float('inf')
return (1 + mag_gamma) / (1 - mag_gamma)
# Get frequency scale
def get_freq_scale(unit):
unit = unit.lower()
scales = {'hz': 1, 'khz': 1e3, 'mhz': 1e6, 'ghz': 1e9}
return scales.get(unit, 1e6) # Default MHz
# Print used equations
def print_equations():
print("\nUsed Equations (in LaTeX format):")
print(r"\omega = 2 \pi f")
print(r"Z_R = R")
print(r"Z_L = j \omega L")
print(r"Z_C = -j / (\omega C)")
print(r"Z_{series} = Z_1 + Z_2 + \dots")
print(r"\Gamma = \frac{Z_{right} - Z_{left}}{Z_{right} + Z_{left}}")
print(r"VSWR = \frac{1 + |\Gamma|}{1 - |\Gamma|}")
# Main calculator function
def calculate_local_vswr(node_index, freq_value, freq_unit='MHz'):
if node_index < 0 or node_index > len(chain):
raise ValueError(f"Node must be 0 to {len(chain)}")
freq_scale = get_freq_scale(freq_unit)
f = freq_value * freq_scale
omega = 2 * math.pi * f
z_right_list = compute_z_right(chain, omega)
z_left_list = compute_z_left(chain, omega)
z_l = z_left_list[node_index]
z_r = z_right_list[node_index]
gamma = compute_gamma(z_l, z_r)
vswr = compute_vswr(gamma)
print(f"\nResults for Node {node_index} at {freq_value} {freq_unit}:")
print(f"Z_left: {z_l.real:.2f} {'+' if z_l.imag >= 0 else ''}{z_l.imag:.2f}j Ω")
print(f"Z_right: {z_r.real:.2f} {'+' if z_r.imag >= 0 else ''}{z_r.imag:.2f}j Ω")
print(f"|Γ|: {abs(gamma):.3f}")
print(f"VSWR: {vswr:.2f}")
print_equations()
# Edit these to run:
node = 2 # Node index (0 is before first component after source, up to len(chain))
freq_value = 10.0 # Frequency value
freq_unit = 'MHz' # Can be 'Hz', 'kHz', 'MHz', 'GHz'
calculate_local_vswr(node, freq_value, freq_unit)
import cmath
import math
# Define the chain as a simple list of tuples or Parallel branches:
# Single components: ('Type', value, 'unit') where Type in {'R','L','C','Source'}
# Parallel branch: ('Parallel', [ element1, element2, ... ])
# Each element in a Parallel list can be:
# - a single component tuple ('R', 10, 'ohm')
# - a list of component tuples representing series components [ ('R',10,'ohm'), ('C',1,'pF') ]
chain = [
('Source', 0, 'ohm'), # AC Source with no impedance
('R', 100, 'ohm'), # 100 ohms
('Parallel', [ # Parallel branch with multiple series groups and single components
('L', 1, 'uH'),
[('R', 10, 'ohm'), ('C', 100, 'pF')], # series group: R in series with C
('C', 200, 'pF'),
[('L', 2, 'uH'), ('R', 5, 'ohm'), ('C', 50, 'pF')] # longer series group
]),
('R', 50, 'ohm'), # 50 ohms
('R', 33, 'ohm'), # 33 ohms
]
# Get scale factor for units
def get_scale(unit, comp_type):
if unit is None:
return 1
unit = unit.lower()
if comp_type in ['R', 'Source']:
scales = {'ohm': 1, 'kohm': 1e3, 'mohm': 1e6}
elif comp_type == 'L':
scales = {'nh': 1e-9, 'uh': 1e-6, 'mh': 1e-3, 'h': 1}
elif comp_type == 'C':
scales = {'ff': 1e-15, 'pf': 1e-12, 'nf': 1e-9, 'uf': 1e-6, 'f': 1}
else:
scales = {}
return scales.get(unit, 1) # Default 1 if unknown
# Compute impedance of a series group (list of component tuples)
def get_series_impedance(series_list, omega):
z_total = 0+0j
for comp in series_list:
if len(comp) != 3:
raise ValueError("Series group components must be tuples of (Type, value, unit)")
c_type, c_value, c_unit = comp
z_comp = get_impedance(c_type, c_value, c_unit, omega)
# If any series element is infinite (open), the series total is infinite
if z_comp == complex(float('inf'), 0):
return complex(float('inf'), 0)
z_total += z_comp
return z_total
# Compute impedance of a parallel branch (branch is a list of elements)
# Each element can be a tuple (single component) or a list (series group)
def get_parallel_impedance(branch, omega):
admittance = 0+0j
for elem in branch:
# element is a series group (list)
if isinstance(elem, list):
z_comp = get_series_impedance(elem, omega)
# element is a single component tuple
elif isinstance(elem, tuple):
if len(elem) != 3:
raise ValueError("Parallel branch component tuples must be (Type, value, unit)")
c_type, c_value, c_unit = elem
z_comp = get_impedance(c_type, c_value, c_unit, omega)
else:
raise ValueError("Parallel branch elements must be tuples or lists of tuples")
# If element is open (inf), it contributes 0 admittance
if z_comp == complex(float('inf'), 0):
continue
# If element is short (0), total parallel impedance is 0
if z_comp == 0:
return 0+0j
admittance += 1 / z_comp
if admittance == 0:
return complex(float('inf'), 0) # open circuit
return 1 / admittance
# Component impedance calculator (handles Parallel specially)
def get_impedance(comp_type, value, unit, omega):
if comp_type == 'Parallel':
# value is expected to be the branch list
return get_parallel_impedance(value, omega)
scale = get_scale(unit, comp_type)
base_value = value * scale
if comp_type in ['R', 'Source']:
return complex(base_value, 0)
elif comp_type == 'L':
return 1j * omega * base_value
elif comp_type == 'C':
# If C == 0 or omega == 0, treat as open circuit (infinite impedance)
if base_value == 0 or omega == 0:
return complex(float('inf'), 0)
return complex(0, -1 / (omega * base_value))
else:
raise ValueError("Unknown type: use 'R', 'L', 'C', 'Source', or 'Parallel'")
# Z_right (toward GND) at each node
def compute_z_right(chain, omega):
z_right = [0j] * (len(chain) + 1)
z = 0j
z_right[len(chain)] = z
for i in range(len(chain) - 1, -1, -1):
comp = chain[i]
comp_type = comp[0]
if comp_type == 'Parallel':
z_comp = get_impedance('Parallel', comp[1], None, omega)
else:
if len(comp) != 3:
raise ValueError(f"Component at index {i} must be ('Type', value, 'unit')")
_, value, unit = comp
z_comp = get_impedance(comp_type, value, unit, omega)
# handle infinities gracefully
if z == complex(float('inf'), 0) or z_comp == complex(float('inf'), 0):
# if either is infinite, series sum is infinite
z = complex(float('inf'), 0)
else:
z = z_comp + z
z_right[i] = z
return z_right
# Z_left (toward source) at each node
def compute_z_left(chain, omega):
z_left = [0j] * (len(chain) + 1)
z = 0j # Start with 0 since source is now in chain
z_left[0] = z
for i in range(len(chain)):
comp = chain[i]
comp_type = comp[0]
if comp_type == 'Parallel':
z_comp = get_impedance('Parallel', comp[1], None, omega)
else:
if len(comp) != 3:
raise ValueError(f"Component at index {i} must be ('Type', value, 'unit')")
_, value, unit = comp
z_comp = get_impedance(comp_type, value, unit, omega)
if z == complex(float('inf'), 0) or z_comp == complex(float('inf'), 0):
z = complex(float('inf'), 0)
else:
z = z + z_comp
z_left[i + 1] = z
return z_left
# Reflection coefficient Gamma
def compute_gamma(z_left, z_right):
# Handle infinite impedances: if both infinite, treat as matched (Gamma=0)
if (z_left == complex(float('inf'), 0)) and (z_right == complex(float('inf'), 0)):
return 0+0j
denom = z_right + z_left
if denom == 0:
return 0+0j
return (z_right - z_left) / denom
# VSWR from |Gamma|
def compute_vswr(gamma):
mag_gamma = abs(gamma)
if mag_gamma >= 1:
return float('inf')
return (1 + mag_gamma) / (1 - mag_gamma)
# Get frequency scale
def get_freq_scale(unit):
if unit is None:
return 1e6
unit = unit.lower()
scales = {'hz': 1, 'khz': 1e3, 'mhz': 1e6, 'ghz': 1e9}
return scales.get(unit, 1e6) # Default MHz
# Print used equations in plain math expression format (not LaTeX)
def print_equations():
print("\nUsed Equations (math expression format):")
print("omega = 2 * pi * f")
print("Z_R = R")
print("Z_L = j * omega * L")
print("Z_C = -j / (omega * C)")
print("Z_series = Z1 + Z2 + ...")
print("Z_parallel = 1 / (1/Z1 + 1/Z2 + ...)")
print("Gamma = (Z_right - Z_left) / (Z_right + Z_left)")
print("VSWR = (1 + |Gamma|) / (1 - |Gamma|)")
# Print node map with support for series groups inside parallel branches
def print_node_map(chain):
print("\nNode Map (index : components):")
for i, comp in enumerate(chain):
comp_type = comp[0]
if comp_type == 'Parallel':
branch = comp[1]
elem_descs = []
for elem in branch:
if isinstance(elem, list):
# series group
parts = []
for c in elem:
parts.append(f"{c[0]} {c[1]} {c[2]}")
elem_descs.append("(" + " + ".join(parts) + ")")
else:
# single component tuple
elem_descs.append(f"{elem[0]} {elem[1]} {elem[2]}")
branch_desc = " || ".join(elem_descs)
print(f"Node {i}: Parallel[{branch_desc}]")
else:
if len(comp) == 3:
print(f"Node {i}: {comp[0]} {comp[1]} {comp[2]}")
else:
print(f"Node {i}: {comp}")
# Calculate and print local VSWR for all nodes
def calculate_all_vswr(freq_value, freq_unit='MHz'):
print_node_map(chain)
freq_scale = get_freq_scale(freq_unit)
f = freq_value * freq_scale
omega = 2 * math.pi * f
z_right_list = compute_z_right(chain, omega)
z_left_list = compute_z_left(chain, omega)
def fmt_z(z):
if z == complex(float('inf'), 0):
return "inf"
return f"{z.real:.6g} {'+' if z.imag >= 0 else ''}{z.imag:.6g}j"
print(f"\nCalculations at {freq_value} {freq_unit} (omega = {omega:.6g} rad/s):")
for node_index in range(0, len(chain) + 1):
z_l = z_left_list[node_index]
z_r = z_right_list[node_index]
gamma = compute_gamma(z_l, z_r)
vswr = compute_vswr(gamma)
print(f"\nNode {node_index}:")
print(f" Z_left : {fmt_z(z_l)} Ω")
print(f" Z_right: {fmt_z(z_r)} Ω")
print(f" |Gamma|: {abs(gamma):.6f}")
if vswr == float('inf'):
print(" VSWR : inf")
else:
print(f" VSWR : {vswr:.6f}")
print_equations()
# Edit these to run:
freq_value = 10.0 # Frequency value
freq_unit = 'MHz' # Can be 'Hz', 'kHz', 'MHz', 'GHz'
if __name__ == "__main__":
calculate_all_vswr(freq_value, freq_unit)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment