This example shows how to test, visualize, and enforce the passivity of output from the rationalfit function.
Time-domain analysis and simulation depends critically on being able to convert frequency-domain S-parameter data into causal, stable, and passive time-domain representations. Because the rationalfit function guarantees that all poles are in the left half plane, rationalfit output is both stable and causal by construction. The problem is passivity.
N-port S-parameter data represents a frequency-dependent transfer function H(f). You can create an sparameter object in RF Toolbox by reading a Touchstone file, such as passive.s2p, into the sparameter function. You can use the ispassive function to check the passivity of the S-parameter data, and the passivity function to plot the 2-norm of the NxN matrices H(f) at each data frequency.
S = sparameters('passive.s2p');
ispassive(S)
ans = logical
1
passivity(S)
The rationalfit function converts N-port sparameter data S into an NxN matrix of rfmodel.rational objects.
Using the ispassive function on the NxN fit output reports that even if input data S is passive, the output fit is not passive. In other words, the norm H(f) is greater than one at some frequency in the range [0,Inf].
The passivity function takes an NxN fit as input and plots its passivity. This is a plot of the upper bound of the norm(H(f)) on [0,Inf], also known as the H-infinity norm.
fit = rationalfit(S); ispassive(fit)
ans = logical
0
passivity(fit)
The makepassive function takes as input an NxN array of fit objects and also the original S-parameter data, and produces a passive fit by using convex optimization techniques to optimally match the data of the S-parameter input S while satisfying passivity constraints. The residues C and feedthrough matrix D of the output pfit are modified, but the poles A of the output pfit are identical to the poles A of the input fit.
pfit = makepassive(fit,S,'Display','on');
ITER H-INFTY NORM FREQUENCY ERRDB CONSTRAINTS 0 1 + 1.791e-02 17.6816 GHz -40.4702 1 1 + 2.877e-04 275.347 MHz -40.9167 5 2 1 + 9.261e-05 365.665 MHz -40.9092 7 3 1 - 3.355e-07 368.266 MHz -40.906 9
ispassive(pfit)
ans = logical
1
passivity(pfit)
all(vertcat(pfit(:).A) == vertcat(fit(:).A))
ans = logical
1
To demonstrate that only C and D are modified by makepassive, one can zero out C and D and re-run makepassive. The output pfit still has the same poles as the input fit. The differences between pfit and pfit2 arise because of the different starting points of the convex optimizations.
One can use this feature of the makepassive function to produce a passive fit from a prescribed set of poles without any idea of starting C and D.
for k = 1:numel(fit) fit(k).C(:) = 0; fit(k).D(:) = 0; end pfit2 = makepassive(fit,S); passivity(pfit2)
all(vertcat(pfit2(:).A) == vertcat(fit(:).A))
ans = logical
1
The generateSPICE function takes a passive fit and generates an equivalent circuit as a SPICE subckt file. The input fit is an NxN array of rfmodel.rational objects as returned by rationalfit with an sparameters object as input. The generated file is a SPICE model constructed solely of passive R, L, C elements and controlled source elements E, F, G, and H.
generateSPICE(pfit2,'mypassive.ckt') type mypassive.ckt
* Equivalent circuit model for mypassive.ckt .SUBCKT mypassive po1 po2 Vsp1 po1 p1 0 Vsr1 p1 pr1 0 Rp1 pr1 0 50 Ru1 u1 0 50 Fr1 u1 0 Vsr1 -1 Fu1 u1 0 Vsp1 -1 Ry1 y1 0 1 Gy1 p1 0 y1 0 -0.02 Vsp2 po2 p2 0 Vsr2 p2 pr2 0 Rp2 pr2 0 50 Ru2 u2 0 50 Fr2 u2 0 Vsr2 -1 Fu2 u2 0 Vsp2 -1 Ry2 y2 0 1 Gy2 p2 0 y2 0 -0.02 Rx1 x1 0 1 Cx1 x1 0 2.73023889928382e-12 Gx1_1 x1 0 u1 0 -2.0604322836562 Rx2 x2 0 1 Cx2 x2 0 7.77758884882576e-12 Gx2_1 x2 0 u1 0 -2.91723139773995 Rx3 x3 0 1 Cx3 x3 0 2.29141629980399e-11 Gx3_1 x3 0 u1 0 -0.544080279799492 Rx4 x4 0 1 Cx4 x4 0 9.31845201627123e-11 Gx4_1 x4 0 u1 0 -0.654514239023566 Rx5 x5 0 1 Cx5 x5 0 4.89917764982128e-10 Gx5_1 x5 0 u1 0 -0.0811504179937328 Rx6 x6 0 1 Fxc6_7 x6 0 Vx7 18.7374939264546 Cx6 x6 xm6 3.95175907226011e-09 Vx6 xm6 0 0 Gx6_1 x6 0 u1 0 -0.0922182973334183 Rx7 x7 0 1 Fxc7_6 x7 0 Vx6 -0.0838145000452979 Cx7 x7 xm7 3.95175907226011e-09 Vx7 xm7 0 0 Gx7_1 x7 0 u1 0 0.00772923048602909 Rx8 x8 0 1 Cx8 x8 0 1.25490425570277e-08 Gx8_1 x8 0 u1 0 -0.947644178687728 Rx9 x9 0 1 Cx9 x9 0 2.73023889928382e-12 Gx9_2 x9 0 u2 0 -2.08391349356345 Rx10 x10 0 1 Cx10 x10 0 7.77758884882576e-12 Gx10_2 x10 0 u2 0 -2.92729174503047 Rx11 x11 0 1 Cx11 x11 0 2.29141629980399e-11 Gx11_2 x11 0 u2 0 -0.607553041582295 Rx12 x12 0 1 Cx12 x12 0 9.31845201627123e-11 Gx12_2 x12 0 u2 0 -0.692661287906845 Rx13 x13 0 1 Cx13 x13 0 4.89917764982128e-10 Gx13_2 x13 0 u2 0 -0.0860906329176697 Rx14 x14 0 1 Fxc14_15 x14 0 Vx15 18.3710776581628 Cx14 x14 xm14 3.95175907226011e-09 Vx14 xm14 0 0 Gx14_2 x14 0 u2 0 -0.0931988268714147 Rx15 x15 0 1 Fxc15_14 x15 0 Vx14 -0.0854862036277874 Cx15 x15 xm15 3.95175907226011e-09 Vx15 xm15 0 0 Gx15_2 x15 0 u2 0 0.00796721389180066 Rx16 x16 0 1 Cx16 x16 0 1.25490425570277e-08 Gx16_2 x16 0 u2 0 -0.948031806763662 Gyc1_1 y1 0 x1 0 -0.138989062060755 Gyc1_2 y1 0 x2 0 -0.0228686621642854 Gyc1_3 y1 0 x3 0 -1 Gyc1_4 y1 0 x4 0 -1 Gyc1_5 y1 0 x5 0 1 Gyc1_6 y1 0 x6 0 -1 Gyc1_7 y1 0 x7 0 -1 Gyc1_8 y1 0 x8 0 0.999804082821414 Gyc1_9 y1 0 x9 0 1 Gyc1_10 y1 0 x10 0 -1 Gyc1_11 y1 0 x11 0 0.809871147230155 Gyc1_12 y1 0 x12 0 0.941820669759408 Gyc1_13 y1 0 x13 0 -0.93505410807767 Gyc1_14 y1 0 x14 0 0.988848655186271 Gyc1_15 y1 0 x15 0 0.953486776095966 Gyc1_16 y1 0 x16 0 -1 Gyd1_1 y1 0 u1 0 0.603086046309566 Gyd1_2 y1 0 u2 0 -0.352316736727294 Gyc2_1 y2 0 x1 0 1 Gyc2_2 y2 0 x2 0 -1 Gyc2_3 y2 0 x3 0 0.900754754325028 Gyc2_4 y2 0 x4 0 0.996964039787101 Gyc2_5 y2 0 x5 0 -0.991558685615042 Gyc2_6 y2 0 x6 0 0.997618153616112 Gyc2_7 y2 0 x7 0 0.96120773249802 Gyc2_8 y2 0 x8 0 -1 Gyc2_9 y2 0 x9 0 -0.265675747275503 Gyc2_10 y2 0 x10 0 0.0684888346158697 Gyc2_11 y2 0 x11 0 -1 Gyc2_12 y2 0 x12 0 -1 Gyc2_13 y2 0 x13 0 1 Gyc2_14 y2 0 x14 0 -1 Gyc2_15 y2 0 x15 0 -1 Gyc2_16 y2 0 x16 0 0.999975807752118 Gyd2_1 y2 0 u1 0 -0.337219048696913 Gyd2_2 y2 0 u2 0 0.700211318479778 .ENDS