mhuser / scikit-rf

RF and Microwave Engineering Scikit
http://scikit-rf.org
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

Factor 2 Removal Implications #2

Open denzchoe opened 2 years ago

denzchoe commented 2 years ago

Just an Observation Posting

Background

  1. The DUT & fdf (fixture dut fixture) that I had simulated & measured has about >15 dB return loss @20 GHz.

  2. The s2xthru (fixture to fixture) is < 20 dB @30 GHz.

  3. You are right, I should get rid of these factor 2. In the example they do not seems to have many effect here, probably because the line is quite uniform..


The Observation

disclaimer, may not be conclusive

@mhuser, while I am running some test for NZC; , using various EM Simulated objects, it appears that for NZC, by including the factor 2, my deembedded results are much closer to the source DUT. (Both Time and Frequency Domain plots) After you have removed the factor 2 (in scikit-rf 0.22.1), my deembedded results deviates away from the source DUT.

I begin looking at it ever since my de-embedded measured results from your old fork code is different from the currently merged code

Seems like it appears that old fork code is more accurate? Perhaps it only affects my current DUT that is poorly matched whether it is simulated / measured scenario?

The changes I made is in reflection of your old fork code. See link below for comparison https://github.com/denzchoe/scikit-rf/compare/denzchoe-to-compare-only...denzchoe-vs-only1?diff=split


Will fill in more details much later. Let me dig a little further. Posting to report on Observation only

mhuser commented 2 years ago

Hello @denzchoe Thank you for posting this observation !

I am still quite certain about this factor 2 removal for this reason : in Python implementation, IRFFT is used, that take only the positive frequency coefficients and assume another half which is complex conjugate. This return about twice the number of points as initial coefficient array. I felt I need to add this 2 coefficient because of that. However, Matlab used a makeSymetric function, which led to the same number of point. The irfft of S21 is used to determine the length of the line in sample by finding the peak (what an elegant way!) and impedance in step response at corresponding position.

Nevertheless, it is well possible that there could be something like an incorrect fft shift or other that cause length or impedance to be wrongly determined and that would have escaped testing with very uniform and well matched examples.

I would advise to do a sanity check with the latest AICC tools that use the Matlab code to see if the results are closer to old or current Python Implementation.

It would also be of interest to plot step response of 2xthru, fdf, dut, fixture on the same plot (so we can see if fixture looks like half 2xthru or not).

Keep brave !

mhuser commented 2 years ago

Side note : on the scikit google group, there was a mention of this paper by Jason Ellison, Stefen B. Smith, Sedig Agili, quite recently. But I did not have the chance to access to it yet.

mhuser commented 2 years ago

Hello @denzchoe I post here another observation that may be of interest. By using 2xthru deembedding to remove a 100 mm CPWG line from a 200mm CPWG line to remove connectors and launches effects, I have seen that 180° jumps appeared in the insertion loss phase of sides models.

I have located the portion of the code that cause it, but it seems coherent with the matlab source. https://github.com/scikit-rf/scikit-rf/blob/f9d0c6f1c5b8d271181ca40e3e892f73a4b599d4/skrf/calibration/deembedding.py#L1153-L1161 https://github.com/scikit-rf/scikit-rf/blob/f9d0c6f1c5b8d271181ca40e3e892f73a4b599d4/skrf/calibration/deembedding.py#L1164-L1172

image

mhuser commented 2 years ago

The phase jump issue is related into choosing the proper solution to sqrt. The sign of the solution should change at each e10 or e01 phase transition but I wonder if I have some artifact that trigger excess transitions.

mhuser commented 2 years ago

image

Got it : small positive pikes due to calibration error and measurement noise are causing wrong transitions to be triggered

denzchoe commented 2 years ago

saw the latest fix @mhuser; Thanks for discovering and detailing it. Very insightful!

denzchoe commented 2 years ago

Ok, I am beginning to understand the limits of this deembedding method now. I was wondering why it works on some of my measured structure, and not in one of them. So it appears that there are some criteria to hit. And 1 of them is this.. (see fig below) Without a length long enough, the TDR will extract the incorrect Impedance value from the 2xthru midpoint. Looks like some background to it is required.

image

Image source, Page 34, 10.1109/IEEESTD.2021.9316329

mhuser commented 2 years ago

Nice finding ! Sounds the pitfall with TDR/ifft based measurements : to see small details, upper frequency bound should be increased but soon hit the limit of the instrument.

I have not added the ability to use the Z-transform instead of fft. See this ieeep370 commit description. They speak of "short structures" with few points in TDR. Could this help with you deembedding challenge ?

mhuser commented 2 years ago

Z-computation start at this line.

denzchoe commented 2 years ago

Dr Hackl also had an application where the maximum available frequency was high enough to achieve one TDR point during construction of the errorbox reflection term. This led to integrating a Z-transform version of the code for very small structures. The specific application for this is on die de-embedding of interconnects. However, it can be useful at low frequencies with a relatively short 2x-thru (< 15mm ).

Oh, hmm my s2xthru structure total length is 25 mm in length. Not sure how useful it can be for me.. But certainly, the theory is applicable for very short die structures..


Back to the 2xthru midpoint, by altering the z11x value here, I managed to get a more causal data of my side1 & side2 output.

        dcs11 = self.DC(s11,f)
        t11 = np.fft.fftshift(np.fft.irfft(concatenate(([dcs11], s11)), axis=0), axes=0)
        step11 = self.makeStep(t11)
        z11 = -self.z0 * (step11 + 1) / (step11 - 1)
        z11x = z11[x]        # edit here via debug mode on Spyder

        temp = Network(frequency = self.s2xthru.frequency, s = s, z0 = self.z0)
        temp.renormalize(z11x)

causality as described in [1, Page 5 - 8] and [2]


Let's see 1 example. The following example is from a Measured s2xthru, fdf. All deembedding done using method IEEEP370_SE_NZC_2xThru Simulated dut is also shown as reference. Also contain plots from AICC tools (vBeta & vR1)

Example 1: Run code as default. z11xextracted at midpoint was 53.3 Ω

Figure_1

Figure_1

Figure_2

Figure_2

Observations: i. constructed side1 & side2 has high S11 @low frequency ii. AICC_vR1.s11 also has high S11. I am not sure why this is happening? deembed_dut.s11 is higher than sim_dut iii. Midpoint of s2xthru in TDR iv. side1 & side2 (terminating-side impedance is not at 50 Ω anymore in Time Domain)

Example 2: Run code at debug mode. z11x forced to be 49 Ω

Figure_3

Figure_3

Figure_4

Figure_4

Observations: v. constructed side1 & side2 has low S11 now @low frequency vi. deembed_dut.s11 is similar to sim_dut < 30 GHz. I say this is acceptable enough for me. vii. side1 & side2 (terminating-side impedance is now closest to 50 Ω in Time Domain) viii. Electrical Length of AICC tools doesn't look correct. Again I don't know why this is happening.

So from what I see; my constructed side1 and side2 can be more causal if I tweak the value at z11x?? And if I leave it at default, the side1 and side2 S-parameters look funny in both Frequency and Time Domain. I am not even sure how do I explain this phenomenon or why this is even happening. Just sharing what I am observing.

Additionally, I cross-verify my P370 deembed_dut with a Commercial De-embedding Software. The results generated from the Commercial Software is closer to sim_dut (i.e.: is like when I tweak z11x)

Here is how the s2xthru looks like in CAD. Unlike the regular examples in P370, this is a coaxial based fixture with discontinuities at different joints.


image

mhuser commented 2 years ago

Thank you for reporting this ! If you share your 2xthru measurement (maybe per email), I am interested to look at the impedance shape after inverse fft.

mhuser commented 2 years ago

So from what I see; my constructed side1 and side2 can be more causal if I tweak the value at z11x

This could be the case if the algorithm failed to compute dc points or midpoints. I propose to add a verbose parameter to the method for advanced users that would open a plot to see how the x length and z11x parameters and if a wrong dc point is not inserting a slope in the curves.

Unlike the regular examples in P370, this is a coaxial-based fixture

This should give even better results: all the fields are in the same homogenous dielectric for a given section of the line, not like microstrip with some going in the air and some in the dielectric.

mhuser commented 2 years ago

Example of verbose = True diagnostic for 25mm PTFE dielectric coax:

import skrf
from skrf.media import Coaxial
from skrf.calibration import IEEEP370_SE_NZC_2xThru

f = skrf.Frequency(0.005, 50, 10000, unit='GHz')

coax = Coaxial(frequency=f, Dint=1e-3, Dout=2e-3, epsilon_r = 2.1, z0 = 50.)
line = coax.line(d = 25.0e-3, unit = 'm', embed = True, z0 = coax.Z0)
line.name = '2x-Thru'

dm = IEEEP370_SE_NZC_2xThru(dummy_2xthru = line, name = '2xthru', verbose = True) 

image

On this artificial example, there is a very decent points number in the time domain and the impedance is nice flat.

mhuser commented 2 years ago

Hello @denzchoe thank you a lot for the meas_s2xthru.s2p!

I exercised the verbose = True parameter that has been merged yesterday and here is the result. The image of the artifact is pasted below for reference.: image

mhuser commented 2 years ago

I tried this with f_stop values 45 or 30 in GHz:

# load data
s2xthru = rf.Network('meas_s2xthru.s2p')

# new frequency axis
f_start = 0.01 # GHz
f_stop  = 45.  # GHz
n       = int(f_stop / f_start)
f_new   = rf.Frequency(f_start, f_stop, n, 'GHz')

# interpolate
s2xthru_i = s2xthru.interpolate(f_new)

# diagnostic
dm = IEEEP370_SE_NZC_2xThru(dummy_2xthru = s2xthru_i, name = '2xthru', verbose = True) 

f_stop = 30 image f_stop = 45 image

if the frequency is reduced furthermore, the z11x value converge to 51 ohm.

mhuser commented 2 years ago

Hello @denzchoe I had some time to think about your deembedding challenge.

In most cases, the delay due to the physical length of the 2xthru is probably not an integer multiple of time-domain sampling time. This may cause a ± 1 sample error in midpoint determination. For cases with a uniform impedance zone in the middle (such as a segment of transmission line), this should not cause an unacceptable error.

For this reason, the above issue is probably not due to interpolation to a harmonic sweep, the original measurement being very close to this with only one point more.

It would remain interesting for comparison to see if simulated 2x-Thru also exhibits such an impedance variation in the middle of the fixtures joint (without doing too much hypothesis for the reason of this discontinuity).

Maybe the ZC algorithm has more deembedding power for such a case : it offer bandwidth limiting, Nyquist rate enforcement (caring for the delay not being an integer number in my understanding) and offers a verbose mode showing how it cascades chunks of various impedance. In the other hand it looks like a more 'dangerous' deembedding being able to change slightly the data.

denzchoe commented 2 years ago

Thank you for taking your time to think about this.. Yes it is indeed a demanding challenge.

It would remain interesting for comparison to see if simulated 2x-Thru also exhibits such an impedance variation in the middle of the fixtures joint (without doing too much hypothesis for the reason of this discontinuity).

I can provide you with this. Will send to your email.

In most cases, the delay due to the physical length of the 2xthru is probably not an integer multiple of time-domain sampling time. This may cause a ± 1 sample error in midpoint determination. For cases with a uniform impedance zone in the middle (such as a segment of transmission line), this should not cause an unacceptable error.

This is definitely the most reasonable explanation. Coming from the perspective of physical dimensions; the time-constant of 50 GHz signal will be at 20 ps long. The wavelength of that frequency at Er = 1.0, is about 236 mils. The length of the small discontinuity in the midpoint is about 28 mils. A 50 GHz VNA does not have the resolution to capture the z11x value accurately. But that doesn't mean it failed. (I think). Because I have tested it on a the same simulated structure; and every-time I picked the z11x that output side1 & side2 that looks reasonable; the deembed dut output comes out closest to the simulated_dut.

  • Legacy x//2 behaviour was picking impedance on the far left, an impedance of almost 50 ohm was always taken.

I guess the reason why Legacy code version appears to be accurate previously was because of the new observations we made recently.

  • At least, we can see that the z11x choice is difficult in this case. I think the overall 2x-Thru is long enough, but the details of the interface at the middle are very small which make the x choice tricky. Maybe it could be interesting to have an option to manually force impedance.

I think it does. I think we should allow this option accessible. What do you think?

denzchoe commented 2 years ago

Maybe the ZC algorithm has more deembedding power for such a case : it offer bandwidth limiting, Nyquist rate enforcement (caring for the delay not being an integer number in my understanding) and offers a verbose mode showing how it cascades chunks of various impedance. In the other hand it looks like a more 'dangerous' deembedding being able to change slightly the data.

This is exactly what I am seeing. At lower frequencies; it seems accurate. However, because my actual fdf structure has a resonant that begins at 32 GHz, I believe the makeTL function cannot accurately recreate the txline. From time-domain, there is an impedance captured (seeing how makeTL is dependable on getz which extracts Impedance from Time-Domain). But in frequency-domain, some kind of parasitic modes probably interfered the dominant TEM mode of the Coaxial at 32 GHz. So when makeTL function executes, it did not accurately recreate the actual txline. Thus after deembedding, the deembed dut goes wonky after 30 GHz. It did changed my data as you have predicted. Perhaps ZC algorithm is not suitable for my use-case. But certainly for others..

mhuser commented 2 years ago
  • At least, we can see that the z11x choice is difficult in this case. I think the overall 2x-Thru is long enough, but the details of the interface at the middle are very small which make the x choice tricky. Maybe it could be interesting to have an option to manually force impedance.

I think it does. I think we should allow this option accessible. What do you think?

Yes I think it is worth to try! Done in https://github.com/scikit-rf/scikit-rf/pull/708

forced_z11x = None (default behaviour): image

forced_z11x = 48: image

Comparison: image

mhuser commented 2 years ago

Hello @denzchoe , Thank you a lot for your sim_2xthru.s2p file ! Measurement: image

Simulation: image

The simulation is consistent with the measurement in exhibiting a non-uniform impedance around mid-point. I think it is a characteristic of this artifact.

image

forced_z11x = 49 looks like a magic value, providing a good match at low frequency.

mhuser commented 2 years ago

I have tweaked an old optimization code that tries to build a model with a low S11 at the lower frequency range. However, I still wonder if there is a sense doing this, but I paste it here for reference. below code output : z11x_opt = 49.035 ohm, close to your results.

from matplotlib import pyplot as plt
import skrf as rf
from skrf.calibration import IEEEP370_SE_NZC_2xThru
from scipy.optimize import minimize

# load data
s2xthru = rf.Network('meas_s2xthru.s2p')

# build closest harmonic sweep and interpolate
f_start = 0.01 # GHz
f_stop = 50. # GHz
n = int(f_stop / f_start)
n_lim = n // 10
fnew = rf.Frequency(f_start, f_stop, n, 'GHz')
s2xthru_i = s2xthru.interpolate(fnew, basis = 't', coords = 'cart', kind = 'cubic')

def model(x, n_lim):
    dm = IEEEP370_SE_NZC_2xThru(dummy_2xthru = s2xthru_i, name = '2xthru',
                                forced_z11x = x[0])
    delta = dm.s_side1.s_mag[:n_lim, 0, 0]
    return sum(delta**2)

x11x_0 = [50.]
opt = res = minimize(model, x11x_0, args = (n_lim), bounds=[(40., 60.)])
z11x_opt = opt.x[0]
print('z11x_opt = {:.3f} ohm'.format(z11x_opt))

dm = IEEEP370_SE_NZC_2xThru(dummy_2xthru = s2xthru_i, name = '2xthru',
                            forced_z11x = z11x_opt)
fix1 = dm.s_side1        

plt.figure()
fix1.plot_s_db(0, 0)
denzchoe commented 2 years ago

Yes @mhuser ; that optimization code would be useful. I was trying to get one out myself (yet to start). But looks like yours came in most handy. I am not sure how useful it will be either, In the terms of causality and the sense of it. Bookmarking this for future usage. Let me test it on more use-cases. I have plenty of NISTMultilineTRL lines in the past. I can check how accurate NZC is against all of them when I find the time to do it later. Optimization would speed up the process.

Close the Issue?

mhuser commented 2 years ago

I have plenty of NISTMultilineTRL lines in the past. I can check how accurate NZC is against all of them when I find the time to do it later.

Sounds great!

Close the Issue?

I think we now have understood why the results were better with the factor 2 on the length x: the algorithm was picking z11x in a zone of flat 50 ohm impedance on the left.

It followed we found useful features to add to the NZC 2xThru method such as verbose, forced_z11x and use_z_instead_fft that help to investigate more on specific cases or get insights of internal process!

This help to make the code more robust, tested and better understood.

denzchoe commented 1 year ago
from matplotlib import pyplot as plt
import skrf as rf
from skrf.calibration import IEEEP370_SE_NZC_2xThru
from scipy.optimize import minimize

# load data
s2xthru = rf.Network('meas_s2xthru.s2p')

# build closest harmonic sweep and interpolate
f_start = 0.01 # GHz
f_stop = 50. # GHz
n = int(f_stop / f_start)
n_lim = n // 10
fnew = rf.Frequency(f_start, f_stop, n, 'GHz')
s2xthru_i = s2xthru.interpolate(fnew, basis = 't', coords = 'cart', kind = 'cubic')

def model(x, n_lim):
    dm = IEEEP370_SE_NZC_2xThru(dummy_2xthru = s2xthru_i, name = '2xthru',
                                forced_z11x = x[0])
    delta = dm.s_side1.s_mag[:n_lim, 0, 0]
    return sum(delta**2)

x11x_0 = [50.]
opt = res = minimize(model, x11x_0, args = (n_lim), bounds=[(40., 60.)])
z11x_opt = opt.x[0]
print('z11x_opt = {:.3f} ohm'.format(z11x_opt))

dm = IEEEP370_SE_NZC_2xThru(dummy_2xthru = s2xthru_i, name = '2xthru',
                            forced_z11x = z11x_opt)
fix1 = dm.s_side1        

plt.figure()
fix1.plot_s_db(0, 0)


Hmm... something is not right with this Optimization script. I am constantly getting the answer of whatever input is over here x11x_0 = [50.]

mhuser commented 1 year ago

Hmm... something is not right with this Optimization script. I am constantly getting the answer of whatever input is over here x11x_0 = [50.]

Could you try to replace forced_z11xby forced_z0_line twice ?

denzchoe commented 1 year ago

damnit, rookie mistake.. Forgot that the library changed the name of the parameter.. 😅.

Works now!

mhuser commented 1 year ago

😉 Python store extra parameters in *args, so it triggered no error