Add XSPICE models for transmission lines

This commit is contained in:
Vadim Kuznetsov 2025-05-22 10:16:42 +03:00
parent f656b73240
commit e82519388e
16 changed files with 2095 additions and 4 deletions

View File

@ -25,6 +25,7 @@ if $?xspice_enabled
@XSPICEINIT@ codemodel @pkglibdir@/xtradev.cm @XSPICEINIT@ codemodel @pkglibdir@/xtradev.cm
@XSPICEINIT@ codemodel @pkglibdir@/xtraevt.cm @XSPICEINIT@ codemodel @pkglibdir@/xtraevt.cm
@XSPICEINIT@ codemodel @pkglibdir@/table.cm @XSPICEINIT@ codemodel @pkglibdir@/table.cm
@XSPICEINIT@ codemodel @pkglibdir@/tlines.cm
end end

View File

@ -7,11 +7,11 @@ include makedefs
# The codemodels to make # The codemodels to make
CMDIRS = spice2poly digital analog xtradev xtraevt table CMDIRS = spice2poly digital analog xtradev xtraevt table tlines
#Invoke $(MAKE) for each of the CMDDIRS #Invoke $(MAKE) for each of the CMDDIRS
all: dstring.o # One common dstring object file for all code modules all: dstring.o msline_common.o tline_common.o# Common object files for all code modules
for cm in $(CMDIRS) ; do \ for cm in $(CMDIRS) ; do \
$(MAKE) cm=$$cm $$cm/$$cm.cm \ $(MAKE) cm=$$cm $$cm/$$cm.cm \
|| exit 1; \ || exit 1; \
@ -36,6 +36,8 @@ uninstall:
clean: clean:
rm -f dstring.o rm -f dstring.o
rm -f msline_common.o
rm -f tline_common.o
for cm in $(CMDIRS) ; do \ for cm in $(CMDIRS) ; do \
$(MAKE) cm=$$cm cm-clean \ $(MAKE) cm=$$cm cm-clean \
|| exit 1; \ || exit 1; \
@ -49,6 +51,12 @@ NGSRCBUILDDIR = $(CURDIR)/../..
dstring.o: $(NGSRCDIR)/misc/dstring.c $(NGSRCDIR)/include/ngspice/dstring.h dstring.o: $(NGSRCDIR)/misc/dstring.c $(NGSRCDIR)/include/ngspice/dstring.h
$(CC) $(CFLAGS) -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $< $(CC) $(CFLAGS) -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $<
msline_common.o: $(srcdir)/../tlines/msline_common.c $(srcdir)/../tlines/msline_common.h
$(CC) $(CFLAGS) -I$(srcdir)/../tlines -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $<
tline_common.o: $(srcdir)/../tlines/tline_common.c $(srcdir)/../tlines/tline_common.h
$(CC) $(CFLAGS) -I$(srcdir)/../tlines -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $<
ifdef cm ifdef cm
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
@ -70,6 +78,8 @@ cm-gens := \
cm-objs := \ cm-objs := \
$(cm)/dlmain.o \ $(cm)/dlmain.o \
dstring.o \ dstring.o \
tline_common.o \
msline_common.o \
$(modlst:%=$(cm)/%/cfunc.o) \ $(modlst:%=$(cm)/%/cfunc.o) \
$(modlst:%=$(cm)/%/ifspec.o) \ $(modlst:%=$(cm)/%/ifspec.o) \
$(udnlst:%=$(cm)/%/udnfunc.o) $(udnlst:%=$(cm)/%/udnfunc.o)
@ -160,11 +170,11 @@ $(cm)/dlmain.o : $(srcdir)/dlmain.c $(cm-descr)
$(do-deps) $(do-deps)
$(cm)/%/cfunc.o : $(cm)/%/cfunc.c $(cm)/%/cfunc.o : $(cm)/%/cfunc.c
$(COMPILE) $(gen_pp) -I$(srcdir)/$(<D) -o $@ -c $< $(COMPILE) $(gen_pp) -I$(srcdir)/$(<D) -I$(srcdir)/../tlines -o $@ -c $<
$(do-deps) $(do-deps)
$(cm)/%/ifspec.o : $(cm)/%/ifspec.c $(cm)/%/ifspec.o : $(cm)/%/ifspec.c
$(COMPILE) $(gen_pp) -I$(srcdir)/$(<D) -o $@ -c $< $(COMPILE) $(gen_pp) -I$(srcdir)/$(<D) -I$(srcdir)/../tlines -o $@ -c $<
$(do-deps) $(do-deps)
$(cm)/%/udnfunc.o : $(srcdir)/$(cm)/%/udnfunc.c $(cm)/%/udnfunc.o : $(srcdir)/$(cm)/%/udnfunc.c

View File

@ -0,0 +1,158 @@
/* ===========================================================================
FILE cfunc.mod
(c) Vadim Kuznetsov 2025
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <complex.h>
#include "msline_common.h"
#include "tline_common.h"
static void copy_complex(double complex s, Complex_t *d)
{
d->real = creal(s);
d->imag = cimag(s);
}
cpline_state_t *sim_points = NULL;
void cm_cpline (ARGS)
{
Complex_t z11, z12, z13, z14;
/* how to get properties of this component, e.g. L, W */
double l = PARAM(l);
double ze = PARAM(ze);
double zo = PARAM(zo);
double ere = PARAM(ere);
double ero = PARAM(ero);
double ae = PARAM(ae);
double ao = PARAM(ao);
ae = pow(10, 0.05*ae);
ao = pow(10, 0.05*ao);
if(INIT) {
}
/* Compute the output */
if(ANALYSIS == DC) {
double V1 = INPUT(p1s);
double V2 = INPUT(p2s);
double V3 = INPUT(p3s);
double V4 = INPUT(p4s);
double I1 = INPUT(p1);
double I2 = INPUT(p2);
double I3 = INPUT(p3);
double I4 = INPUT(p4);
double z = sqrt(ze*zo);
double V2out = V1 + z*I1;
double V1out = V2 + z*I2;
OUTPUT(p1) = V1out + I1*z;
OUTPUT(p2) = V2out + I2*z;
double V3out = V4 + z*I4;
double V4out = V3 + z*I3;
OUTPUT(p3) = V3out + I3*z;
OUTPUT(p4) = V4out + I4*z;
cm_analog_auto_partial();
}
else if(ANALYSIS == AC) {
double o = RAD_FREQ;
double complex _Z11, _Z12, _Z13, _Z14;
double complex arg_e = log(ae)*l/2.0 + I*o*l/C0*sqrt(ere);
double complex arg_o = log(ao)*l/2.0 + I*o*l/C0*sqrt(ero);
_Z11 = zo / (2*ctanh(arg_o)) + ze / (2*ctanh(arg_e));
_Z12 = zo / (2*csinh(arg_o)) + ze / (2*csinh(arg_e));
_Z13 = ze / (2*csinh(arg_e)) - zo / (2*csinh(arg_o));
_Z14 = ze / (2*ctanh(arg_e)) - zo / (2*ctanh(arg_o));
copy_complex(_Z11,&z11);
copy_complex(_Z12,&z12);
copy_complex(_Z13,&z13);
copy_complex(_Z14,&z14);
AC_GAIN(p1,p1) = z11; AC_GAIN(p2,p2) = z11;
AC_GAIN(p3,p3) = z11; AC_GAIN(p4,p4) = z11;
AC_GAIN(p1,p2) = z12; AC_GAIN(p2,p1) = z12;
AC_GAIN(p3,p4) = z12; AC_GAIN(p4,p3) = z12;
AC_GAIN(p1,p3) = z13; AC_GAIN(p3,p1) = z13;
AC_GAIN(p2,p4) = z13; AC_GAIN(p4,p2) = z13;
AC_GAIN(p1,p4) = z14; AC_GAIN(p4,p1) = z14;
AC_GAIN(p2,p3) = z14; AC_GAIN(p3,p2) = z14;
}
else if(ANALYSIS == TRANSIENT) {
double t = TIME;
double Vp[PORT_NUM];
double Ip[PORT_NUM];
Vp[0] = INPUT(p1s);
Vp[1] = INPUT(p2s);
Vp[2] = INPUT(p3s);
Vp[3] = INPUT(p4s);
Ip[0] = INPUT(p1);
Ip[1] = INPUT(p2);
Ip[2] = INPUT(p3);
Ip[3] = INPUT(p4);
double delay = l/(C0);
append_cpline_state(&sim_points, t, Vp, Ip, 1.2*delay);
if (t > delay) {
cpline_state_t *pp = find_cpline_state(sim_points, t - delay);
if (pp != NULL) {
double J1e = 0.5*(Ip[3] + Ip[0]);
double J1o = 0.5*(Ip[0] - Ip[3]);
double J2e = 0.5*(Ip[1] + Ip[2]);
double J2o = 0.5*(Ip[1] - Ip[2]);
double J1et = 0.5*(pp->Ip[3] + pp->Ip[0]);
double J1ot = 0.5*(pp->Ip[0] - pp->Ip[3]);
double J2et = 0.5*(pp->Ip[1] + pp->Ip[2]);
double J2ot = 0.5*(pp->Ip[1] - pp->Ip[2]);
double V1et = 0.5*(pp->Vp[3] + pp->Vp[0]);
double V1ot = 0.5*(pp->Vp[0] - pp->Vp[3]);
double V2et = 0.5*(pp->Vp[1] + pp->Vp[2]);
double V2ot = 0.5*(pp->Vp[1] - pp->Vp[2]);
double V1e = ze*J1e + V2et + ze*J2et;
double V1o = zo*J1o + V2ot + zo*J2ot;
double V2e = ze*J2e + V1et + ze*J1et;
double V2o = zo*J2o + V1ot + zo*J1ot;
double V1 = V1o + V1e;
double V2 = V2o + V2e;
double V3 = V2e - V2o;
double V4 = V1e - V1o;
OUTPUT(p1) = V1;
OUTPUT(p2) = V2;
OUTPUT(p3) = V3;
OUTPUT(p4) = V4;
}
cm_analog_auto_partial();
} else {
cm_analog_auto_partial();
}
}
}

View File

@ -0,0 +1,176 @@
/* ===========================================================================
FILE ifspec.ifs
(c) vadim Kuznetsov 2025
=========================================================================== */
/* Ports connection
4 --||||||-- 3
1 --||||||-- 2
*/
NAME_TABLE:
Spice_Model_Name: cpline
C_Function_Name: cm_cpline
Description: "Generic transmission line"
PORT_TABLE:
Port_Name: p1
Description: "Terminals Line1"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p2
Description: "Terminals Line1"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p3
Description: "Terminals Line2"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p4
Description: "Terminals Line2"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p1s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p2s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p3s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p4s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PARAMETER_TABLE:
Parameter_Name: l
Description: "length"
Data_Type: real
Default_Value: 1.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: ze
Description: "characteristic impedance of even mode"
Data_Type: real
Default_Value: 50.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: zo
Description: "characteristic impedance of odd mode"
Data_Type: real
Default_Value: 50.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: ae
Description: "attenuation per length (dB) even mode"
Data_Type: real
Default_Value: 0.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: ao
Description: "attenuation per length (dB) odd mode"
Data_Type: real
Default_Value: 0.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: ere
Description: "dielectric constant even mode"
Data_Type: real
Default_Value: 1.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: ero
Description: "dielectric constant odd mode"
Data_Type: real
Default_Value: 1.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes

View File

@ -0,0 +1,496 @@
/* ===========================================================================
FILE cfunc.mod
(c) Vadim Kuznetsov 2025
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <complex.h>
#include "msline_common.h"
#include "tline_common.h"
static double ae, ao, be, bo, ze, zo, ee, eo;
static void copy_complex(double complex s, Complex_t *d)
{
d->real = creal(s);
d->imag = cimag(s);
}
static cpline_state_t *state = NULL;
static void analyseQuasiStatic (double W, double h, double s,
double t, double er,
int SModel, double* Zle,
double* Zlo, double* ErEffe,
double* ErEffo);
static void analyseDispersion (double W, double h, double s,
double t, double er, double Zle,
double Zlo, double ErEffe,
double ErEffo, double frequency,
int DModel, double *ZleFreq,
double *ZloFreq,
double *ErEffeFreq,
double *ErEffoFreq);
static void calcPropagation (double W, double s,
double er, double h, double t, double tand, double rho, double D,
int SModel, int DModel, double frequency)
{
// quasi-static analysis
double Zle, ErEffe, Zlo, ErEffo;
analyseQuasiStatic (W, h, s, t, er, SModel, &Zle, &Zlo, &ErEffe, &ErEffo);
// analyse dispersion of Zl and Er
double ZleFreq, ErEffeFreq, ZloFreq, ErEffoFreq;
analyseDispersion (W, h, s, t, er, Zle, Zlo, ErEffe, ErEffo, frequency, DModel,
&ZleFreq, &ZloFreq, &ErEffeFreq, &ErEffoFreq);
// analyse losses of line
double ace, aco, ade, ado;
analyseLoss (W, t, er, rho, D, tand, Zle, Zlo, ErEffe,
frequency, HAMMERSTAD, &ace, &ade);
analyseLoss (W, t, er, rho, D, tand, Zlo, Zle, ErEffo,
frequency, HAMMERSTAD, &aco, &ado);
// compute propagation constants for even and odd mode
double k0 = 2 * M_PI * frequency / C0;
ae = ace + ade;
ao = aco + ado;
be = sqrt (ErEffeFreq) * k0;
bo = sqrt (ErEffoFreq) * k0;
ze = ZleFreq;
zo = ZloFreq;
ee = ErEffeFreq;
eo = ErEffoFreq;
}
/* The function calculates the quasi-static dielectric constants and
characteristic impedances for the even and odd mode based upon the
given line and substrate properties for parallel coupled microstrip
lines. */
static void analyseQuasiStatic (double W, double h, double s,
double t, double er,
int SModel, double* Zle,
double* Zlo, double* ErEffe,
double* ErEffo) {
// initialize default return values
*ErEffe = er; *ErEffo = er;
*Zlo = 42.2; *Zle = 55.7;
// normalized width and gap
double u = W / h;
double g = s / h;
// HAMMERSTAD and JENSEN
if (SModel == HAMMERSTAD) {
double Zl1, Fe, Fo, a, b, fo, Mu, Alpha, Beta, ErEff;
double Pe, Po, r, fo1, q, p, n, Psi, Phi, m, Theta;
// modifying equations for even mode
m = 0.2175 + pow (4.113 + pow (20.36 / g, 6.), -0.251) +
log (pow (g, 10.) / (1 + pow (g / 13.8, 10.))) / 323;
Alpha = 0.5 * exp (-g);
Psi = 1 + g / 1.45 + pow (g, 2.09) / 3.95;
Phi = 0.8645 * pow (u, 0.172);
Pe = Phi / (Psi * (Alpha * pow (u, m) + (1 - Alpha) * pow (u, -m)));
// TODO: is this ... Psi * (Alpha ... or ... Psi / (Alpha ... ?
// modifying equations for odd mode
n = (1 / 17.7 + exp (-6.424 - 0.76 * log (g) - pow (g / 0.23, 5.))) *
log ((10 + 68.3 * sqr (g)) / (1 + 32.5 * pow (g, 3.093)));
Beta = 0.2306 + log (pow (g, 10.) / (1 + pow (g / 3.73, 10.))) / 301.8 +
log (1 + 0.646 * pow (g, 1.175)) / 5.3;
Theta = 1.729 + 1.175 * log (1 + 0.627 / (g + 0.327 * pow (g, 2.17)));
Po = Pe - Theta / Psi * exp (Beta * pow (u, -n) * log (u));
// further modifying equations
r = 1 + 0.15 * (1 - exp (1 - sqr (er - 1) / 8.2) / (1 + pow (g, -6.)));
fo1 = 1 - exp (-0.179 * pow (g, 0.15) -
0.328 * pow (g, r) / log (M_E + pow (g / 7, 2.8)));
q = exp (-1.366 - g);
p = exp (-0.745 * pow (g, 0.295)) / cosh (pow (g, 0.68));
fo = fo1 * exp (p * log (u) + q * sin (M_PI * log10 (u)));
Mu = g * exp (-g) + u * (20 + sqr (g)) / (10 + sqr (g));
Hammerstad_ab (Mu, er, &a, &b);
Fe = pow (1 + 10 / Mu, -a * b);
Hammerstad_ab (u, er, &a, &b);
Fo = fo * pow (1 + 10 / u, -a * b);
// finally compute effective dielectric constants and impedances
*ErEffe = (er + 1) / 2 + (er - 1) / 2 * Fe;
*ErEffo = (er + 1) / 2 + (er - 1) / 2 * Fo;
Hammerstad_er (u, er, a, b, &ErEff); // single microstrip
// first variant
Zl1 = Z0 / (u + 1.98 * pow (u, 0.172));
Zl1 /= sqrt (ErEff);
// second variant
Hammerstad_zl (u, &Zl1);
Zl1 /= sqrt (ErEff);
*Zle = Zl1 / (1 - Zl1 * Pe / Z0);
*Zlo = Zl1 / (1 - Zl1 * Po / Z0);
}
// KIRSCHNING and JANSEN
else if (SModel == KIRSCHING) {
double a, b, ae, be, ao, bo, v, co, d, ErEff, Zl1;
double q1, q2, q3, q4, q5, q6, q7, q8, q9, q10;
// consider effect of finite strip thickness (JANSEN only)
double ue = u;
double uo = u;
if (t != 0 && s > 10 * (2 * t)) {
double dW = 0;
// SCHNEIDER, referred by JANSEN
if (u >= M_1_PI / 2 && M_1_PI / 2 > 2 * t / h)
dW = t * (1 + log (2 * h / t)) / M_PI;
else if (W > 2 * t)
dW = t * (1 + log (4 * M_PI * W / t)) / M_PI;
// JANSEN
double dt = 2 * t * h / s / er;
double We = W + dW * (1 - 0.5 * exp (-0.69 * dW / dt));
double Wo = We + dt;
ue = We / h;
uo = Wo / h;
}
// even relative dielectric constant
v = ue * (20 + sqr (g)) / (10 + sqr (g)) + g * exp (-g);
Hammerstad_ab (v, er, &ae, &be);
Hammerstad_er (v, er, ae, be, ErEffe);
// odd relative dielectric constant
Hammerstad_ab (uo, er, &a, &b);
Hammerstad_er (uo, er, a, b, &ErEff);
d = 0.593 + 0.694 * exp (-0.562 * uo);
bo = 0.747 * er / (0.15 + er);
co = bo - (bo - 0.207) * exp (-0.414 * uo);
ao = 0.7287 * (ErEff - (er + 1) / 2) * (1 - exp (-0.179 * uo));
*ErEffo = ((er + 1) / 2 + ao - ErEff) * exp (-co * pow (g, d)) + ErEff;
// characteristic impedance of single line
Hammerstad_zl (u, &Zl1);
Zl1 /= sqrt (ErEff);
// even characteristic impedance
q1 = 0.8695 * pow (ue, 0.194);
q2 = 1 + 0.7519 * g + 0.189 * pow (g, 2.31);
q3 = 0.1975 + pow (16.6 + pow (8.4 / g, 6.), -0.387) +
log (pow (g, 10.) / (1 + pow (g / 3.4, 10.))) / 241;
q4 = q1 / q2 * 2 /
(exp (-g) * pow (ue, q3) + (2 - exp (-g)) * pow (ue, -q3));
*Zle = sqrt (ErEff / *ErEffe) * Zl1 / (1 - Zl1 * sqrt (ErEff) * q4 / Z0);
// odd characteristic impedance
q5 = 1.794 + 1.14 * log (1 + 0.638 / (g + 0.517 * pow (g, 2.43)));
q6 = 0.2305 + log (pow (g, 10.) / (1 + pow (g / 5.8, 10.))) / 281.3 +
log (1 + 0.598 * pow (g, 1.154)) / 5.1;
q7 = (10 + 190 * sqr (g)) / (1 + 82.3 * cubic (g));
q8 = exp (-6.5 - 0.95 * log (g) - pow (g / 0.15, 5.));
q9 = log (q7) * (q8 + 1 / 16.5);
q10 = (q2 * q4 - q5 * exp (log (uo) * q6 * pow (uo, -q9))) / q2;
*Zlo = sqrt (ErEff / *ErEffo) * Zl1 / (1 - Zl1 * sqrt (ErEff) * q10 / Z0);
}
}
/* The function computes the dispersion effects on the dielectric
constants and characteristic impedances for the even and odd mode
of parallel coupled microstrip lines. */
static void analyseDispersion (double W, double h, double s,
double t, double er, double Zle,
double Zlo, double ErEffe,
double ErEffo, double frequency,
int DModel, double *ZleFreq,
double *ZloFreq,
double *ErEffeFreq,
double *ErEffoFreq) {
// initialize default return values
*ZleFreq = Zle;
*ErEffeFreq = ErEffe;
*ZloFreq = Zlo;
*ErEffoFreq = ErEffo;
// normalized width and gap
double u = W / h;
double g = s / h;
double ue, uo;
double B, dW, dt;
// compute u_odd, u_even
if (t > 0.0) {
if (u < 0.1592) {
B = 2 * M_PI * W;
} else {
B = h;
}
dW = t * (1.0 + log(2 * B / t)) / M_PI;
dt = t / (er * g);
ue = (W + dW * (1.0 - 0.5 * exp( -0.69 * dW / dt ))) / h;
uo = ue + dt / h;
} else {
ue = u;
uo = u;
}
// GETSINGER
if (DModel == GETSINGER) {
// even mode dispersion
Getsinger_disp (h, er, ErEffe, Zle / 2,
frequency, ErEffeFreq, ZleFreq);
*ZleFreq *= 2;
// odd mode dispersion
Getsinger_disp (h, er, ErEffo, Zlo * 2,
frequency, ErEffoFreq, ZloFreq);
*ZloFreq /= 2;
}
// KIRSCHNING and JANSEN
else if (DModel == DISP_KIRSCHING) {
double p1, p2, p3, p4, p5, p6, p7, Fe;
double fn = frequency * h * 1e-6;
// even relative dielectric constant dispersion
p1 = 0.27488 * (0.6315 + 0.525 / pow (1 + 0.0157 * fn, 20.)) * ue -
0.065683 * exp (-8.7513 * ue);
p2 = 0.33622 * (1 - exp (-0.03442 * er));
p3 = 0.0363 * exp (-4.6 * ue) * (1 - exp (- pow (fn / 38.7, 4.97)));
p4 = 1 + 2.751 * (1 - exp (- pow (er / 15.916, 8.)));
p5 = 0.334 * exp (-3.3 * cubic (er / 15)) + 0.746;
p6 = p5 * exp (- pow (fn / 18, 0.368));
p7 = 1 + 4.069 * p6 * pow (g, 0.479) *
exp (-1.347 * pow (g, 0.595) - 0.17 * pow (g, 2.5));
Fe = p1 * p2 * pow ((p3 * p4 + 0.1844 * p7) * fn, 1.5763);
*ErEffeFreq = er - (er - ErEffe) / (1 + Fe);
// odd relative dielectric constant dispersion
double p8, p9, p10, p11, p12, p13, p14, p15, Fo;
p1 = 0.27488 * (0.6315 + 0.525 / pow (1 + 0.0157 * fn, 20.)) * uo -
0.065683 * exp (-8.7513 * uo);
p3 = 0.0363 * exp (-4.6 * uo) * (1 - exp (- pow (fn / 38.7, 4.97)));
p8 = 0.7168 * (1 + 1.076 / (1 + 0.0576 * (er - 1)));
p9 = p8 - 0.7913 * (1 - exp (- pow (fn / 20, 1.424))) *
atan (2.481 * pow (er / 8, 0.946));
p10 = 0.242 * pow (er - 1, 0.55);
p11 = 0.6366 * (exp (-0.3401 * fn) - 1) *
atan (1.263 * pow (uo / 3, 1.629));
p12 = p9 + (1 - p9) / (1 + 1.183 * pow (uo, 1.376));
p13 = 1.695 * p10 / (0.414 + 1.605 * p10);
p14 = 0.8928 + 0.1072 * (1 - exp (-0.42 * pow (fn / 20, 3.215)));
p15 = fabs (1 - 0.8928 * (1 + p11) *
exp (-p13 * pow (g, 1.092)) * p12 / p14);
Fo = p1 * p2 * pow ((p3 * p4 + 0.1844) * fn * p15, 1.5763);
*ErEffoFreq = er - (er - ErEffo) / (1 + Fo);
// dispersion of even characteristic impedance
double t, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21;
q11 = 0.893 * (1 - 0.3 / (1 + 0.7 * (er - 1)));
t = pow (fn / 20, 4.91);
q12 = 2.121 * t / (1 + q11 * t) * exp (-2.87 * g) * pow (g, 0.902);
q13 = 1 + 0.038 * pow (er / 8, 5.1);
t = quadr (er / 15);
q14 = 1 + 1.203 * t / (1 + t);
q15 = 1.887 * exp (-1.5 * pow (g, 0.84)) * pow (g, q14) /
(1 + 0.41 * pow (fn / 15, 3.) *
pow (u, 2 / q13) / (0.125 + pow (u, 1.626 / q13)));
q16 = q15 * (1 + 9 / (1 + 0.403 * sqr (er - 1)));
q17 = 0.394 * (1 - exp (-1.47 * pow (u / 7, 0.672))) *
(1 - exp (-4.25 * pow (fn / 20, 1.87)));
q18 = 0.61 * (1 - exp (-2.31 * pow (u / 8, 1.593))) /
(1 + 6.544 * pow (g, 4.17));
q19 = 0.21 * quadr (g) / (1 + 0.18 * pow (g, 4.9)) / (1 + 0.1 * sqr (u)) /
(1 + pow (fn / 24, 3.));
q20 = q19 * (0.09 + 1 / (1 + 0.1 * pow (er - 1, 2.7)));
t = pow (u, 2.5);
q21 = fabs (1 - 42.54 * pow (g, 0.133) * exp (-0.812 * g) * t /
(1 + 0.033 * t));
double re, qe, pe, de, Ce, q0, ZlFreq, ErEffFreq;
Kirschning_er (u, fn, er, ErEffe, &ErEffFreq);
Kirschning_zl (u, fn, er, ErEffe, ErEffFreq, Zle, &q0, &ZlFreq);
re = pow (fn / 28.843, 12.);
qe = 0.016 + pow (0.0514 * er * q21, 4.524);
pe = 4.766 * exp (-3.228 * pow (u, 0.641));
t = pow (er - 1, 6.);
de = 5.086 * qe * re / (0.3838 + 0.386 * qe) *
exp (-22.2 * pow (u, 1.92)) / (1 + 1.2992 * re) * t / (1 + 10 * t);
Ce = 1 + 1.275 * (1 - exp (-0.004625 * pe * pow (er, 1.674) *
pow (fn / 18.365, 2.745))) - q12 + q16 - q17 + q18 + q20;
*ZleFreq = Zle * pow ((0.9408 * pow (ErEffFreq, Ce) - 0.9603) /
((0.9408 - de) * pow (ErEffe, Ce) - 0.9603), q0);
// dispersion of odd characteristic impedance
double q22, q23, q24, q25, q26, q27, q28, q29;
Kirschning_er (u, fn, er, ErEffo, &ErEffFreq);
Kirschning_zl (u, fn, er, ErEffo, ErEffFreq, Zlo, &q0, &ZlFreq);
q29 = 15.16 / (1 + 0.196 * sqr (er - 1));
t = sqr (er - 1);
q25 = 0.3 * sqr (fn) / (10 + sqr (fn)) * (1 + 2.333 * t / (5 + t));
t = pow ((er - 1) / 13, 12.);
q26 = 30 - 22.2 * t / (1 + 3 * t) - q29;
t = pow (er - 1, 1.5);
q27 = 0.4 * pow (g, 0.84) * (1 + 2.5 * t / (5 + t));
t = pow (er - 1, 3.);
q28 = 0.149 * t / (94.5 + 0.038 * t);
q22 = 0.925 * pow (fn / q26, 1.536) / (1 + 0.3 * pow (fn / 30, 1.536));
q23 = 1 + 0.005 * fn * q27 / (1 + 0.812 * pow (fn / 15, 1.9)) /
(1 + 0.025 * sqr (u));
t = pow (u, 0.894);
q24 = 2.506 * q28 * t / (3.575 + t) *
pow ((1 + 1.3 * u) * fn / 99.25, 4.29);
*ZloFreq = ZlFreq + (Zlo * pow (*ErEffoFreq / ErEffo, q22) - ZlFreq * q23) /
(1 + q24 + pow (0.46 * g, 2.2) * q25);
}
}
void cm_cpmline (ARGS)
{
Complex_t z11, z12, z13, z14;
/* how to get properties of this component, e.g. L, W */
double W = PARAM(w);
double l = PARAM(l);
double s = PARAM(s);
int SModel = PARAM(model);
int DModel = PARAM(disp);
/* how to get properties of the substrate, e.g. Er, H */
double er = PARAM(er);
double h = PARAM(h);
double t = PARAM(t);
double tand = PARAM(tand);
double rho = PARAM(rho);
double D = PARAM(d);
/* Compute the output */
if(ANALYSIS == DC) {
calcPropagation(W,s,er,h,t,tand,rho,D,SModel,DModel,0);
double V1 = INPUT(p1s);
double V2 = INPUT(p2s);
double V3 = INPUT(p3s);
double V4 = INPUT(p4s);
double I1 = INPUT(p1);
double I2 = INPUT(p2);
double I3 = INPUT(p3);
double I4 = INPUT(p4);
double z = sqrt(ze*zo);
double V2out = V1 + z*I1;
double V1out = V2 + z*I2;
OUTPUT(p1) = V1out + I1*z;
OUTPUT(p2) = V2out + I2*z;
double V3out = V4 + z*I4;
double V4out = V3 + z*I3;
OUTPUT(p3) = V3out + I3*z;
OUTPUT(p4) = V4out + I4*z;
cm_analog_auto_partial();
}
else if(ANALYSIS == AC) {
double o = RAD_FREQ;
calcPropagation(W,s,er,h,t,tand,rho,D,SModel,DModel, o/(2*M_PI));
double complex _Z11, _Z12, _Z13, _Z14;
double complex ge = ae + I*be;
double complex go = ao + I*bo;
_Z11 = zo / (2*ctanh(go*l)) + ze / (2*ctanh(ge*l));
_Z12 = zo / (2*csinh(go*l)) + ze / (2*csinh(ge*l));
_Z13 = ze / (2*csinh(ge*l)) - zo / (2*csinh(go*l));
_Z14 = ze / (2*ctanh(ge*l)) - zo / (2*ctanh(go*l));
copy_complex(_Z11,&z11);
copy_complex(_Z12,&z12);
copy_complex(_Z13,&z13);
copy_complex(_Z14,&z14);
AC_GAIN(p1,p1) = z11; AC_GAIN(p2,p2) = z11;
AC_GAIN(p3,p3) = z11; AC_GAIN(p4,p4) = z11;
AC_GAIN(p1,p2) = z12; AC_GAIN(p2,p1) = z12;
AC_GAIN(p3,p4) = z12; AC_GAIN(p4,p3) = z12;
AC_GAIN(p1,p3) = z13; AC_GAIN(p3,p1) = z13;
AC_GAIN(p2,p4) = z13; AC_GAIN(p4,p2) = z13;
AC_GAIN(p1,p4) = z14; AC_GAIN(p4,p1) = z14;
AC_GAIN(p2,p3) = z14; AC_GAIN(p3,p2) = z14;
}
else if(ANALYSIS == TRANSIENT) {
calcPropagation(W,s,er,h,t,tand,rho,D,SModel,DModel,0);
double t = TIME;
double Vp[PORT_NUM];
double Ip[PORT_NUM];
double Vnew[PORT_NUM];
Vp[0] = INPUT(p1s);
Vp[1] = INPUT(p2s);
Vp[2] = INPUT(p3s);
Vp[3] = INPUT(p4s);
Ip[0] = INPUT(p1);
Ip[1] = INPUT(p2);
Ip[2] = INPUT(p3);
Ip[3] = INPUT(p4);
double delay = l/(C0);
append_cpline_state(&state, t, Vp, Ip, 1.2*delay);
if (t > delay) {
cpline_state_t *pp = find_cpline_state(state, t - delay);
if (pp != NULL) {
double J1e = 0.5*(Ip[3] + Ip[0]);
double J1o = 0.5*(Ip[0] - Ip[3]);
double J2e = 0.5*(Ip[1] + Ip[2]);
double J2o = 0.5*(Ip[1] - Ip[2]);
double J1et = 0.5*(pp->Ip[3] + pp->Ip[0]);
double J1ot = 0.5*(pp->Ip[0] - pp->Ip[3]);
double J2et = 0.5*(pp->Ip[1] + pp->Ip[2]);
double J2ot = 0.5*(pp->Ip[1] - pp->Ip[2]);
double V1et = 0.5*(pp->Vp[3] + pp->Vp[0]);
double V1ot = 0.5*(pp->Vp[0] - pp->Vp[3]);
double V2et = 0.5*(pp->Vp[1] + pp->Vp[2]);
double V2ot = 0.5*(pp->Vp[1] - pp->Vp[2]);
double V1e = ze*J1e + V2et + ze*J2et;
double V1o = zo*J1o + V2ot + zo*J2ot;
double V2e = ze*J2e + V1et + ze*J1et;
double V2o = zo*J2o + V1ot + zo*J1ot;
double V1 = V1o + V1e;
double V2 = V2o + V2e;
double V3 = V2e - V2o;
double V4 = V1e - V1o;
OUTPUT(p1) = V1;
OUTPUT(p2) = V2;
OUTPUT(p3) = V3;
OUTPUT(p4) = V4;
}
cm_analog_auto_partial();
} else {
cm_analog_auto_partial();
}
}
}

View File

@ -0,0 +1,216 @@
/* ===========================================================================
FILE ifspec.ifs
(c) vadim Kuznetsov 2025
=========================================================================== */
/* Ports connection
4 --||||||-- 3
1 --||||||-- 2
*/
NAME_TABLE:
Spice_Model_Name: cpmlin
C_Function_Name: cm_cpmline
Description: "Generic transmission line"
PORT_TABLE:
Port_Name: p1
Description: "Terminals Line1"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p2
Description: "Terminals Line1"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p3
Description: "Terminals Line2"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p4
Description: "Terminals Line2"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p1s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p2s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p3s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: p4s
Description: "Sensing terminals line 1"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PARAMETER_TABLE:
Parameter_Name: l
Description: "length"
Data_Type: real
Default_Value: 1.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: w
Description: "width"
Data_Type: real
Default_Value: 1e-3
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: s
Description: "gap"
Data_Type: real
Default_Value: 1e-3
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: model
Description: "Model type"
Data_Type: int
Default_Value: 0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: disp
Description: "Dispersion type"
Data_Type: int
Default_Value: 0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: er
Description: "Substrate dielectric permittivity"
Data_Type: real
Default_Value: 9.8
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: h
Description: "Substrate thickness"
Data_Type: real
Default_Value: 1e-3
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: t
Description: "Metal strip thickness"
Data_Type: real
Default_Value: 35e-6
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: tand
Description: "Substrate dielectric loss"
Data_Type: real
Default_Value: 2e-4
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: rho
Description: "Metal resistance"
Data_Type: real
Default_Value: 0.022e-6
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: d
Description: "RMS Substrate roughness"
Data_Type: real
Default_Value: 0.15e-6
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes

View File

@ -0,0 +1,133 @@
/* ===========================================================================
FILE cfunc.mod
(c) Vadim Kuznetsov 2025
*/
#include <stdio.h>
#include <math.h>
#include <complex.h>
#include "tline_common.h"
#include "msline_common.h"
static tline_state_t *sim_points = NULL;
static double zl, alpha, beta, ereff;
static void calcPropagation (double W, int SModel, int DModel,
double er, double h, double t, double tand, double rho, double D,
double frequency) {
/* local variables */
double ac, ad;
double ZlEff, ErEff, WEff, ZlEffFreq, ErEffFreq;
// quasi-static effective dielectric constant of substrate + line and
// the impedance of the microstrip line
mslineAnalyseQuasiStatic (W, h, t, er, SModel, &ZlEff, &ErEff, &WEff);
// analyse dispersion of Zl and Er (use WEff here?)
mslineAnalyseDispersion (W, h, er, ZlEff, ErEff, frequency, DModel,
&ZlEffFreq, &ErEffFreq);
// analyse losses of line
analyseLoss (W, t, er, rho, D, tand, ZlEff, ZlEff, ErEff,
frequency, HAMMERSTAD, &ac, &ad);
// calculate propagation constants and reference impedance
zl = ZlEffFreq;
ereff = ErEffFreq;
alpha = ac + ad;
beta = sqrt (ErEffFreq) * 2 * M_PI * frequency / C0;
}
void cm_mlin (ARGS)
{
Complex_t z11, z21;
/* how to get properties of this component, e.g. L, W */
double W = PARAM(w);
double l = PARAM(l);
int SModel = PARAM(model);
int DModel = PARAM(disp);
int TModel = PARAM(tranmodel);
/* how to get properties of the substrate, e.g. Er, H */
double er = PARAM(er);
double h = PARAM(h);
double t = PARAM(t);
double tand = PARAM(tand);
double rho = PARAM(rho);
double D = PARAM(d);
/* Initialize/access instance specific storage for capacitor voltage */
if(INIT) {
}
/* Compute the output */
if(ANALYSIS == DC) {
calcPropagation(W,SModel,DModel,er,h,t,tand,rho,D,0);
double V1 = INPUT(V1sens);
double V2 = INPUT(V2sens);
double I1 = INPUT(port1);
double I2 = INPUT(port2);
double V2out = V1 + zl*I1;
double V1out = V2 + zl*I2;
OUTPUT(port1) = V1out + I1*zl;
OUTPUT(port2) = V2out + I2*zl;
cm_analog_auto_partial();
}
else if(ANALYSIS == AC) {
double frequency = RAD_FREQ/(2.0*M_PI);
calcPropagation(W,SModel,DModel,er,h,t,tand,rho,D,frequency);
double complex g = alpha + beta*I;
double complex _Z11 = zl / ctanh(g*l);
double complex _Z21 = zl / csinh(g*l);
z11.real = creal(_Z11); z11.imag = cimag(_Z11);
z21.real = creal(_Z21); z21.imag = cimag(_Z21);
AC_GAIN(port1,port1) = z11; AC_GAIN(port2,port2) = z11;
AC_GAIN(port1,port2) = z21; AC_GAIN(port2,port1) = z21;
}
else if(ANALYSIS == TRANSIENT) {
calcPropagation(W,SModel,DModel,er,h,t,tand,rho,D,0);
double t = TIME;
double V1 = INPUT(V1sens);
double V2 = INPUT(V2sens);
double I1 = INPUT(port1);
double I2 = INPUT(port2);
double delay = l/(C0) * sqrt(ereff);
if (TModel == TRAN_FULL) {
append_state(&sim_points, t, V1, V2, I1, I2, 1.2*delay);
}
if (t > delay && TModel == TRAN_FULL) {
tline_state_t *pp = get_state(sim_points, t - delay);
if (pp != NULL) {
double V2out = pp->V1 + zl*(pp->I1);
double V1out = pp->V2 + zl*(pp->I2);
OUTPUT(port1) = V1out + I1*zl;
OUTPUT(port2) = V2out + I2*zl;
}
cm_analog_auto_partial();
} else {
double V2out = V1 + zl*I1;
double V1out = V2 + zl*I2;
OUTPUT(port1) = V1out + I1*zl;
OUTPUT(port2) = V2out + I2*zl;
cm_analog_auto_partial();
}
}
}

View File

@ -0,0 +1,173 @@
/* ===========================================================================
FILE ifspec.ifs
(c) vadim Kuznetsov 2025
=========================================================================== */
NAME_TABLE:
Spice_Model_Name: mlin
C_Function_Name: cm_mlin
Description: "Microstrip line"
PORT_TABLE:
Port_Name: port1
Description: "Microstrip terminals"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: port2
Description: "Microstrip terminals"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: V1sens
Description: "Sensing terminals"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: V2sens
Description: "Sensisng terminals"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PARAMETER_TABLE:
Parameter_Name: l
Description: "length"
Data_Type: real
Default_Value: 1e-2
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: w
Description: "width"
Data_Type: real
Default_Value: 1e-3
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: model
Description: "Model type"
Data_Type: int
Default_Value: 0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: disp
Description: "Dispersion type"
Data_Type: int
Default_Value: 0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: er
Description: "Substrate dielectric permittivity"
Data_Type: real
Default_Value: 9.8
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: h
Description: "Substrate thickness"
Data_Type: real
Default_Value: 1e-3
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: t
Description: "Metal strip thickness"
Data_Type: real
Default_Value: 35e-6
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: tand
Description: "Substrate dielectric loss"
Data_Type: real
Default_Value: 2e-4
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: rho
Description: "Metal resistance"
Data_Type: real
Default_Value: 0.022e-6
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: d
Description: "RMS Substrate roughness"
Data_Type: real
Default_Value: 0.15e-6
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: tranmodel
Description: "RMS Substrate roughness"
Data_Type: int
Default_Value: 0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes

View File

@ -0,0 +1,4 @@
mlin
tline
cpline
cpmlin

View File

@ -0,0 +1,91 @@
/* ===========================================================================
FILE cfunc.mod
(c) Vadim Kuznetsov 2025
*/
#include <stdio.h>
#include <math.h>
#include <complex.h>
#include "msline_common.h"
#include "tline_common.h"
static tline_state_t *sim_points = NULL;
void cm_tline (ARGS)
{
Complex_t z11, z21;
/* how to get properties of this component, e.g. L, W */
double z = PARAM(z);
double l = PARAM(l);
double a = PARAM(a);
double alpha = pow(10,0.05*a);
alpha = log(alpha)/2.0;
/* Initialize/access instance specific storage for capacitor voltage */
if(INIT) {
}
/* Compute the output */
if(ANALYSIS == DC) {
double V1 = INPUT(V1sens);
double V2 = INPUT(V2sens);
double I1 = INPUT(in);
double I2 = INPUT(out);
double V2out = V1 + z*I1;
double V1out = V2 + z*I2;
OUTPUT(in) = V1out + I1*z;
OUTPUT(out) = V2out + I2*z;
cm_analog_auto_partial();
}
else if(ANALYSIS == AC) {
double beta = RAD_FREQ/C0;
double complex g = alpha + beta*I;
double complex _Z11 = z / ctanh(g*l);
double complex _Z21 = z / csinh (g*l);
z11.real = creal(_Z11);
z11.imag = cimag(_Z11);
z21.real = creal(_Z21);
z21.imag = cimag(_Z21);
AC_GAIN(in, in) = z11; AC_GAIN(out,out) = z11;
AC_GAIN(in,out) = z21; AC_GAIN(out,in) = z21;
}
else if(ANALYSIS == TRANSIENT) {
double t = TIME;
double V1 = INPUT(V1sens);
double V2 = INPUT(V2sens);
double I1 = INPUT(in);
double I2 = INPUT(out);
double delay = l/(C0);
append_state(&sim_points, t, V1, V2, I1, I2, 1.2*delay);
if (t > delay) {
tline_state_t *pp = get_state(sim_points, t - delay);
if (pp != NULL) {
double V2out = pp->V1 + z*(pp->I1);
double V1out = pp->V2 + z*(pp->I2);
OUTPUT(in) = V1out + I1*z;
OUTPUT(out) = V2out + I2*z;
}
cm_analog_auto_partial();
} else {
double V2out = V1 + z*I1;
double V1out = V2 + z*I2;
OUTPUT(in) = V1out + I1*z;
OUTPUT(out) = V2out + I2*z;
cm_analog_auto_partial();
}
}
}

View File

@ -0,0 +1,87 @@
/* ===========================================================================
FILE ifspec.ifs
(c) vadim Kuznetsov 2025
=========================================================================== */
NAME_TABLE:
Spice_Model_Name: tline
C_Function_Name: cm_tline
Description: "Generic transmission line"
PORT_TABLE:
Port_Name: in
Description: "Terminals"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: out
Description: "Terminals"
Direction: inout
Default_Type: hd
Allowed_Types: [hd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: V1sens
Description: "Sensing terminals"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PORT_TABLE:
Port_Name: V2sens
Description: "Sensisng terminals"
Direction: in
Default_Type: vd
Allowed_Types: [vd]
Vector: no
Vector_Bounds: -
Null_Allowed: no
PARAMETER_TABLE:
Parameter_Name: l
Description: "length"
Data_Type: real
Default_Value: 1.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: z
Description: "characteristic impedance"
Data_Type: real
Default_Value: 50.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: a
Description: "attenuation per length (dB)"
Data_Type: real
Default_Value: 0.0
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes

View File

View File

@ -0,0 +1,337 @@
/* msline_common.c
* common definitions for microstrip devices
* (c) Vadim Kuznetsov 2025
*/
#include <stdio.h>
#include <math.h>
#include "tline_common.h"
#include "msline_common.h"
/* This function calculates the quasi-static impedance of a microstrip
* line, the value of the effective dielectric constant and the
* effective width due to the finite conductor thickness for the given
* microstrip line and substrate properties. */
void mslineAnalyseQuasiStatic (double W, double h, double t,
double er, int Model,
double *ZlEff, double *ErEff,
double *WEff) {
double z, e;
// default values
e = er;
z = z0;
*WEff = W;
// WHEELER
if (Model == WHEELER) {
double a, b, c, d, x, dW1, dWr, Wr;
// compute strip thickness effect
if (t != 0) {
dW1 = t / M_PI * log (4 * M_E / sqrt (sqr (t / h) +
sqr (M_1_PI / (W / t + 1.10))));
}
else dW1 = 0;
dWr = (1 + 1 / er) / 2 * dW1;
Wr = W + dWr; *WEff =Wr;
// compute characteristic impedance
if (W / h < 3.3) {
c = log (4 * h / Wr + sqrt (sqr (4 * h / Wr) + 2));
b = (er - 1) / (er + 1) / 2 * (log (M_PI_2) + log (2 * M_2_PI) / er);
z = (c - b) * Z0 / M_PI / sqrt (2 * (er + 1));
}
else {
c = 1 + log (M_PI_2) + log (Wr / h / 2 + 0.94);
d = M_1_PI / 2 * (1 + log (sqr (M_PI) / 16)) * (er - 1) / sqr (er);
x = 2 * M_LN2 / M_PI + Wr / h / 2 + (er + 1) / 2 / M_PI / er * c + d;
z = Z0 / 2 / x / sqrt (er);
}
// compute effective dielectric constant
if (W / h < 1.3) {
a = log (8 * h / Wr) + sqr (Wr / h) / 32;
b = (er - 1) / (er + 1) / 2 * (log (M_PI_2) + log (2 * M_2_PI) / er);
e = (er + 1) / 2 * sqr (a / (a - b));
}
else {
a = (er - 1) / 2 / M_PI / er * (log (2.1349 * Wr / h + 4.0137) -
0.5169 / er);
b = Wr / h / 2 + M_1_PI * log (8.5397 * Wr / h + 16.0547);
e = er * sqr ((b - a) / b);
}
}
// SCHNEIDER
else if (Model == SCHNEIDER) {
double dW = 0, u = W / h;
// consider strip thickness equations
if (t != 0 && t < W / 2) {
double arg = (u < M_1_PI / 2) ? 2 * M_PI * W / t : h / t;
dW = t / M_PI * (1 + log (2 * arg));
if (t / dW >= 0.75) dW = 0;
}
*WEff = W + dW; u = *WEff / h;
// effective dielectric constant
e = (er + 1) / 2 + (er - 1) / 2 / sqrt (1 + 10 / u);
// characteristic impedance
if (u < 1.0) {
z = M_1_PI / 2 * log (8 / u + u / 4);
}
else {
z = 1 / (u + 2.42 - 0.44 / u + pow ((1. - 1. / u), 6.));
}
z = Z0 * z / sqrt (e);
}
// HAMMERSTAD and JENSEN
else if (Model == HAMMERSTAD) {
double a, b, du1, du, u, ur, u1, zr, z1;
u = W / h; // normalized width
t = t / h; // normalized thickness
// compute strip thickness effect
if (t != 0) {
du1 = t / M_PI * log (1 + 4 * M_E / t / sqr (coth (sqrt (6.517 * u))));
}
else du1 = 0;
du = du1 * (1 + sech (sqrt (er - 1))) / 2;
u1 = u + du1;
ur = u + du;
*WEff = ur * h;
// compute impedances for homogeneous medium
Hammerstad_zl (ur, &zr);
Hammerstad_zl (u1, &z1);
// compute effective dielectric constant
Hammerstad_ab (ur, er, &a, &b);
Hammerstad_er (ur, er, a, b, &e);
// compute final characteristic impedance and dielectric constant
// including strip thickness effects
z = zr / sqrt (e);
e = e * sqr (z1 / zr);
}
*ZlEff = z;
*ErEff = e;
}
/* This function calculates the frequency dependent value of the
* effective dielectric constant and the microstrip line impedance for
* the given frequency. */
void mslineAnalyseDispersion (double W, double h, double er,
double ZlEff, double ErEff,
double frequency, int Model,
double* ZlEffFreq,
double* ErEffFreq) {
double e, z;
// default values
z = *ZlEffFreq = ZlEff;
e = *ErEffFreq = ErEff;
// GETSINGER
if (Model == GETSINGER) {
Getsinger_disp (h, er, ErEff, ZlEff, frequency, &e, &z);
}
// SCHNEIDER
else if (Model == DISP_SCHNEIDER) {
double k, f;
k = sqrt (ErEff / er);
f = 4 * h * frequency / C0 * sqrt (er - 1);
f = sqr (f);
e = ErEff * sqr ((1 + f) / (1 + k * f));
z = ZlEff * sqrt (ErEff / e);
}
// YAMASHITA
else if (Model == YAMASHITA) {
double k, f;
k = sqrt (er / ErEff);
f = 4 * h * frequency / C0 * sqrt (er - 1) *
(0.5 + sqr (1 + 2 * log10 (1 + W / h)));
e = ErEff * sqr ((1 + k * pow (f, 1.5) / 4) / (1 + pow (f, 1.5) / 4));
}
// KOBAYASHI
else if (Model == KOBAYASHI) {
double n, no, nc, fh, fk;
fk = C0 * atan (er * sqrt ((ErEff - 1) / (er - ErEff))) /
(2 * M_PI * h * sqrt (er - ErEff));
fh = fk / (0.75 + (0.75 - 0.332 / pow (er, 1.73)) * W / h);
no = 1 + 1 / (1 + sqrt (W / h)) + 0.32 * cubic (1 / (1 + sqrt (W / h)));
if (W / h < 0.7) {
nc = 1 + 1.4 / (1 + W / h) * (0.15 - 0.235 *
exp (-0.45 * frequency / fh));
}
else nc = 1;
n = no * nc < 2.32 ? no * nc : 2.32;
e = er - (er - ErEff) / (1 + pow (frequency / fh, n));
}
// PRAMANICK and BHARTIA
else if (Model == PRAMANICK) {
double Weff, We, f;
f = 2 * MU0 * h * frequency * sqrt (ErEff / er) / ZlEff;
e = er - (er - ErEff) / (1 + sqr (f));
Weff = Z0 * h / ZlEff / sqrt (ErEff);
We = W + (Weff - W) / (1 + sqr (f));
z = Z0 * h / We / sqrt (e);
}
// HAMMERSTAD and JENSEN
else if (Model == DISP_HAMMERSTAD) {
double f, g;
g = sqr (M_PI) / 12 * (er - 1) / ErEff * sqrt (2 * M_PI * ZlEff / Z0);
f = 2 * MU0 * h * frequency / ZlEff;
e = er - (er - ErEff) / (1 + g * sqr (f));
z = ZlEff * sqrt (ErEff / e) * (e - 1) / (ErEff - 1);
}
// KIRSCHNING and JANSEN
else if (Model == DISP_KIRSCHING) {
double r17, u = W / h, fn = frequency * h / 1e6;
// dispersion of dielectric constant
Kirschning_er (u, fn, er, ErEff, &e);
// dispersion of characteristic impedance
Kirschning_zl (u, fn, er, ErEff, e, ZlEff, &r17, &z);
}
*ZlEffFreq = z;
*ErEffFreq = e;
}
/* Computes the exponent factors a(u) and b(er) used within the
* effective relative dielectric constant calculations for single and
* coupled microstrip lines by Hammerstad and Jensen. */
void Hammerstad_ab (double u, double er, double *a,
double *b) {
*a = 1 + log ((quadr (u) + sqr (u / 52)) / (quadr (u) + 0.432)) / 49 +
log (1 + cubic (u / 18.1)) / 18.7;
*b = 0.564 * pow ((er - 0.9) / (er + 3), 0.053);
}
/* The function computes the effective dielectric constant of a single
* microstrip. The equation is used in single and coupled microstrip
* calculations. */
void Hammerstad_er (double u, double er, double a,
double b, double* e) {
*e = (er + 1) / 2 + (er - 1) / 2 * pow (1 + 10 / u, -a * b);
}
/* This function computes the characteristic impedance of single
* microstrip line based upon the given width-height ratio. The
* equation is used in single and coupled microstrip calculations as
* well. */
void Hammerstad_zl (double u, double *zl) {
double fu = 6 + (2 * M_PI - 6) * exp (- pow (30.666 / u, 0.7528));
*zl = Z0 / 2 / M_PI * log (fu / u + sqrt (1 + sqr (2 / u)));
}
/* Calculates dispersion effects for effective dielectric constant and
* characteristic impedance as defined by Getsinger (for single and
* coupled microstrips). */
void Getsinger_disp (double h, double er, double ErEff,
double ZlEff, double frequency,
double *e, double *z) {
double g, f, d;
g = 0.6 + 0.009 * ZlEff;
f = frequency * 2 * MU0 * h / ZlEff;
*e = er - (er - ErEff) / (1 + g * sqr (f));
d = (er - *e) * (*e - ErEff) / *e / (er - ErEff);
*z = ZlEff * sqrt (*e / ErEff) / (1 + d); // group delay model
}
/* This function computes the dispersion of the effective dielectric
* constant of a single microstrip line. It is defined in a separate
* function because it is used within the coupled microstrip lines as
* well. */
void Kirschning_er (double u, double fn, double er,
double ErEff, double* ErEffFreq) {
double p, p1, p2, p3, p4;
p1 = 0.27488 + (0.6315 + 0.525 / pow (1. + 0.0157 * fn, 20.)) * u -
0.065683 * exp (-8.7513 * u);
p2 = 0.33622 * (1 - exp (-0.03442 * er));
p3 = 0.0363 * exp (-4.6 * u) * (1 - exp (- pow (fn / 38.7, 4.97)));
p4 = 1 + 2.751 * (1 - exp (- pow (er / 15.916, 8.)));
p = p1 * p2 * pow ((0.1844 + p3 * p4) * fn, 1.5763);
*ErEffFreq = er - (er - ErEff) / (1 + p);
}
/* Computes dispersion effects of characteristic impedance of a single
* microstrip line according to Kirschning and Jansen. Also used in
* coupled microstrip lines calculations. */
void Kirschning_zl (double u, double fn, double er,
double ErEff, double ErEffFreq,
double ZlEff, double* r17,
double* ZlEffFreq) {
double r1, r2, r3, r4, r5, r6, r7, r8, r9, r10;
double r11, r12, r13, r14, r15, r16;
r1 = 0.03891 * pow (er, 1.4);
r2 = 0.267 * pow (u, 7.);
r3 = 4.766 * exp (-3.228 * pow (u, 0.641));
r4 = 0.016 + pow (0.0514 * er, 4.524);
r5 = pow (fn / 28.843, 12.);
r6 = 22.20 * pow (u, 1.92);
r7 = 1.206 - 0.3144 * exp (-r1) * (1 - exp (-r2));
r8 = 1 + 1.275 * (1 - exp (-0.004625 * r3 *
pow (er, 1.674) * pow (fn / 18.365, 2.745)));
r9 = 5.086 * r4 * r5 / (0.3838 + 0.386 * r4) *
exp (-r6) / (1 + 1.2992 * r5) *
pow (er - 1., 6.) / (1 + 10 * pow (er - 1., 6.));
r10 = 0.00044 * pow (er, 2.136) + 0.0184;
r11 = pow (fn / 19.47, 6.) / (1 + 0.0962 * pow (fn / 19.47, 6.));
r12 = 1 / (1 + 0.00245 * sqr (u));
r13 = 0.9408 * pow (ErEffFreq, r8) - 0.9603;
r14 = (0.9408 - r9) * pow (ErEff, r8) - 0.9603;
r15 = 0.707 * r10 * pow (fn / 12.3, 1.097);
r16 = 1 + 0.0503 * sqr (er) * r11 * (1 - exp (- pow (u / 15., 6.)));
*r17 = r7 * (1 - 1.1241 * r12 / r16 *
exp (-0.026 * pow (fn, 1.15656) - r15));
*ZlEffFreq = ZlEff * pow (r13 / r14, *r17);
}
/* The function calculates the conductor and dielectric losses of a
* single microstrip line. */
void analyseLoss (double W, double t, double er,
double rho, double D, double tand,
double ZlEff1, double ZlEff2,
double ErEff,
double frequency, int Model,
double* ac, double* ad) {
*ac = *ad = 0;
// HAMMERSTAD and JENSEN
if (Model == HAMMERSTAD) {
double Rs, ds, l0, Kr, Ki;
// conductor losses
if (t != 0.0) {
Rs = sqrt (M_PI * frequency * MU0 * rho); // skin resistance
ds = rho / Rs; // skin depth
// valid for t > 3 * ds
if (t < 3 * ds && frequency != 0) {
fprintf (stderr,
"WARNING: conductor loss calculation invalid for line "
"height t (%g) < 3 * skin depth (%g)\n", t, 3 * ds);
}
// current distribution factor
Ki = exp (-1.2 * pow ((ZlEff1 + ZlEff2) / 2 / Z0, 0.7));
// D is RMS surface roughness
Kr = 1 + M_2_PI * atan (1.4 * sqr (D / ds));
*ac = Rs / (ZlEff1 * W) * Ki * Kr;
}
// dielectric losses
l0 = C0 / frequency;
*ad = M_PI * er / (er - 1) * (ErEff - 1) / sqrt (ErEff) * tand / l0;
}
}

View File

@ -0,0 +1,60 @@
/* msline_common.h
* common definitions for microstrip devices
* (c) Vadim Kuznetsov 2025
*/
#ifndef MSLINE_COMMON_H
#define MSLINE_COMMON_H
//TRAN model
#define TRAN_DC 0
#define TRAN_FULL 1
// MS line model
#define HAMMERSTAD 0
#define KIRSCHING 1
#define WHEELER 2
#define SCHNEIDER 3
// Dispersion model
#define DISP_KIRSCHING 0
#define KOBAYASHI 1
#define YAMASHITA 2
#define DISP_HAMMERSTAD 3
#define GETSINGER 4
#define DISP_SCHNEIDER 5
#define PRAMANICK 6
void Hammerstad_ab (double, double,
double*, double*);
void Hammerstad_er (double, double, double,
double, double*);
void Hammerstad_zl (double, double*);
void Getsinger_disp (double, double, double,
double, double,
double*, double*);
void Kirschning_er (double, double, double,
double, double*);
void Kirschning_zl (double, double, double,
double, double, double,
double*, double*);
void mslineAnalyseQuasiStatic (double W, double h, double t,
double er, int Model,
double *ZlEff, double *ErEff,
double *WEff);
void mslineAnalyseDispersion (double W, double h, double er,
double ZlEff, double ErEff,
double frequency, int Model,
double* ZlEffFreq,
double* ErEffFreq);
void analyseLoss (double, double, double, double,
double, double, double, double,
double, double, int,
double*, double*);
#endif

View File

@ -0,0 +1,86 @@
/* tline_common.c
* common definitions for all transmission lines
* (c) Vadim Kuznetsov 2025
*/
#include <stdlib.h>
#include <string.h>
#include "tline_common.h"
void append_state(tline_state_t **first, double time, double V1, double V2,
double I1, double I2, double tmax)
{
tline_state_t *pp = (tline_state_t *) malloc(sizeof(tline_state_t));
pp->next = NULL;
pp->time = time;
pp->V1 = V1; pp->I1 = I1;
pp->V2 = V2; pp->I2 = I2;
if (*first == NULL) {
*first = pp;
} else {
tline_state_t *pn = *first;
while (pn->next != NULL) {
pn = pn->next;
}
pn->next = pp;
double t0 = (*first)->time;
if ((time - t0) > tmax) {
tline_state_t *new_first = (*first)->next;
free(*first);
*first = new_first;
}
}
}
tline_state_t *get_state(tline_state_t *first, double time)
{
tline_state_t *pp = first;
while (pp != NULL && pp->time < time) {
pp = pp->next;
}
return pp;
}
void append_cpline_state(cpline_state_t **first, double time, double *Vp, double *Ip, double tmax)
{
cpline_state_t *pp = (cpline_state_t *) malloc(sizeof(cpline_state_t));
pp->next = NULL;
pp->time = time;
memcpy(pp->Vp, Vp, PORT_NUM*sizeof(double));
memcpy(pp->Ip, Ip, PORT_NUM*sizeof(double));
if (*first == NULL) {
*first = pp;
} else {
cpline_state_t *pn = *first;
while (pn->next != NULL) {
pn = pn->next;
}
pn->next = pp;
double t0 = (*first)->time;
if ((time - t0) > tmax) {
cpline_state_t *new_first = (*first)->next;
free(*first);
*first = new_first;
}
}
}
cpline_state_t *find_cpline_state(cpline_state_t *first, double time)
{
cpline_state_t *pp = first;
while (pp != NULL && pp->time < time) {
pp = pp->next;
}
return pp;
}

View File

@ -0,0 +1,63 @@
/* tline_common.h
* common definitions for all transmission lines
* (c) Vadim Kuznetsov 2025
*/
#ifndef TLINE_COMMON_H
#define TLINE_COMMON_H
// Constants
#define Z0 (120*M_PI)
#define z0 50.0
#define MU0 (4*M_PI*1e-7)
#define C0 299792458.0
#define GMIN 1e-12
// Functions
#define sqr(x) (x*x)
#define cubic(x) (x*x*x)
#define quadr(x) (x*x*x*x)
#define coth(x) (1.0/tanh(x))
#define sech(x) (1.0/cosh(x))
#define cosech(x) (1.0/sinh(x))
// Data structures to hold transient state
typedef struct tline_state {
double time;
double I1;
double I2;
double V1;
double V2;
struct tline_state *next;
} tline_state_t;
// Functions to retrieve previous transient state
void append_state(tline_state_t **first, double time, double V1, double V2,
double I1, double I2, double tmax);
tline_state_t *get_state(tline_state_t *first, double time);
#define PORT_NUM 4
typedef struct cpline_state {
double time;
double Ip[PORT_NUM];
double Vp[PORT_NUM];
struct cpline_state *next;
} cpline_state_t;
void append_cpline_state(cpline_state_t **first, double time, double *Vp, double *Ip, double tmax);
cpline_state_t *find_cpline_state(cpline_state_t *first, double time);
#endif