Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tapered line component #380

Closed
wants to merge 16 commits into from
Closed

Conversation

andresmmera
Copy link
Contributor

This component is aimed to model the S-param behaviour of a tapered line. It provides 4 impedance profiles: Exponential, Linear, Triangular and Klopfenstein [1]. The linear profile is not defined in [1] but I have added it anyway.

Regarding the implementation details, I didn't find a explicit model which calculates the S-Parameters of a tapered line, so I decided to tackle the problem as follows:

  1. The transmission line is divided into differential slots (the size of each slot is set equal to 3e-3*lambda), where lambda is the wavelength. This means that the size of the slot depends on the frequency of the S-param simulation.
  2. Every slot is supposed to have uniform characteristic impedance (because its length is much smaller that than wavelength), so the ABCD matrix can be calculated as:

ABCD = [cosh(gamma·dl), Zi·sinh(gamma·dl); sinh(gamma·dl)/Zi, cosh(gamma·dl)]

where gamma = alpha+j·beta is the complex propagation constant and dl is the length of the slots.
https://en.wikipedia.org/wiki/Two-port_network#Table_of_transmission_parameters

  1. According to the cascading property of the ABCD parameters, the overall ABCD matrix can be calculated as product of the ABCD matrices of the slots.

  2. ABCD is converted into S/Y parameters.

I reckon that this model works, indeed the |S11| perfectly matches the reflection coefficient plots at [1]. Here are the results:

a) Impedance profile
a.1) Qucs taper line model
zprofile
a.2) Pozar [1]
pozarzprofile

b) Exponential taper:

b.1) S11
exponentials11
b.2) S21
exponentials21

c) Triangular taper:

c.1) S11
triangulars11

c.2) S21
triangulars21

d) Klopfenstein taper:

d.1) S11
klopfensteins11

d.2) S21
klopfensteins21

As mentioned above, the |S11| seems to mimic the reflection coefficient plot given in [1]:
pozars11

Please, notice that the length of the line is l=150mm, so freq = 1e9 <=> beta·l = pi

The plots above are taken from (this schematic)[https://gist.github.com/andresmmera/7b1b7a96d68ec0b8741c]. I have also written an Octave script in order to verify the model.

[1] Microwave engineering. David M Pozar. 4th Edition. Pages 261-267

1) It was created the component entry for an exponentially tappered
transmission line.
2) The S-parameter simulation was implemented by discretizing the line
into differential slots and calculating the global S-param matrix as the
product of the T matrix of the differential slots

References:
[1] Microwave engineering. David Pozar. 4th Edition. Pg 262-263
[2] Investigation on Tapered multiple microstrip lines for VLSI
circuits. Mehalic, Chan, Mittra. IEEE MTT-S Digest 1988
It was added a general model for tapered lines. It is based on dividing
the entire transmission line into differential slots (under the
assumption that the characteristic impedance in each slot is constant).
The ABCD matrix of every slot is calculated so the overall ABCD is
trivial to obtain.

Finally, the S/Y-parameters are calculated.

So far, it supports exponential, linear and triangular impedance
profiles.

References:
[1] Microwave engineering. David M Pozar. John Wiley and Sons. 4th
edition. Pages 261-267
It was implemented the modified Bessel function I_{\alpha}(x). However,
it is needed a much more efficient implementation. Currently, the
Klopfenstein taper takes about 50 min to calculate and the results are
not accurated enough.
The Klopfenstein taper is now fully working. The cpu time was improved
by implementing a more efficient modified Bessel function (See
http://people.math.sfu.ca/~cbm/aands/page_375.htm)
@felix-salfelder
Copy link
Member

nice. this only extends qucs(ator), apart from minimal changes necessary to register the code. merging just now should not cause any problem.

however, please

  • remove accidentally added qucs-core/build-aux/test-driver
  • consider squashing the commits into one

thanks.

@ra3xdh
Copy link
Contributor

ra3xdh commented Sep 26, 2015

@andresmmera , Sorry, I have not yet tested your code, but it looks good. I think, it could be merged after release. I don't recommend to merge any new feature right before release.

@felix-salfelder
Copy link
Member

@ra3xdh this is not a feature, just a component.

yes, we should make qucs more flexible, so components can be added without tampering with the code base. we are not there yet, but i would certainly appreciate your help.

EDIT: @andresmmera by writing "just" i do not intend to de-emphasize your effort.

@andresmmera
Copy link
Contributor Author

Vadim, I am aware that this kind of PR should not be merged in this
release. I opened it just to let you know what I have done.

Felix, I will squash the commits tomorrow.

Thank you
On Sep 26, 2015 5:52 PM, "Vadim Kusnetsov" notifications@github.com wrote:

@andresmmera https://github.com/andresmmera , Sorry, I have not yet
tested your code, but it looks good. I think, it could be merged after
release. I don't recommend to merge any new feature right before
release.


Reply to this email directly or view it on GitHub
#380 (comment).

@felix-salfelder
Copy link
Member

if you don't want to see this merged, please attach a milestone > 0.0.19.

@in3otd
Copy link
Contributor

in3otd commented Sep 26, 2015

Great work! I was actually in need of doing some simulations with tapered lines and thought it was a pity that Qucs did not support them. Nice to see that you already did all the work ! 😁

I tried to look at all the code, but I'll need some time more to dig into all the details. I have some first comments:

  • the tapered line S-parameters calculation is not correct. There you compute the tapered line S-parameter from the ABCD matrix by normalizing to its input and output design impedances, Z1 and Z2. Actually, the S-parameters matrix needed there by the simulator has to be normalized to the internal simulator reference impedance, z0. Note that you did obtain the "expected" graph with your simulations, but you had 50 ohm input and output impedances ! These graphs should be obtained when the input and output impedances are Z1 and Z2. Or, see how the low-frequency S21 in your test schematic does not tend to 1.0, yet the line should be a short-circuit at DC so there should be no (reflection) losses when the input and output impedances are the same.
  • the Alpha parameter, the attenuation factor per length in 1/m, for the other transmission lines models in Qucs is positive, non negative, for lossy lines. It's a matter of conventions, similarly to the "return loss", where someone uses a minus sign is used and others not. I suggest to align with the other Qucs transmission lines and change its sign.
  • it would be nice to handle also the case Z1 > Z2 or at least issue a warning in this case and not fail silently.
  • here a couple of real() and a conj() are missing, but that will make a difference only for complex Z1 and/or Z2.
    Note that qucsator already has a function fot this conversion, you could use a code like
  matrix tmpABCD(2);
  matrix tmpS(2);

  tmpABCD.set(0,0, A);
  tmpABCD.set(0,1, B);
  tmpABCD.set(1,0, C);
  tmpABCD.set(1,1, D);
  tmpS = qucs::atos(tmpABCD, z0, z0);
  S11 = tmpS.get(0,0);
  S12 = tmpS.get(0,1);
  S21 = tmpS.get(1,0);
  S22 = tmpS.get(1,1);

(of course it will be even nicer if the ABCD matrix was a member of the taperedline() class.

  • in the integrations here and here I think you are not taking into account correctly the contributions from the endpoints, see the composite trapezoidal formula here . (makes only a small difference for the small steps you are using)
  • regarding the test-driver probably .gitignore should be updated to ignore this file also.
  • the Bessel I1(x) calculation works fine, yet you could have obtained it as imag(J1(ix)) and J1(x) is already in Qucs as qucs::jn(alpha, x) (works also for complex x). I have tried and your function is actually still slightly faster so there is no real need to change it 😁 OTOH, might be nice to make I1(x) available with the other functions in the the equation subsystem.
  • for the AC simulation, y12 and y22 are not correctly used where they should here
  • there is still something not correct for the ABCD matrix, the asymptotic value for S(2,1) seems not correct to me, will hopefully take a look in the next days
  • while your nonuniform line calculations done by subdividing in small sections and computing the overall ABCD matrix are in principle correct for the most general case, I'm not sure that it is really needed for the 4 tapers we can have. At least for the Klopfenstein the shape of Γ (f) is known, so I think you should be able to compute the complete S-parameters matrix (at least for the lossless case), which will be normalized to Z1 and Z2 as mentioned above, and renormalize it to z0 (50 ohm) for both inputs and outputs. This matrix could then be used directly in calcSP()

@andresmmera
Copy link
Contributor Author

@in3otd Many thanks for your comments!

    the tapered line S-parameters calculation is not correct. There you compute the tapered line S-parameter from the ABCD matrix by normalizing to its input and output design impedances, Z1 and Z2. Actually, the S-parameters matrix needed there by the simulator has to be normalized to the internal simulator reference impedance, z0. Note that you did obtain the "expected" graph with your simulations, but you had 50 ohm input and output impedances ! These graphs should be obtained when the input and output impedances are Z1 and Z2. Or, see how the low-frequency S21 in your test schematic does not tend to 1.0, yet the line should be a short-circuit at DC so there should be no (reflection) losses when the input and output impedances are the same.

Ok, I was not aware of that. I'll try to fix it

    the Alpha parameter, the attenuation factor per length in 1/m, for the other transmission lines models in Qucs is positive, non negative, for lossy lines. It's a matter of conventions, similarly to the "return loss", where someone uses a minus sign is used and others not. I suggest to align with the other Qucs transmission lines and change its sign.

The attenuation factor was set to -30dB just to check an almost lossless case. No problem, I'll set it to 0dB to be coherent with the other components.

    it would be nice to handle also the case Z1 > Z2 or at least issue a warning in this case and not fail silently.

Sure, I think it shouldn't be difficult :-)

    here a couple of real() and a conj() are missing, but that will make a difference only for complex Z1 and/or Z2.

Uhm, I'm 98% sure that Qucs cannot handle complex ports. Indeed, I remember that here (#339) I had to use RLC components to synthesize a (narrowband) complex load. In that case, I wouldn't be necessary to use real() and conj()

in the integrations here and here I think you are not taking into account correctly the contributions from the endpoints, see the composite trapezoidal formula here . (makes only a small difference for the small steps you are using)

To be honest, I haven't heard about this kind of interpolation ever :-) It's good to learn these things. Thanks!
(Update: Hum, I'm remembering this... trapezoidal interpolation, Simpson's rule... I had completely forgotten them)

    for the AC simulation, y12 and y22 are not correctly used where they should here

Yes... it happened because I'm prone to using the copying and pasting technique...

    while your nonuniform line calculations done by subdividing in small sections and computing the overall ABCD matrix are in principle correct for the most general case, I'm not sure that it is really needed for the 4 tapers we can have. At least for the Klopfenstein the shape of Γ (f) is known, so I think you should be able to compute the complete S-parameters matrix (at least for the lossless case), which will be normalized to Z1 and Z2 as mentioned above, and renormalize it to z0 (50 ohm) for both inputs and outputs. This matrix could then be used directly in calcSP()

Yes, Γ (f) are known for all the tapers. (I've been digging in this before I started coding). From the conservation energy principle you have:

P_in = P_reflected + P_transmitted + P_dissipated = |S11|^2 + |S21|^2 + alpha*L

where P denotes power, alpha is the attenuation coefficient (in natural units) and L is the length of the line.

Γ (f)=|S11|, P_in and alpha are known, then |S21| can be calculated. But,... what about the phase of S11 and S21? I couldn't find the way to calculate them... :-)
If you have some idea about how to get the phases, please let me know. It would speed up the simulation (a lot)

@felix-salfelder I reckon that I can't add a milestone. Probably, only you (@in3otd, @ra3xdh, @guitorri, you and maybe someone more) can add these tags.

@felix-salfelder felix-salfelder added this to the 0.0.20 milestone Sep 27, 2015
1) The bug on the AC simulation was corrected.
2) ABCD->S conversion was being normalized to the input/output
impedances when they should be normalized to the internal reference
impedance.
3) The attenuation coefficient was set to 0 dB by default
@in3otd
Copy link
Contributor

in3otd commented Sep 27, 2015

The attenuation factor was set to -30dB just to check an almost lossless case. No problem, I'll set it to 0dB to be coherent with the other components.

What I meant to say is that for the other lines in Qucs, if you want to specify a loss of 30 dB/m you have to set Alpha to 30 dB not -30 dB. Anyway, having a default of 0 dB is good.
Actually, I have just checked again, and while the sign convention above seems to be true, Edit: (see below) there is a bug (or two) in the losses formula for the tline in Qucs... 😕

Uhm, I'm 98% sure that Qucs cannot handle complex ports.

Yes, make that 100%. Currently it does not handle complex reference impedances for the ports. I just wanted to point out that the formulas you wrote did not agree 100% with the reference you cited in the code. Sure, you could just consider the real reference impedance case here.
BTW, being able to handle complex reference impedances will be nice, but I feel it will be difficult to implement properly. As you are likely aware, there are(at least) two "schools" regarding complex reference S-parameters definition, the "power waves" one and the "pseudo waves/traveling waves" one (see here and here) . While the traveling wave definition is the "rightest" at least one widespread commercial tool implements (or implemented ?) the "power wave" one, so we will likely need to support both. I'll leave opening this can of worms for later...

To be honest, I haven't heard about this kind of interpolation ever :-) It's good to learn these things.

BTW, I did some comparison (with Octave) and the integration error is not that small currently. qucsator has already an integration function, you might be able to reuse that.

But,... what about the phase of S11 and S21? I couldn't find the way to calculate them... :-)

uhm. did not dig into this, but for every causal component the real and imaginary parts are related to each other by the Hilbert transform, this should be enough to obtain the phase, with some work...

To simplify the calculations, do you think that the the tapered line impedance profile Zi could be precomputed (in the initSP() and stored to be reused in the ABCD matrix calculations? In this way we could avoid recomputing it for every frequency. This may mean a different way of handling the lsteps in calcABCDparams() but I have a feeling that it could be doable...

@andresmmera
Copy link
Contributor Author

@in3otd Unfortunately, this for loop https://github.com/andresmmera/qucs/blob/TaperedLines/qucs-core/src/components/taperedline.cpp#L63 is an iterative matrix product rather than an integration, so I think that conventional numerical integration techniques cannot be applied. Probably, it'd be possible to use some kind of interpolation (polinomial, spline,...) to find the coefficients of 'intermediate' matrices and improve the final results. On the other hand, I think that the difference between two consecutive matrices is small enough to get accurate results. I mean: ABCD(i) = ABCD(i-1) + E; and |E| -> 0

Concerning the phases problem, as you said, it is possible to obtain the real and imaginary parts are related to each other by the Hilbert transform. However, I (we) don't know the tapered line response H(omega) in the Hilbert domain. Indeed, if we get it, we can also derive the impulsive response, h(t) and implement transient simulation (this would be beautiful :-) )

Regarding the possibility of precalculating the impedance profile, it sounds good. It implies some code changes, but as you said, it is doable :-) Thank you

@andresmmera
Copy link
Contributor Author

I hope I'm not being annoying, just a question:

Can a component know the range of frequencies in the S-parameter simulation or is it something 'external'?

In order to pre-calculate the impedance profile, it is necessary to know the highest frequency so as to define a length step for the tapered line analysis. I mean, the impedance profile requires more samples as frequency increases, so it makes sense to sample the impedance profile at a high rate in initSP() and then 'decimate' such profile at lower frequencies.

@in3otd
Copy link
Contributor

in3otd commented Sep 27, 2015

found the problem in the S21 values; the alpha you need to use for the attenuation constant needs to be in neper/m , not in linear units. The values you get from here is in linear units (automatically converted from dB, if you used that; i.e. if you enter '0dB' alpha will be 1)
So you need to convert the linear units to neper/m, with the formula alpha = std::log(alpha) / 2.0 and now alpha has the right units to use in the transmission lines computations for the ABCD matrix. In this way it's consistent with the usage done elsewhere and the S21 are correct.
BTW, there is no bug in tline, I was mistaken since when specifying the length as 1 m it is (correctly) taken as '1 millimeter' and not as '1 meter'. I do not use meter-length lines often 😁 .

Regarding the ABCD matrices products, yes, it's not a quadrature, I was sloppy in the description because I wanted to make a common comment but there what I think should be done is taking the Zi at the midpoint and not at the end of the segment (so Zi(n*lstep+lstep/2)), so the start and endpoints may then need to be treated separately.

I hope I'm not being annoying, just a question:

No problem, here or on qucs-devel is the right place to ask questions...

Can a component know the range of frequencies in the S-parameter simulation or is it something 'external'?

uhm, at present it seems only the solvers (spsolver() here) knows the simulation range (it's in the sweep swp variable, https://github.com/Qucs/qucs/blob/master/qucs-core/src/spsolver.h#L78). But I think being able to do some preliminary calculations could be useful in general. We will have the same kind of issue if we will ever support time-domain simulation of arbitrary frequency-domain-defined components.

@in3otd
Copy link
Contributor

in3otd commented Sep 27, 2015

(pssst, @andresmmera , don't tell anyone, but the phase of Γ(f) is in Pozar's book, same pages you cited already, and its value is... surprising! 😁 . I have implemented the computation of the S-matrix for the Klopfenstein case based on this, works beautifully and, of course, very fast)

export

@andresmmera
Copy link
Contributor Author

Uhm, Pozar gives the Γ(f) formula (i.e. it has module and phase). How could you get the phase of S21 from there? You have just left me scratching my head...

The attenuation coefficient has been corrected according to Claudio's
explanation.
The integration step has been also corrected, although, hopefully, the
integration will no longer needed  soon :)
@in3otd
Copy link
Contributor

in3otd commented Sep 28, 2015

Yes, I didn't say that I had to make an additional assumption, to fix the phase of S22. I saw that, numerically, S22=S11* (when the S-parameters reference impedances are Z1 and Z2) but I do not know if this holds for all the tapers in general.

Anyway, looking at the literature I saw that, not surprisingly, someone else had the same problem of efficiently finding the impedance along a Klopfenstein taper before and almost 50 years ago a simple recursive formula was found. I also realized that you do not need to subdivide the taper depending on the wavelength; the important thing is not how much phase shift each section introduces but how much the actual impedance changes across the sections. This means you can use a fixed number of sections, depending on how big the overall impedance step is, but it seems between 10 and 50 section is enough and this also helps in speeding up things.

I'll send you a PR with these latest modifications.

BTW, your current code does not work here, I get a malloc(): memory corruption error message, I guess the integration routine is not working well/not used properly but did not investigate as it won't be needed anymore.

@andresmmera
Copy link
Contributor Author

Yes, I didn't say that I had to make an additional assumption, to fix the phase of S22. I saw that, numerically, S22=S11* (when the S-parameters reference impedances are Z1 and Z2) but I do not know if this hold for all the tapers in general.

Uhm, I'm a little bit confused, transmission lines should be reciprocal devices since they are made of passive and isotropic materials, so according to Eq 4.48 (Pozar's book*, 4th Edition, Page 182), S = S^t => S11 = S22

BTW, your current code does not work here, I get a malloc(): memory corruption error message, I guess the integration routine is not working well/not used properly but did not investigate as it won't be needed anymore.

Ok, I'll take a look as soon as possible. I've just checked the last commit: It doesn't crash here, but I'm getting weird results. Probably, I made some foolish mistake (I was too excited because of your last comment) :-)

@in3otd
Copy link
Contributor

in3otd commented Sep 28, 2015

transmission lines should be reciprocal devices since they are made of passive and isotropic materials, so according to Eq 4.48 (Pozar's book*, 4th Edition, Page 182), S = S^t => S11 = S22

Nope, S = [S]t actually implies S12 = S21
For a reciprocal and also lossless device you have
S12 = S21
|S11| = |S22|
|S11|2 + |S21|2=1
S11S21* +S21S22* = 0
which gives some constraints also on the phases, see also here .

@andresmmera
Copy link
Contributor Author

andresmmera commented Sep 28, 2015 via email

in3otd and others added 3 commits September 29, 2015 00:17
Implemented the simple recursive formula from Grossberg, Proc. IEEE
1968, pp.1629-1630 to compute the phi(x, A) function needed for
the Klopfenstein taper calculations.
Changed the ABCD matrix to use a constant number of sections, instead
of using constant fraction of wavelength sections, since the number
of sections needed depends mainly on the impedance change across
the section, not on its length.
Corrected the ABCD matrix to Y-parameters conversion (had a missing
negative sign). Now using the a Qucs function to set the S-matrix.
Speed-up Klopfenstein taper and ABCD matrix calculations
1) If the impedance at port 1 is bigger than the impedance at port 2,
the model sweeps (Z1, Z2), and calculates the S matrix as usual.
2) The calculation of the impedance profile was moved to initSP(). This
avoids redundant function calls.
@in3otd
Copy link
Contributor

in3otd commented Sep 29, 2015

good to move the impedance profile calculation, but it should be in a separate function, that needs to be called by both initSP() and initAC(); at present the AC simulation does not work anymore.

I just saw that the include guard in taperedline.h is not following the proper naming convention, see #268 and #264 for further details (in short, you should use TAPEREDLINE_H)

1) It was created a specific function for calculating impedance profile
2) The include guard was also fixed so as to be coherent with the naming
convention
@andresmmera
Copy link
Contributor Author

Ok, I've fixed both issues.
I think that once this model becomes stable, it should be rather straightforward to write a specific model for a microstrip (maybe CPW too?) implementation, shouldn't it?.

@andresmmera
Copy link
Contributor Author

Glups, I'm sorry :)
I've just pushed the changes.
Thank you for your help

@ra3xdh
Copy link
Contributor

ra3xdh commented Jan 31, 2017

This PR will be the next candidate for merging in the develop after the #356. @andresmmera , Have you time to rebase and retarget this branch to develop until the weekend?

@andresmmera
Copy link
Contributor Author

andresmmera commented Jan 31, 2017 via email

@andresmmera
Copy link
Contributor Author

I'm closing this PR because I've just rebased it in #657

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants