diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7646668 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*:Zone.Identifier + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..49be80f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +scikit-rf +matplotlib diff --git a/stospice.py b/stospice.py old mode 100644 new mode 100755 index 9759901..3b25211 --- a/stospice.py +++ b/stospice.py @@ -1,47 +1,79 @@ +#!/usr/bin/env python -def stospice(filename, name, f, s, z0): + + +def extract_comments(infilename): + comments = [] + with open(infilename, "r") as ifd: + lines = ifd.read().splitlines() + for line in lines: + line = line.strip() + if not line.startswith("!"): + continue + comments.append(f"* {line[1:]}") + return comments + + +def stospice(filename, name, f, s, z0, comments=None): dim = 100 nports = s.shape[1] if nports > dim - 2: raise SystemExit('too many ports') z0 = z0 if hasattr(z0, '__len__') else [ z0 ] * nports - line = [] - nodes = " ".join(str(i+1) for i in range(nports+1)) - line.append(f'.SUBCKT {name} {nodes}') + lines = [] + ports = list(range(1, nports+1)) + nodes = " ".join(f"{p}" for p in ports) + nodes += " gnd_node" + lines.append(f"*\n*** Example use in SPICE:") + port_names = " ".join(f"Port{p}" for p in ports) + lines.append(f"** X {name} {port_names} GND") + lines.append("") + lines.append(f'.SUBCKT {name} {nodes}') + lines.append(f"Vgnd_node_{nports+1} {nports+1} gnd_node 0") for i in range(nports): - line.append(f"R{i+1}N { (i+1)} {dim*(i+1) } { -z0[i].real}") - line.append(f"R{i+1}P {dim*(i+1)} {dim*(i+1)+1} {2*z0[i].real}") - line.append('') + lines.append(f"R{i+1}N { (i+1)} {dim*(i+1) } { -z0[i].real}") + lines.append(f"R{i+1}P {dim*(i+1)} {dim*(i+1)+1} {2*z0[i].real}") + lines.append('') for i in range(nports): for j in range(nports): index = (i * nports) + j + 1 k = dim * (i + 1) + j + 1 out = nports + 1 if j == nports - 1 else k + 1 - line.append(f'A{k} %vd({dim*(j+1)} {nports+1}) %vd({k}, {out}) xfer{index}') - line.append(f'.model xfer{index} xfer R_I=true table=[') + lines.append(f'A{k} %vd({dim*(j+1)} {nports+1}) %vd({k}, {out}) xfer{index}') + lines.append(f'.model xfer{index} xfer R_I=true table=[') for n in range(len(s)): - line.append(f'+ {f[n]} {s[n,i,j].real} {s[n,i,j].imag}') - line.append('+ ]') - line.append('') - line.append('.ENDS\n') - text = '\n'.join(line) - with open(filename, 'w') as fd: + lines.append(f'+ {f[n]} {s[n,i,j].real} {s[n,i,j].imag}') + lines.append('+ ]') + lines.append('') + lines.append('.ENDS\n') + text = "" + if comments: + text += "\n".join(comments) + text += "\n" + text += '\n'.join(lines) + with open(filename, 'w+') as fd: fd.write(text) - if __name__ == '__main__': - import os - import sys + from pathlib import Path import skrf import argparse - parser = argparse.ArgumentParser() - parser.add_argument('filename', nargs=1, help='s-parameter file to validate against') + parser = argparse.ArgumentParser( + description="stospice: A program that converts S-Parameter (*.S[234]P) files into a spice netlist .inc file") + parser.add_argument('filename', type=str, help='s-parameter file to convert to SPICE netlist') + parser.add_argument('-o', dest="overwrite", action="store_true", help="Force overwrite exsting output", default=False) args = parser.parse_args() - filename = args.filename[0] - rootname = os.path.splitext(filename)[0] - incfile = f'{rootname}.inc' + filename = Path(args.filename) + incfile = filename.with_suffix('.inc') + if incfile.exists and not args.overwrite: + raise IOError("Output file {incfile} exists, use -o to overwrite") nw = skrf.Network(filename) - stospice(incfile, nw.name, nw.f, nw.s, nw.z0[0]) + comments = extract_comments(filename) + name = nw.name + stospice(incfile, name, nw.f, nw.s, nw.z0[0], comments) + print(f"Converted {filename} to {incfile} and created SPICE sub-circuit {name}") + +