Bode plot for scope owners that don't have the option - Page 1
Forum section: electronics / testgear

author: TheoB
author_rank: Regular Contributor
timestamp: January 03, 2025, 10:23:28 pm
content: This topic shows how a step response can be used to make a Bode plot. All that is needed is a signal generator, a digital scope from which you can grab data and software to post process.The software can be downloaded fromhttps://github.com/tband/dho-remoteThe software, dho-remote, can work with and without injection transformer to analyse loop stability.The conventional way of creating aBode plotis to sweep a signal generator and monitor the transfer from input to output of a network. A few hundred frequency points are generated one by one with a (sine wave) signal generator and at each point a wait time is added. This is needed for the updated frequency to settle and for the scope to measure the amplitude and phase on at least two channels. That's slow so you want to automate it. That is what the Bode option normally takes care of.dho-remote uses a step response and analyzes the transfer function in the frequency domain.I've been discussing this idea in a Rigol DHO915sthread(the model with the Bode plot option) as Rigol really did a bad job implementing the Bode option. It shows wobbly waveforms and is inaccurate. It's basically useless. But this thread is focused purely on this model and this feature, so I thought let discuss the idea with a wider audience.A video in which I demo the program measuring a crystal:Another video in which I show how to analyze a negative feedback loop using an injection transformer:https://www.youtube.com/watch?v=Dlk0V3aPnzgFor other scope brands the SCPI commands probably need to be changed a bit.The project can be found on GitHub:https://github.com/tband/dho-remote.LCR example:23nF parallel with 68uHMore challenging, a 4.0MHz crystal. A 100Hz signal is used which is 4 decades lower than the frequency measured.Bigger goal for the Rigol hackers is to implement a Bode plot option in the scope. The software will then run on Android and controls a signal generator via a network connection (or the internal AWG if available). It can use this step response method using a square wave or the stepped sine wave for larger dynamic range. But that's just day dreaming at the moment.

author: Someone
author_rank: Super Contributor
timestamp: January 03, 2025, 10:44:13 pm
content: Quote from: TheoB on January 03, 2025, 10:23:28 pmWhile that does work for some cases, the dynamic range of the acquisition front end becomes limiting. The value of an oscilloscope is the front end attenuators which provide gain when needed and are exploited when stepping/sweeping through different frequencies.There are other more general reasons why sweeps are desirable:https://audioroundtable.com/misc/Mueller.pdf

author: TheoB
author_rank: Regular Contributor
timestamp: January 04, 2025, 10:07:07 am
content: That's true. If you need over 100dB of dynamic range you better use a swept sine wave. But for most common scenario's an impulse response just work fine. Here I measured a ceramic resonator which resonates around 3MHz.I have a VNA which is much more suitable to measure S21 plots. At least for frequencies above 10kHz and with 50Ohm impedance. This is a nice complement as frequency range extends to near DC and scope load is 10MOhm.

author: iMo
author_rank: Super Contributor
timestamp: January 05, 2025, 01:17:32 pm
content: With the sawtooth wave generator you will get much better input spectra for your Bode analyzer.With square wave you will get harmonics only.

author: TheoB
author_rank: Regular Contributor
timestamp: January 05, 2025, 04:15:30 pm
content: Not exactly. A sawtooth has much less harmonics. Harmonics you get from fast transitions. A sawtooth does not really have that:I'm not sure what your definition of sawtooth is but I also show here one with a fast and slow slope:

author: tggzzz
author_rank: Super Contributor
timestamp: January 05, 2025, 05:04:29 pm
content: Use a noise generator as a source.Be aware that with two or more tones any non-linearities will generate intermodulation products which will interfere with the measurements.

author: TimFox
author_rank: Super Contributor
timestamp: January 05, 2025, 05:23:17 pm
content: Quote from: TheoB on January 05, 2025, 04:15:30 pmFourier series analysis shows that any time-periodic waveform contains frequency components at only the fundamental frequency (equal to the repetition rate) and integer harmonics of the fundamental frequency.The last example is what is normally called a “sawtooth”, as used for CRT horizontal deflection (sweep).The square wave (first example) and symmetric triangle wave (second example), due to time symmetry, contain only the fundamental and odd harmonics. The asymmetric sawtooth (third example) contains even harmonics as well.Since the triangle wave is the time integral of the square wave, the relative amplitude of the harmonics is lower than for the square wave.A train of very narrow pulses gives a string of harmonics that falls off slowly with frequency (“picket fence” in both time and frequency), but requires a very high dynamic range to be useful here.

author: gf
author_rank: Super Contributor
timestamp: January 05, 2025, 06:30:34 pm
content: Quote from: TheoB on January 05, 2025, 04:15:30 pmThe amplitudes of sawtooth harmonics decrease with 20dB/decade, like square wave harmonics do, but unlike a square wave spectrum, it includes even and odd harmonics, i.e. you don't get "gaps". For a given peak-to-peak amplitude, the power is a few dB lower, though. I wonder why your sawtooth spectrum has zeros at N*10kHz (i.e. already at the 100th harmonic)? An ideal sawtooth spectrum should not have them. Maybe your edge is not steep enough?

author: iMo
author_rank: Super Contributor
timestamp: January 05, 2025, 07:22:17 pm
content: Yup, missing "odd" in my second sentence above.The sawtooth is the right hand side picture in your sim. A steep rising or falling edge.

author: Fungus
author_rank: Super Contributor
timestamp: January 05, 2025, 07:40:53 pm
content: Quote from: TheoB on January 05, 2025, 04:15:30 pmA sawtooth has exactlytwiceas many harmonics as a square wave.You can easily see it in the images you posted.Quote from: TheoB on January 05, 2025, 04:15:30 pmThe middle one is a triangle wave, the one on the right is a sawtooth.It gets it's name from ... a saw!

author: TheoB
author_rank: Regular Contributor
timestamp: January 05, 2025, 08:26:51 pm
content: Wow. That hurts.I meant with less harmonics, fewer harmonics useful for this application.I entered 1% for symmetry for the sawtooth. The instrument can go to zero as well. That explains the null around the 100rd harmonic.Sawtooth is the best option as it gives the most harmonics in the first decade. It does give less power at higher frequencies though. The square wave lacks the even harmonics and the 25% duty cycle version misses the 4th and 8th harmonic. That's a nice compromise. At the end it's easy to play with the waveforms and see which one give the best performance. In my tests I don't see much difference. There should not be a lot of phase/amplitude change in the first decade. In the first decade the resolution is rather course. For high Q applications you really need a low frequency stimulus (for a crystal I used 300Hz) to measure around 4.9MHz.

author: tggzzz
author_rank: Super Contributor
timestamp: January 05, 2025, 08:31:44 pm
content: With a square or sawtooth or noise stimulus, how do you take account of IMD (intermodulation distortion)?Or are you presuming the UUT is linear? No magnetics? No saturation? No semiconductors?

author: TimFox
author_rank: Super Contributor
timestamp: January 05, 2025, 08:35:46 pm
content: English grammar note: the square wave hasfewerharmonics.

author: iMo
author_rank: Super Contributor
timestamp: January 05, 2025, 08:49:08 pm
content: For measuring the crystals 300Hz is not enough, imho. You will get a picture, but the ser/par res peaks are rather narrow, so you would need something like x Hz.What you can do is to wobble/sweep with the fundamental of the sawtooth, for example between 300Hz and 500Hz step 5Hz and reconstruct the graph off the particular bode_sweep_freq accordingly. The same when you want to go over several decades - do sweep w/ the fundamental freq from 1kHz to 20kHz step 100Hz for example, and reconstruct the graph off the 200 bode_sweep_freq (or do the step with 1/2/5 or 1/3/10 ratio, etc).Like (linear, a crazy example):1. 1000Hz -> bode12. 1100Hz -> bode2..199. 19.9kHz -> bode199200. 20.0kHz -> bode200and get all the bodeX together into a single graph (somehow)..

author: TheoB
author_rank: Regular Contributor
timestamp: January 05, 2025, 09:57:57 pm
content: Quote from: tggzzz on January 05, 2025, 08:31:44 pmYou don't. The assumption is a linear dut. To measure the transfer of a non-linear system you need to lower the amplitude until it becomes sufficient linear again. But you better spend some money to buy a proper instrument. This idea just opens Bode diagrams for hobbyist.

author: TheoB
author_rank: Regular Contributor
timestamp: January 05, 2025, 10:14:15 pm
content: Quote from: iMo on January 05, 2025, 08:49:08 pmYes, that's what I had also in mind. I made a button that grabs and stores one dataset (with two channels). Everytime the button is pressed, a dataset is added. When you push plot all datasets are reduced to one dataset. That works good for two datasets (like 100Hz and 10kHz), but for sets with more overlap I have some issues with accuracy. Work in progress.Anyhow, I placed a first version onGitHub.

author: mawyatt
author_rank: Super Contributor
timestamp: January 06, 2025, 02:41:19 am
content: Quote from: TheoB on January 05, 2025, 09:57:57 pmSome instruments have Synchronous Sampling/Detection or Frequency Selective Sampling which basically limits the measurement bandwidth around the stimulus sine-wave frequency and reduces noise and unwanted outside influences allowing low levels of sine-wave stimulus. This feature has trickled down from the Higher End Instruments to where you don't need to spend a lot to get this within the Bode Function (SDS800X HD is one example of such)Best

author: mawyatt
author_rank: Super Contributor
timestamp: January 06, 2025, 03:27:41 am
content: What about a Pseudo Random Pulse Sequence as a stimulus? Should be rich in frequency content and some AWGs can produce this directly or from a file.Best

author: tggzzz
author_rank: Super Contributor
timestamp: January 06, 2025, 08:56:48 am
content: Quote from: mawyatt on January 06, 2025, 03:27:41 amIt can work.I played around with it to see if a cheap SDR dongle could be used as a poor man's 1.5GHz scalar network analyser.The principal limitations were IMD, dynamic range limited by the total power output (not the Rx frequency), and SDR sensitivity. Unsurprisingly the dongle's frequency response was far from flat, and had to be calibrated out.Good enough to see general filterpassbandshapes, with a comb filter being a useful test case.Overall, a noise diode was better.

author: iMo
author_rank: Super Contributor
timestamp: January 06, 2025, 09:55:47 am
content: The random square sequence (and Dirac) has been briefly discussed already in thethe Rigol 914S thread..

author: iMo
author_rank: Super Contributor
timestamp: January 06, 2025, 10:03:03 am
content: Quote from: TheoB on January 05, 2025, 10:14:15 pmThe first naive approach could be - simply merge all bode points (a Bpoint is {freq, magDB, phase}) from all bode data sets into a single array/record and sort all the Bpoints based on the ascending freq, in case there are Bpoints with the same freq (it may happen) make an average of them.

author: tszaboo
author_rank: Super Contributor
timestamp: January 06, 2025, 10:07:30 am
content: Few practical things:You don't need 1000 points for a bode plot. It's useful with as little as 10 points per decade.Sine wave is used because systems might benon-linear, where you cannot distinguish between harmonics from your DUT or the excitation.And you need noise filtering or averaging to get really good results. How much depends on the frequency and your test setup. Better have it configurable. I guess most of the magic in the calculations goes into this, and I don't think scopes simply read the peak to peak values and call it a day.Also you should make something functional first before optimizing it.

author: iMo
author_rank: Super Contributor
timestamp: January 06, 2025, 11:17:23 am
content: Also a windowing function may/will play a role (and based on its type the coefficient you have to multiply the resulting magnitudes with). Also the dynamic range (with say 10bits ADC ENOB) will not be better than 60dB with this approach. So this thread is rather an interesting investigation what is feasible to do with a minimal effort (and it could be used for some less demanding measurements as for example the square wave with a variable duty, period and amplitude is easy to produce)..As I wrote in the 914S thread thenew Quality Bodeshould be based on the individual bode points (with the sine wave input) and an "adaptive" settings made before each bode point measurement (thus you get the best results with >120dB range, imho).

author: gf
author_rank: Super Contributor
timestamp: January 06, 2025, 11:57:03 am
content: Quote from: iMo on January 06, 2025, 10:03:03 amWhen averaging duplicate Bode points of the same frequency, you should average the complex gain, not the magnitude/phase separately. And to ensure that noisy points due to a small stimulus amplitude don't ruin the result, I suggest calculating aweightedaverage (weighted by the stimulus amplitude at the particular frequency).Another option is keeping the point with the largest stimulus amplitude if there are duplicates with the same frequency. However, this approach cannot improve the SNR, while averaging may enhance the SNR if there are multiple points with a large stimulus amplitude.Another question is how to determine whether a particular FFT frequency point is considered a "Bode point" or not. If the stimulus is known to be a sum of sine waves (e.g., fundamental+harmonics), you could search for the peaks in the spectrum. For a stimulus with a continuous spectrum, or for a noise stimulus, this does not work, and the only way coming into my mind is to exclude FFT points with a stimulus power below a certain threshold, and treat all other FFT points as "Bode points". In this case, I would also prefer to average duplicate Bode points of the same frequency and not just keep the points with the largest stimulus amplitude, since there is a chance to encounter multiple acquisitions with a high stimulus amplitude, where averaging may enhance the SNR.

author: gf
author_rank: Super Contributor
timestamp: January 06, 2025, 12:37:52 pm
content: Quote from: iMo on January 06, 2025, 11:17:23 amA rectangular window also has an advantage: It has the lowest ENBW of all window functinos (-> best SNR). You can use a rectangular window function1) if the stimulus is a sum of sine waves where the frequency of each sinewave is anexact integer multiple of the FFT bin spacing2) and if you do not have the requirement to filter out unwanted disturbanciesbetween the stimulus frequencies with high stop band attenation, so that theydon't leak into your measurements.If condition (1) is not met (e.g., due to a frequency deviation between the signal generator time base and the scope time base), then spectral leakage becomes a potential problem that should be quantified for the desired frequency plan, and, if necessary, addressed by using a non-rectangular window function.For a stimulus with continuous spectrum, or noise, I'd generally use a non-rectangular window function with high selectivity.If you have the additional aim to filter out unwanted non-stimulus frequencies (disturbancies) with large stop band rejection, you will need a proper window function too.In general there is always a trade-off between selectivity / stop band rejection of a window function and ENBW. There is no free lunch. You have to pay for a higher selectivity / stop band rejection / lower leakage by accepting more noise, or by using even more FFT points (-> longer measurement time).

author: shabaz
author_rank: Frequent Contributor
timestamp: January 06, 2025, 01:12:53 pm
content: I just took a very simple approach, since it will cover many of my use-cases without much effort. Although I have the functionality built-in too, not all 'scopes support it, and besides, sometimes it's just preferred to have the custom chart rather than a 'scope screenshot.My simple approach was to use SCPI from Python code to control the sine-wave spot frequencies, and it's not too granular, in fact I have to list each discrete frequency in the code (not a big deal, especially since I mostly am not looking for any more detail than a few tens of points total). I made no effort to optimize the volts-per-div for dynamic range; I expect to go into the code and tweak it if needed (but usually I don't bother).I don't use a GUI; instead, I use the Python command prompt to issue a command to run the scan, and then another command to plot the output. I can call additional functions to set the parameters for the capture, if I'm not happy with the defaults, or if I don't edit the source code.The chart shows an audio preamp circuit I was experimenting with, with the bass turned down low in that capture.The chart looks a bit odd without the horizontal grid lines; that was supposed to be temporary because I was experimenting with trying to align the left and right side axes so that I could have common grid lines, but was unsuccessful in figuring out how to do that in the time I had; I need to think on that more when I get around to it.Anyway, obviously such a quick hack won't suit all, but sometimes that's good enough.

author: TheoB
author_rank: Regular Contributor
timestamp: January 06, 2025, 09:39:16 pm
content: Quote from: gf on January 06, 2025, 11:57:03 amI averaged the complex FFT of a number of acquisitions. That can also be automated for say N=16 but I tried it first manually. That works as expected.It's already in my code but I changed it to picking the strongest of N of CH1. The same mask (which sample to use from N) is also applied to CH2. This works best to combine acquisitions from different stimuli frequencies. Using both 100 Hz and 10kHz improves the SNR by 40dB from 10kHz onwards. Averaging with N=16 gives 12dB SNR improvement. I will add a radio button to select between these two options. Running 32 FFT over 1Msamples is time consuming though. Using two input frequencies needs only 4 FFT calculations.The points to keep are all points that are less than 60dB below the max point. That should roughly be three decades higher than the fundamental. It's this function that determines the quality of the waves.If all acquisitions use the same stimulus then averaging before masking is the best. If the stimuli is not the same averaging should not be used. The 100th harmonic of 100Hz might fall on the fundamental at 10kHz. Averaging the two will not improve the SNR as the amplitude levels are vastly different.I apply a Hanning window over the data at the cost of some SNR. That's more needed for plotting single channel FFT. The same code is reused for the Bode plot. I now realize this is perhaps not needed. The frequency bleeding will be the same for both channels for which I plot the difference.

author: gf
author_rank: Super Contributor
timestamp: January 07, 2025, 01:47:12 pm
content: Quote from: TheoB on January 06, 2025, 09:39:16 pmYou mean averaging fft(CH1) and fft(CH2) separately?FFT is a linear operation (multiplication of the time domain signal vector of length N with a huge complex NxN matrix), and average is basically just a sum and division by a constant, so the distributive law applies. Therefore average(fft(CH1)) is equivalent to fft(average(CH1)). So what you get is the fft of the average of the CH1 traces. [ Btw, this also offers a cheaper alternative to calculate this average: Average the time domain traces and calculate the FFT only once. ]Note that this kind of averaging will only give you the desired result if the traces to be averaged are in phasefor each frequencycontained in the signal. In the end, you still rely on the trigger to do this alignment. But this does not work in general. Of course, it is supposed to work if the same periodic waveform is captured multiple times. But it will not do what you want it to do if you average two traces of, say, a 25% duty cycle square wave, where one trace captures a 10 Hz tone and the second trace captures a 100 Hz tone, both triggered on the rising edge. Then the 10th harmonic of the 10 Hz tone in the first trace isnot in phasewith the fundamental of the 100 Hz tone in the 2nd trace (-pi/4 vs. -pi/2). Therefore, they do not qualify for this kind of averaging.What I actually had in mind instead is average(fft(CH2)/fft(CH1)), i.e. averaging the complex gains of multiple acquisitions. The expected value for the phase angles of this quotient is the phase of the DUT's transfer function, which is invariant with respect to the acquisition phase. Therefore, this average does not depend on the trigger, but relies only on the coherence between CH1 and CH2, which is achieved by sampling CH1 and CH2 simultaneously.QuoteThere are two main considerations:1)Scalloping loss: You are correct, it cancels out in the complex gain fft(CH2) / fft(CH1), but of course it also reduces the SNR because a lower signal level is detected by the FFT bin. OTOH, for displaying the spectrum of a periodic signal you usually want to avoid scalloping loss by using a flattop window in order to show the amplitudes of the harmonics correctly, even for arbitrary signal frequencies.2)Bleeding between harmonicsdoes not cancel out, and therefore also matters for the complex gain. Especially for a narrowband DUT, you likely want a window function with a high selectivity and high stopband attenuation (maybe even 100+ dB), like e.g. Blackman-Harris, Blackman-Nuttall, or Kaiser with an appropriate beta.EDIT:Plotting fft(CH1) and fft(CH2) is, of course, only for your diagnosis.It's not part of the actual Bode plot.EDIT:For better understanding, here is pseudo code for what I had in mind:Code:// initializefor f in 0...FFT_SIZE-1 {gain = 0 + 0i; // complexweight_sum = 0;}for each acquisition {Vin = fft(CH1);Vout = fft(CH2);for f in 0...FFT_SIZE-1 {weight = abs(Vin); // magnitude of reference signal at frequency f// skip frequencies where the drive level is too lowif (weight > threshold) {gain += Vout / Vin * weight;weight_sum += weight;}}}for f in 0...FFT_SIZE-1 {if (weight_sum > 0)gain /= weight_sum;}discard/ignore all points gain where weight_sum == 0plot magnitude and phase of the remaining points.

author: TheoB
author_rank: Regular Contributor
timestamp: January 07, 2025, 06:57:06 pm
content: Yes, good point averaging can just as well be done by the scope in the time domainI try to work on arrays and not loop over all frequency points. The numpy library is optimized for working on large arrays.Code:if mode=='fftmean':fft_val1=np.mean(fft1,axis=0)fft_val2=np.mean(fft2,axis=0)fft1 and fft2 are two dimensional arrays of FFT results for CH1 and CH2. But I use the max of each now, not the mean.Code:if mode=='fftmax':fft_val1=np.max(fft1,axis=0)fft_val2=np.max(fft2,axis=0)next I select samples which are not smaller that 60dB from the peak value.Code:xbode = Vrms1 > (max(Vrms1[2:]) / 1000)This xbode is a selector (array of booleans) that select only the samples I want. Samples near DC I also filter out.Code:# Use range input from userxbode[xf < float(self.bodefmin.get())] = Falsexbode[xf > float(self.bodefmax.get())] = Falsebodedb = 20 * np.log10(Vrms2) - 20 * np.log10(Vrms1)PhaseDiff=np.angle(fft_val1[:data["N"] // 2])-np.angle(fft_val2[:data["N"] // 2])bodephase = np.unwrap(PhaseDiff, period=360)Your pseudo code looks similar except that you still average if more than one acquisition contains more energy than the threshold. I don't see the practical use for that. Averaging is done by the scope.

author: gf
author_rank: Super Contributor
timestamp: January 08, 2025, 09:15:08 am
content: Quote from: TheoB on January 07, 2025, 06:57:06 pmBe careful with np.max(). For complex numbers, it seems not to do what you want - it does not select the largest magnitude:Code:>>> np.max([0+2j,1+0j])(1+0j)Sort order for complex in python seems to be not by magnitude, but lexical, using the real component as primary key and the imaginary component as secondary key.

author: tggzzz
author_rank: Super Contributor
timestamp: January 08, 2025, 10:05:33 am
content: Quote from: gf on January 08, 2025, 09:15:08 amOh, wow.I'd like to know the definitive root cause of that "surprising" result, since it might affectfarmore than just that one function.

author: iMo
author_rank: Super Contributor
timestamp: January 08, 2025, 10:55:59 am
content: My dsp math gets a bit rusty with the decades so let me ask following:1. I do bode1 with 1000Hz fundamental (any starting phase, I do for example Kaiser_beta_8)2. my data set is bode1[freq, real, imag]3. I do bode2 with 1200Hz fundamental (any starting phase, I do for example Kaiser_beta_8)4. my data set is bode2[freq, real, imag]5. I do bode3 with 2000Hz fundamental (any starting phase, I do for example Kaiser_beta_8)6. my data set is bode3[freq, real, imag]7. I do sort bode1 and bode2 and bode3 ascending based on freq8. I can see the freq=6000 is common for bode1_6th and bode2_5th and bode3_3rd harmonic9. How do I make the average of the three points?

author: RoGeorge
author_rank: Super Contributor
timestamp: January 08, 2025, 11:01:43 am
content: There are drawbacks when using a signal with a rich spectrum to deduce the Bode plot (thinking here using noise as an input signal, or a frequency comb, or a Dirac, etc.). By drawbacks I mean limiting things like these:- low number of bits (8..12bit) in an oscilloscope ADC- low dynamic range of an oscilloscope analog stage (can't use a big input pulse(s), might saturate the input of an oscilloscope, and the effect of the short impulse(s) might be too low in amplitude for the ADC)- intermodulation distortions (because of many frequencies are present at once, and interact with each other)Looks like would be easier to measure if we have only one frequency at a time. However, this will take a lot of measurements, and the measuring loop has to wait for the signal to stabilise at each frequency step, so repeatedly measuring the amplitude at different frequency might take too long.Here's an idea:- we apply a continuous frequency sweep at the input, at a constant amplitude, and a known start/stop frequency range- sample the entire duration of the frequency sweep in single mode trigger (my 10 years old DSO, Rigol DS1054Z can take up to 24 million consecutive ADC samples, then transfer them later to a PC)- on the PC, apply the Wavelet Transform and plot the spectrum over timeThe problem is that I'm not very confident with my lame math skills, so I'm not very sure if the Wavelet Transform idea will work in practice. My understanding when I've looked into the Wavelet Transform for the first time (https://www.eevblog.com/forum/chat/fun-for-nerds/msg3554257/#msg3554257), and very first application I thought of after reading the PDFs, was to use a continuous frequency sweep, sample, then apply Wavelet Transform, and plot the output, which will be the spectrum over time. (There are already Python modules that can calculate the WT.)Since we know the frequency of the input signal at every moment during the frequency sweep, we can apply WT on the output of the DUT, and the projection of the WT on the amplitude plane would be the Bode plot.This looks like some sort of "Captain Obvious" type of application for the Wavelet Transform.Is this a known technique, to produce a Bode plot by using a frequency sweep then WT?

author: TheoB
author_rank: Regular Contributor
timestamp: January 08, 2025, 10:08:13 pm
content: Quote from: gf on January 08, 2025, 09:15:08 amVery good point. This is a bug due to my wrong assumption that max considers magnitude. I was already investigating some wrong results I sometimes got with multiple captures. Will fix.

author: TheoB
author_rank: Regular Contributor
timestamp: January 08, 2025, 10:51:12 pm
content: Quote from: iMo on January 08, 2025, 10:55:59 amIn this example you have three results for the same frequency (bin). If the amplitude would be the same, averaging would be easy, giving the same weight to all three observations. Here the amplitude ratio is 2:1.5:1. For simplicity assume the amplitude ratio is two using two samples. This means the noise in the weak sample is twice as high as the noise in the other sample. Both samples represent the same CH2/CH1 ratio. Goal is to lower the noise by averaging. Using equal weight to both samples would decrease the SNR by 1dB where we normally expect an increase of 3dB. A weighted average would still increase the SNR but by only 1dB. Averaging is only efficient if the signal amplitude is about the same.

author: TheoB
author_rank: Regular Contributor
timestamp: January 08, 2025, 11:28:55 pm
content: Quote from: RoGeorge on January 08, 2025, 11:01:43 amIt's fully understood that is not the best Bode plot implementation. Where it wins is in price performance ratio. I find 100dB dynamic range reasonable when measuring bandpass filters. Limited by the scope's dynamic range. That can only be increased by changing the scope frontend to give more gain when a weak out of band signal needs to be measured. And then you must use a sine signal.The WaveLet idea gives time information when a chirp signal is applied. That's not needed for this application. Just capture an entire sweep in the time domain and consider it to be stationary (it repeats forever) and perform FFT. I don't expect much better performance from a chirp compared to a constant frequency signal. The generator will only very shortly output a tone for each individual frequency bin. It needs to sweep linearly as the FFT bins are all the same size. The sweep time and capture time need to be carefully aligned. If you limit the sweep range to that of a crystal resonator, you do have a big advantage as all energy is concentrated around the frequency of interest. An FM modulated signal should work as well.Why not try it out

author: Smokey
author_rank: Super Contributor
timestamp: January 08, 2025, 11:35:54 pm
content: Bode plot from swept sine (with matlab code):https://www.eevblog.com/forum/projects/bode-plot-from-swept-sine-input-and-scope-captured-data/https://pages.hmc.edu/mspencer/e151/AutoBodeGuide.pdfAnother paper describing the same thing:https://www.thinksrs.com/downloads/PDFs/ApplicationNotes/SR1_SweptSine.pdf

author: gf
author_rank: Super Contributor
timestamp: January 09, 2025, 10:56:54 am
content: Quote from: TheoB on January 07, 2025, 06:57:06 pm... if the scope returns the averaged trace at a resolution higher than the ADC resolution, and if the scope still supports full memory depth for the averaged trace.

author: TheoB
author_rank: Regular Contributor
timestamp: January 12, 2025, 07:32:13 pm
content: Spend my weekend to fix two bugs as some things were actuallyI updateddho-remoteto v1.1The best sample from multiple captures is correctly chosen.Phase display is displayed in degrees and not in radians.Added an input field to specify the amplitude of the harmonics to be used relative to 0dB full scale. That is needed if CH2 has a much smaller signal like when measuring a bandpass filter or crystal.Quote from: gf on January 09, 2025, 10:56:54 amThe waveform data of the Rigol is 16 bits and that is used for averaged data. Although it improves the SNR, it also limits the memory depth to 1M. For higher memory depth the averaging could be done in software.I also experimented with a sine wave as stimulus. That gives one point per capture. A complete Bode diagram can be build in this way as well but doing so is a lot of work.

author: gf
author_rank: Super Contributor
timestamp: January 12, 2025, 10:03:27 pm
content: Quote from: TheoB on January 12, 2025, 07:32:13 pmWell, if the generator is controlled from the program, then it's not much work. But it takes quite a while...Since a chirp was also proposed in this thread, I tried to simulate a measurement of a 4th order Butterworth notch filter with -3dB notch width of 3.995...4.005 Mhz (similar to yours, but with higher Q) with a single chirp impulse sweeping from 3.97...4.03 MHz. Measurement window is 2000000 samples @625 MSa/s, chirp impulse duration is 1600µs (1000000 samples @625 MSa/s). No averaging. I also included random noise of 0.1% RMS of full scale (SNR -51 dBFS) for each of the 2 channels, and 12-bit quantization in the simulation. The resulting plots (see attachment) look stunningly cleanExpected dynamic range is ~85 dB. The processing gain (and therefore the expected DR) does not depend on the number of samples, but on the sample_rate/sweep_span ratio, i.e. for high DR we want a high sample rate, and a narrow span.Quote from: TheoB on January 08, 2025, 11:28:55 pmJust treat the chirp impulse as what it is, a single impulse with finite support. For a time-limited discrete-time signal (which is essentially zero outside the support region), the DFT calculates discrete-frequency samples of the signal's continuous DTFT spectrum. That's exactly what we want. Don't use a window function, a time-limited impulse is already (self-)windowed.There is no need for exact alignment of the measurement window. Just make sure that both the support of the chirp impulse and also the support of the DUT's response to the chirp impulse fallcompletelywithin the measurement window. Note that the DUT may delay and/or stretch the stimulus impulse, so the measurement window must be wider than the support of the chirp impulse to capture the full DUT response until it has decayed/settled essentially to zero after the end of the chirp impulse. Note that the settling time of a DUT with narrow bandwidth can be quite long. It doesn't matter if you include some additional leading and trailing "silence" (signal=0) in the measurement window before and after the impulses (but don't include more than necessary because the captured "silence" will still contain noise).EDIT: If you want to consider it stationary, then let the signal generator repeat the chirp periodically and proceed as you would with any periodic signal: Wait for the DUT to settle, then capture a couple of periods of the stationary state and apply a decent window function. Of course, unlike for a single pulse, the spectrum is not continuous then, but a comb formed by the fundamental+harmonics of the repetition rate.EDIT: Corrected scale of phase plot. Corrected chirp and window lenght (missing "0").

author: TheoB
author_rank: Regular Contributor
timestamp: January 14, 2025, 08:57:41 am
content: I cannot generate a single chirp with support. Otherwise I would have experimented with it.Instead I tried a 4MHz FM modulated carrier (10kHz deviation, 625Hz modulation). That's a repeating signal, so I captured exactly one modulation period (Samplerate/Mdepth=625e6/1e6=625Hz).The best and easiest result I still get by capturing a single rising or falling edge of a square wave signal.

author: iMo
author_rank: Super Contributor
timestamp: January 15, 2025, 09:12:46 am
content: I've been playing with my older python Bode experiments stuff on the 804 via usb, also tried with yours from github, but it works only from 3.10 upwards because of "match case".Btw., the built in FFT in the scope - I can see a difference in reporting the amplitude of a sine fundamantel (in Vrms) for different windowing functions - like from 620mVrms to 707mVrms (at 50kHz, 10dBm on 50 ohm). Shouldn't we see identical values? They have to apply correction coefficients for each window, haven't they?

author: gf
author_rank: Super Contributor
timestamp: January 15, 2025, 09:38:26 am
content: Quote from: iMo on January 15, 2025, 09:12:46 amOnly if the frequency of your sine wave isexactlyone ofsample_rate / num_fft_points * k(where k is an integer in the range 0...num_fft_points/2), then any window function will give the same result. For other frequencies, you will encounter more or less scalloping loss with different window functions. Use a flattop window to minimize scalloping loss, i.e., if you want to accurately measure the amplitude of sine waves of arbitrary frequency.EDIT: Think of the windowing + FFT as a filter bank ofnum_fft_pointsbandpass filters, whose center frequencies have a spacing ofsample_rate / num_fft_points, and a power and phase detector attached to the output of each filter. If you plot the (overlapping) frequency responses of these bandpass filters, you can see the magnitude response of each bandpass filter responds to a given signal frequency. Only if the shape of the frequency response of the bandpass filters has a flat top with a width >=sample_rate / num_fft_points, then at least one of the filters (FFT bins) can respond with full amplitude to a sine wave of arbitrary frequency.

author: iMo
author_rank: Super Contributor
timestamp: January 15, 2025, 10:35:02 am
content: Ok, with 31.25Msa/s and 1MPts, 93.750kHz sine (31.25*3000), DDS gen at 50ohm, FFT set center 93.750kHz:Rectangular 242mVrmsBlackman 247Hanning 247Hamming 247Flattop 257 (too wide?)Triangle 232

author: iMo
author_rank: Super Contributor
timestamp: January 15, 2025, 11:38:34 am
content: Ok, now with python 3.11.9 (pyvisa-py, numpy, matplotlib, scipy, tkinter, tkinter.ttk installed)Code:C:\Users\Smith\MyCode\Python\dho-remote-main>python dho-remote.py 192.168.0.66File "C:\Users\Smith\MyCode\Python\dho-remote-main\dho-remote.py", line 373print(f"Sample rate={data["sr"] / 1e6:g} Ms")^^SyntaxError: f-string: unmatched '['It seems it does not like " ", works with ' '

author: iMo
author_rank: Super Contributor
timestamp: January 15, 2025, 12:33:45 pm
content: With my 4kHz LC res circuit fed via 33k resistor (200Hz fundamental, sawtooth, HP3310B, 50ohm out, 500kSa/s, 10kpts, 10 precaptures)..Nice!PS: the channel colors in the graph do not match the scope channel's colors, it seems..PPS: v2 With my 4kHz LC res circuit fed via 33k resistor (200Hz fundamental, sawtooth, HP3310B, 50ohm out, 31.25MSa/s, 1Mpts, 16 precaptures?)..

author: gf
author_rank: Super Contributor
timestamp: January 15, 2025, 01:09:51 pm
content: Quote from: iMo on January 15, 2025, 10:35:02 am1) Is the number of FFT points really 1,000,000, or 1,048,576 (2^20, power of two)?2) Is the frequency accurate enough? (possible deviation between generator and scope time base?)For 31Hz bin spacing, the deviation should not be much more than 1 Hz to consider it "essentially exact".3) I remember older posts on EEVblog saying that the flattop window was broken in the DHO scopes. I don't have a DHO and I don't know if this applies to all DHO models and if it has been possibly fixed in the meantime. Perhaps one of the owners can say more about this. According to the cockroach theory, if one window function is broken, I would not rule out that others are broken, too.[ If you calculate Windowing + FFT yourself from time domain samples, make sure that the window function is normalized such that mean(window_function)==1 in order to get comparable amplitudes to a FFT w/o window function (for a signal frequency matching exactly one of the bin frequencies). ]

author: gf
author_rank: Super Contributor
timestamp: January 15, 2025, 06:54:33 pm
content: Quote from: iMo on January 15, 2025, 12:33:45 pm@TheoB, there still seems to be something wrong with the stairsteps in iMo's Bode plot, which are particularly visible in the 300...3000 Hz range. They do not exist in the real transfer function, and they are not noise either. IMO they are artifacts from the calculation.I may be wrong, but I suspect that these stairsteps occur because the set of "Bode points" includes (many) frequencies that are not present in the stimulus signal. The stimulus signal is periodic, and a periodic signal does not have a continuous spectrum, but contains only the fundamental + harmonics, and no frequencies in between. Any leakage from these individual frequencies into neighboring bins in the FFT spectrum is not real - it does not exist in the original spectrum of the signal. Therefore, only the FFT bin closest to each fundamental or harmonic frequency should be considered a "Bode point" (and even that is still just an approximation, since the true frequency may lie somewhere between two adjacent bins). If the stimulus spectrum is sparse, then only sparse points of the transfer function can be measured. Only if the stimulus spectrum is continuous (-> non-periodic signal), then every FFT point is a potential "Bode point" candidate.

author: iMo
author_rank: Super Contributor
timestamp: January 15, 2025, 09:32:36 pm
content: Quote from: gf on January 15, 2025, 01:09:51 pm1) The user guide says "1Mpts" max..2) The deviation could be way much larger than 1Hz, sure. Let say +/-10Hz at that 93.750KHz (50ppm crystal in the DDS and 25ppm in the scope afaik) ..3) Yup, there is a patch file with the new window, afaik, not found it and not installed it yet.

author: gf
author_rank: Super Contributor
timestamp: January 15, 2025, 09:51:23 pm
content: Quote from: iMo on January 15, 2025, 09:32:36 pmIt's not always clear what "1M" is intended to mean. Strictly speaking, the prefix "Mega" (M) means 1,000,000 and "Mebi" (Mi) means 1,048,576, but some people don't take that too seriously. Some scopes only support power of 2 FFT sizes (radix-2 FFT). If the Rigol DHO supports decimal FFT sizes (mixed radix-2 and radix-5 FFT), then this would certainly be convenient.QuoteWhat does the scope's frequency counter measure? Adjust the frequency until the scope sees the desired frequency when you perform this testcase.

author: iMo
author_rank: Super Contributor
timestamp: January 15, 2025, 10:43:34 pm
content: The 804 frequency counter shows +0.5Hz higher (93.7505kHz) than the DDS gen is set up (93.750kHz).The counter can resolve the 1Hz step of the DDS nicely.The issue is the FFT's peak search (31.25MSa/s, 1Mpts) does not place the peak exactly at 93.750kHz, but (from the peak search table):Rect 93.7805kHz 241mVrmsBlck 93.7805 247Han 93.7805 247Ham 93.7805 247Fltt 93.7499 257 (254 when I move DDS such it shows 93.7805)Tria 93.7805 232

author: gf
author_rank: Super Contributor
timestamp: January 16, 2025, 08:23:10 am
content: Quote from: iMo on January 15, 2025, 10:43:34 pmWith a flat top, the horizontal peak location may be ambiguos. Where ist the peak of a flat top? [ Actually, the frequency response of a flattop window is not perfectly flat, but it has very small passband ripple with multiple maxima. There is also no single flattop window, but there exist several variants. I don't know which variant Rigol uses. ]But the frequency responses of all other mentioned window functions do have a dedicated peak, and with a 93.7505kHz input signal, the 93.75kHz FFT bin should have the largest magnitude. The reported 93.7805kHz is approx one bin off (but again not exactly, since the next bin frequency is 93.78125kHz)I'm not sure if the marker is trying to interpolate between bins. But even then it is still wrong.

author: iMo
author_rank: Super Contributor
timestamp: January 16, 2025, 08:46:12 am
content: Flat top has none peak as the top of the spectral line is wider (as I indicated in #46) than the peaks for other windows. I will try to go with the DDS with 1Hz step around the Hanning peak for example, what it will show.DDS 804 Counter Peak_Search_Table_freq93.742 93.7424 93.749993.743 93.7434 93.7805.. 93.780593.772 93.7724 93.780593.773 93.7734 93.8110The peak is 2 values wide, 2kHz span, RBW 50.

author: gf
author_rank: Super Contributor
timestamp: January 16, 2025, 09:14:39 am
content: Something is strange. But it's getting off topic. Let's not pollute TheoB's Bode plot thread.

author: iMo
author_rank: Super Contributor
timestamp: January 16, 2025, 09:47:32 am
content: A comparison (no precapture)..

author: gf
author_rank: Super Contributor
timestamp: January 16, 2025, 10:41:22 am
content: The attached images show what I would expect with a similar filter as yours, 200Hz sawtooth stimulus, 156.25MSa/s, 5Mpts, no averaging, blackman window, and selecting only those Bode points which correspond to fundamental or harmonics of the stimulus signal.EDIT: It's about the expected "clarity" of the plotted curves, don't expect magnitude/phase to match yours.Btw, can you make the rising edge of the sawtooth steeper (as steep as possible) in order to shift the (undesired) zeros in the spectrum to higher frequencies?

author: iMo
author_rank: Super Contributor
timestamp: January 16, 2025, 11:11:11 am
content: Python's graphs interpolate between the Bode points. Therefore it looks like a continuum. But still there are the "stairs"..Nope, I cannot make the sawtooth's rising edge steeper, unless I would start messing with my soldering iron inside my HP3310B (what I do not want as it is my gem of late sixties).. Hopefully I am not the only person who plays with this analyser and there are much better equipped members here..

author: gf
author_rank: Super Contributor
timestamp: January 16, 2025, 11:27:15 am
content: Quote from: iMo on January 16, 2025, 11:11:11 amI mean the coarse stairs in your image (see stairs.png). These are not 1 pixel stairs from line plotting. Many more points have been explicitly plotted here than 200, 400, 600, 800,... Hz (or the nearest frequency bins, which are 187.50, 406.25, 593.75, 812.50,... Hz).EDIT: And of course the bin quantization (i.e. plotting the point for the 200Hz tone at 187.50 Hz) introduces an error in the plot as well. Ideally, the point for the 200 Hz tone should be ploted at 200 Hz and neither at 187.50 nor 218.75 Hz. This error is clearly visible in my plot in #55.OTOH, the transfer function H(200Hz) = Vout(200Hz)/Vin(200Hz) can be taken from the nearest neighbor bin of 200 Hz. The error due the frequency deviation cancels almost out in the quotient. In fact, all nearby neighbor bins of 200Hz compute approximately H(200Hz), not H(bin_frequency). That's also the reason why we get the stair steps when we include multiple neighboring bins in the set of bode points.QuoteAnd your DDS generator cannot either?

author: iMo
author_rank: Super Contributor
timestamp: January 16, 2025, 11:32:33 am
content: DDS is sine only..

author: gf
author_rank: Super Contributor
timestamp: January 16, 2025, 04:01:56 pm
content: Quote from: gf on January 16, 2025, 11:27:15 amFor comparison, the attached plot shows(1) the expected ground truth (blue line)(2) Using all points > threshold as Bode points (orange dots) => staircase(3) Using only the nearest frequency bins to 200, 400, 600, 800,... Hz as Bode points (purple dots) => better but still not accurate

author: TheoB
author_rank: Regular Contributor
timestamp: January 16, 2025, 07:41:20 pm
content: Quote from: gf on January 16, 2025, 04:01:56 pmYou are right.Ideally the captured data should contain exactly N periods. N being an integer, not a fraction. In the examples shown, a non-integer amount of periods were captured. This is likely to happen and because of that a window function is applied (in the current code I choose kaiser with beta=14). Each harmonic spills over a bit to it's neighbors frequency bin. In the FFT plot you see lobes, instead of a single line (one bin contains power, the others none). When the neighbor bins still have enough power (less than 60 dB full scale), they are selected to be displayed. The amplitude might be a bit lower but the ratio is exactly the same. What happens is that the same information is plotted from multiple bins and that shows as a flat line. I added little crosses to the orange phase line to indicate what the used datapoints are specifically for this reason.I have been thinking of an easy way to correct for this, but it's not trivial. You have to assume it's a repeating signal and not a chirp for instance. The reason I've not implemented anything is that the solution is rather simple. Make sure you capture only one edgeCapturing no more than one edge means the fundamental frequency is synchronized to the scope sample rate. The lowest bin is SR/Mdepth. In this example the left one is taken with Mdepth=10k and Sample rate=156.25MSa/s. That means the capture time is 10k/156.25M=64us. That's only a fraction from the 100us period. Now all bins are nicely aligned and no window is needed.Quote from: iMo on January 15, 2025, 11:38:34 amI see that the Windows version of Python looks a bit different to the code than the Linux version. But good that you mention, it's easy to fix.Quote from: iMo on January 15, 2025, 12:33:45 pmI just used the default color for the two traces. You are right, they should match the scopeWill update.

author: iMo
author_rank: Super Contributor
timestamp: January 16, 2025, 08:27:28 pm
content: .. so for the users like me there should be a "help" (or better a built in tool) how to set up everything such you get the single edge only..

author: TheoB
author_rank: Regular Contributor
timestamp: January 16, 2025, 10:30:30 pm
content: An auto setup button?I can measure the frequency you apply and change the time base, the memory depth and vertical settings accordingly. Sounds like a nice addition. It means that the lowest frequency starts a fraction above the applied frequency.What is shown on the screen is only a part of the total memory. The scope screen does not show the full memory.

author: iMo
author_rank: Super Contributor
timestamp: January 17, 2025, 09:45:18 am
content: Quote from: TheoB on January 16, 2025, 10:30:30 pmYup, that would be great and definitely helpful for most users. Otherwise they may get a messy results easily (this method is sensitive to proper settings, imho).

author: TheoB
author_rank: Regular Contributor
timestamp: January 18, 2025, 09:38:32 am
content: I updateddho-remoteto v1.2Auto setup for Bode plot addedTrace colors match the scopeThe auto setup feature configures the scope based on the frequency measured on CH1.Trace colors are now the same as the scope. I kept the background white although the colors are meant for a black background.I also had my first scope hang in which the rigol.scope app kept crashing. And it was persistent. It was known for an earlier firmware version if you switched off the scope in XY mode, but apparently it's still present. Workaround was to change the scope via de SCPI webinterface to not start from the last state.

author: iMo
author_rank: Super Contributor
timestamp: January 18, 2025, 10:28:15 am
content: FYI - I spent a day playing with an LC resonant DUT coupled "inductively" to its input and output.Like a 12mm ferrite (or powder) toroid with a parallel capacitor resonating at around 8MHz, coupled by 2 turns to generator and the another 2 turns to scope (none common gnd). I was not able to get a nice picture as I had not found the proper settings.The issue here is the transient is just a rather narrow short burst (at the edges) around zero with almost the same amplitudes on both channels, also the counter shows nonsense as it catches several counts within the bursts.This is a typical use case for a VNA, sure, on the other hand usage of a decoupling transformer for the measurements of Bode is recommended by Rigol/Siglent etc., afaik.Has to be investigated, imho, but it seems to me the sine wave is the only option here..

author: TheoB
author_rank: Regular Contributor
timestamp: January 18, 2025, 04:57:16 pm
content: A transformer is used to inject the stimulus signal into a control loop at another level than ground. I don't see what your closed loop is. You mention a LC tank which is fully passive. No need for a transformer. But if your transformer works, the generator signal is present at the secondary side and you connect one side to ground, the other to network input. There should not be any difference. CH1 should just see a square wave. Perhaps you can make a sketch of your setup?An impulse/step response can be used to determine a control loop behavior. That's actually a common way to see if the small signal transfer is also valid for larger signals. A DCDC regulator control loop tends to have a loop response that depends on the amplitude of the signal. A step in the time domain applied on the reference should result in the same step response on the output (due to feedback). It will however resonate which is an indication that multiple poles exist.I will see if I can find a transformer in my junk box and show an example with a feedback loop to prove my point.

author: mawyatt
author_rank: Super Contributor
timestamp: January 18, 2025, 05:29:27 pm
content: Quote from: TheoB on January 18, 2025, 04:57:16 pmOne possible issue with this is that the Impulse and Step have a "DC" value and thus can "upset" the loop under investigation. Back in 70s we developed a "Pinger" which approximated a Dirac Impulse Doublet, and has no DC value but excites the loop under test.The Pinger was a handheld battery powered pulse doublet injection device that one could observe the System Under Test Time Domain response, and since almost all systems can be distilled into an approximate 2nd order function one could quickly deduce the system dynamics from the Time Domain response which we often captured with a Tek scope camera (way back before DSOs). The name "Pinger" came from the old technique of flicking a finger at an electromechanical servo system (for example a gyro), thus "Pinging it" and observing the system response.BTW you folks are generating some very interesting results wrt these "Pulse Types" and Bode Plots, keep up the good workBest

author: iMo
author_rank: Super Contributor
timestamp: January 18, 2025, 05:49:21 pm
content: Quote from: TheoB on January 18, 2025, 04:57:16 pmThis was an experiment trying the inductive coupling only. Not a guide how to do it..CH1 at "V1 generator", CH2 at "scope"

author: TheoB
author_rank: Regular Contributor
timestamp: January 18, 2025, 07:24:50 pm
content: All coils have mutual coupling (you mention a rod). The generator output (V1.pos) should be connected to CH1 as this is your source. That should still be a square wave, unless the generator cannot drive the impedance formed by the two windings primary winding. With a high enough coupling factor (>0.9 which you most likely do not have) the generator sees the 50 Ohm scope load parallel with C1.What kind of impedance you have between V1.pos and net scope should be easy to plot. You will be surprised to find out what the actual transfer is (I expect a peaking around some frequency). The square wave at V1.pos (the gen output) might be completely distorted and only show narrow needles, it does not matter for the transfer calculation.The normal application is to measure open loop transfer by placing only L1 in series with the feedback path of the control loop. L1 generates some signal and you monitor L1.pos and L1.neg on CH1 and CH2 to plot the loop transfer. At very low frequencies all signal will be seen on CH1 (the low Ohmic output of the LDO/DCDC) while at very high frequency all signal will be seen on CH2 (connected to the feedback point error amplifier). At some frequency the two channels have the same amplitude. That's the 0dB point where you want to know the phase. That phase is your phase margin.I found two small unknown transformers that I measured. I connected one winding to the generator and CH1, the secondary winding I connected to CH2. The transfer is as shown below:Transformer 1Transformer 2This shows how easy it is to detect that the gain is 15dB (voltage out/in = 5x). It also shows that the transformer is usable up to 350kHz/100kHz. At these frequencies the transformer starts to self resonate. Above the self resonance frequency the output drops by about 40dB/decade as expected. The requirement for the transformer is very low. As long as it can generate a signal above the point you expect the loop gain to become zero.A 5:1 transformer is fine as the generator can drive one winding with 0.5V to deliver 0.1V into the control loop. And 0.1V step is assumed to be within the LDO/DCDC output voltage control range (5.05V and 4.95V for instance). CH1 of the scope will start at low frequencies with 100mVpp which can easily be set to full vertical range. CH2 (the error amplifier input) will hardly see any signal as the feedback will remove most of the signal. CH2 vertical range will be set to perhaps 1mV/div. This assures that the dynamic range of this measurement will be 120dB (80dB scope and 40dB due to the change in vertical settings of the scope).This is just the theory I have in mind. Still need to find a regulator to try it on

author: iMo
author_rank: Super Contributor
timestamp: January 19, 2025, 08:15:08 am
content: As I wrote above I used a "toroid", coupling factor of at least 0.95 with that, at 8MHz resonance.With small inductancies (like well below 500nH primary and say 10-100uH secondary, almost zero resistance) the signals at CH1 and CH2 are around zero (except the transients around the edges) with those "low repetition" pulses (the DC component is 0V).Your setup is a bit different - you are using "600ohm" audio transformers (or similar) with a high internal inductance (like 10s to 100s of mH) and high winding resistance (my bet your winding resistance is close to 50ohms).So usable with high inductance audio transformers, not much with the RF transformers (it is a VNA case)..PS: for those switching PSUs (usually 50kHz - 500kHz, but today up to 2-3MHz operating PWM) Bode plots with decoupling transformers you would need a special transformer, no way to use the "mains transformers" as I've seen on some YT videos, even the audio ones are not suitable, imho..Quote from: iMo on January 18, 2025, 10:28:15 am

author: TheoB
author_rank: Regular Contributor
timestamp: January 19, 2025, 11:25:06 am
content: Ah, I did not read that good enough, toroidIf you drive with a square wave harmonics around 8MHz should be present (rise/fall times should be below 40ns). The auto setup might not work as the counter does not extract the repetition frequency. All you need to do is set the time base such to capture one ringing event. If you set the scope timebase to 1us or faster, the sample rate will be 625Ms/s. With a Memory depth set to 1M, the captured time is 1.6ms. Drive your toriod with a frequency below 625Hz.I think the transformer is indeed used for audio. I've now driven it from the high impedance side and the bandwidth is over 7MHz at -15dB, so well suited for measuring the loop stability of an opamp (I'll start with that)

author: TheoB
author_rank: Regular Contributor
timestamp: January 21, 2025, 07:16:34 pm
content: I have the first early result of the loopgain analysis of an opamp. The opamp is connected unity gain with a injection transformer between the output and the negative input. Input signal is 100mV at 1.5kHz. Setup is automatically chosen.The phase margin I show in the right lower corner as 73 degrees. That makes sense as a ua741 is of coarse very stable with internal compensation. The point is that you can measure loop gain up to very high frequencies, limited to the injection transformer.The DC loop gain should go all the way to 105dB, but the best I could get on the lower end is 60dB around 1kHz. Anthing lower is distorted by the CH2 DFT of the first bins. The DC signal seems to spill over to the next bin of interest (the first non-zero bin I want to see).This is a bread-bord with long wires. That affects the result past 1MHz, as I can assure you the opamp does not oscillate at 2MHz. This is also the result of changing to 80 dB below full scale. Then you also might get noisy result. But at least I could see the behavor of interest around 1MHz. Injecting 2 decades below the 0dB closed loop gain point should give a good result for any control loop.Will probably release an update this weekend.

author: gf
author_rank: Super Contributor
timestamp: January 22, 2025, 10:32:20 am
content: Quote from: TheoB on January 21, 2025, 07:16:34 pmWhere are CH1 and CH2 connected? CH1 to the inverting input and CH2 to the output of the opamp?One thing puzzles me: if CH1 is the input and CH2 is the output of the DUT (as defined here), then - given the waveforms and spectra of CH1 and CH2 - I don't understand why the Vout(f)/Vin(f) or CH2(f)/CH1(f) bode plot shows a low pass response? What am I missing? It rather seems to plot CH1(f)/CH2(f) which is Vin(f)/Vout(f) or 1/gain?Quote from: TheoB on January 21, 2025, 07:16:34 pmSure, the main lobe width of the frequency response of a Kaiser(beta=14) window is about 9.2 bins, so only the bins from #5 onwards have the full stopband attenuation for DC and negative frequencies (which is >= 106dB for a Kaiser(14) window).

author: mawyatt
author_rank: Super Contributor
timestamp: January 22, 2025, 02:23:33 pm
content: Quote from: gf on January 22, 2025, 10:32:20 amHere's a clip from our notebook (requested earlier) that may help. VBPis thefloatingBode Plot Voltage Source, which is usually an Injection Transformer Secondary to realize afloating source. DSO Inputs for a Bode Plot are taken across thefloating source, which if taken differentially is just VBP.Note that VBP(Injected Signal) is considered an Error Signal within the Negative Feedback System, and thus experiences the Negative Feedback System's attempt to correct such. GH is the SystemLoop Gainand if the Feedback "H" is Unity (1) then the Voltage Ratio across the floating VBPSource is the Open Loop Gain "G", which is Op-Amp Open Loop Gain in this case being discussed.Anyway, hope this helps.Best

author: TheoB
author_rank: Regular Contributor
timestamp: January 22, 2025, 06:08:01 pm
content: Quote from: gf on January 22, 2025, 10:32:20 amGood observation. CH1 is output, CH2 is the feedback input.I've added a command line switch '-loopgain'. In that case I plot CH1/CH2 and calculate the phase margin. CH1 is used to determine the available frequencies. In the loopgain application only the output of the amplifier contains that information. The feedback error signal (the - input) should be extremely small due to the high loopgain at low frequencies.I've now removed the DC component after applying the window function. That makes sense as DC is not of interest and it just leaks in the other bins when a window function is applied.

author: gf
author_rank: Super Contributor
timestamp: January 24, 2025, 11:23:48 pm
content: Quote from: TheoB on January 22, 2025, 06:08:01 pmThe (scalar) DC offset is supposed to be subtracted from theoriginal signal, not from the windowed signal. If you want to subtract it from the windowed signal, you must apply the window function to DC as well, according to the equivalence (CH1 - DC) * window = CH1 * window - DC * window (where CH1 and window are vectors/arrays and DC is a scalar value).The subtraction is trivial, but you can only subtract anestimate, and the estimation of the DC value also suffers from leakage. Leakage is not unidirectional, but AC frequencies also leak into the estimated DC value. Only in certain special cases, can you get an accurate estimate, but not in general. The best you can get is a signal whose DCestimateis zero after subtraction, but this does not mean that you have eliminated the actual DC component of the signal. You may even introduce a DC offset which was not present originally. Of course, for a tiny AC signal riding on a huge DC offset, DC subtraction is a big win, even if there remains a residual offset.In addition to leakage from DC, the first few of bins also suffer from leakage from negative frequencies. If low frequency components are present in the signal, it is inevitable that their negative frequency counterparts are be present in the Fourier spectrum as well, leaking into the first few positive frequency bins.EDIT:I have no idea how to compensate for the error of the first few bins in general, so my conclusion is to consider them unusable. If you need to measure these low frequencies, you can of course increase the measurement window duration by a factor of 5 and discard the first 5 bins. That's the price you pay for getting a spectrum analysis filter with high selectivity, high stopband rejection and low far-distance leakage.Another question: When you measure with a single edge in the measurement window, what assumptions do you make about the settling time of the DUT? Do you expect/require the DUT to settle completely in the interval between the edge and the end of the measurement window?

author: TheoB
author_rank: Regular Contributor
timestamp: January 25, 2025, 02:44:35 pm
content: My goal was to make the DC bin zero. First idea was to subtract the average from the CH2-CH1 signal. Indeed there is no longer a DC signal if I don't use a window function. But once I use any window function, DC comes back. And I can see how that happens. The center samples get a much higher weight compared to the edge samples. I do FFT(WINDOW(CH2-CH1)). As long as MEAN(WINDOW(CH2-CH1))=0 there is no DC. And that works indeed. The input for FFT should not have any DC.The part about negative frequencies I don't understand. The FFT (actually RFFT) is taken from real valued time samples so negative and positive frequencies show the same values.QuoteI couldn't say it like that, but I can copy:https://numpy.org/doc/stable/reference/generated/numpy.fft.rfft.htmlQuote from: gf on January 24, 2025, 11:23:48 pmYes when I capture a 1Ms/625Ms/s=1.6ms trace, the DUT must have a stable output in that time (or indeed in half of that time). The lowest bin is 1/1.6ms=625Hz. When the input frequency is lower only one edge is captured.The same limitation is seen when a you capture 1.6ms of a 10kHz signal. 16 periods will be shown. Still you will not see any output below 10kHz in a linear network. Simply because there is no input signal present below 10KHz.

author: TheoB
author_rank: Regular Contributor
timestamp: January 25, 2025, 06:26:48 pm
content: I updated dho-remote to v1.3Loopgain analysis added (argument --loopgain)FFT window selection menu addedHann window addedSelection algorithm for Bode points optimizedA demo video showing the loop gain featurehttps://youtu.be/Dlk0V3aPnzgProgram can be downloaded form Github:https://github.com/tband/dho-remote

author: gf
author_rank: Super Contributor
timestamp: January 25, 2025, 10:08:27 pm
content: Quote from: TheoB on January 25, 2025, 02:44:35 pmLook at the attached FFT plots of 4 sine waves: a=cos(1Hz), b=sin(1Hz), c=cos(10Hz), d=sin(10Hz). All have 1V RMS and no DC offset. 40Sa/s, and FFT size = 40, Kaiser window.Ideally, the correct amplitude in the frequency domain should be 0.707V RMS at all four frequencies -1Hz, +1H, -10Hz and +10Hz, and DC should be zero.The -10Hz and +10Hz peaks are indeed correct, for both cos(10Hz) and sin(10Hz).But look at the -1Hz, +1Hz and DC bins, and notice the difference between a and b. The amplitude of "a" at -1Hz and +1Hz is too high, and the amplitude of "b" at -1Hz and 1Hz is too low. And "a" also leaks into the DC bin, although no DC is present in the time domain signal.What happens is that a fraction of +1 Hz leaks into DC and into -1Hz, and a fraction of -1Hz leaks into DC and +1Hz, and depending on the phase, the phasors add up differently. Sometimes they add up, sometimes they even cancel each other (the latter happens with the leakage of signal "b" into the DC bin - it is not zero because there is no leakage, but because the leakage from -1Hz and from +1Hz happens to cancel each other).I think you can see the resulting non-determinism in the first few bins? Both, "a" and "b" are 1Hz and have the same amplitude. Nevertheless you get different amplitudes in the FFT plot and also a different DC value, although the true DC offset of both is zero. Signal "b" even happens to show DC=0, so DC removal would not change anything for signal "b". And if you would subtract mean(a*w) from signal "a", such that the FFT shows DC=0 after the subtraction, then you would get only an amplitude of 0.16 in the -1Hz and +1Hz bins, which is much too low. So for signal "a", DC removal does not help either.Code:% 40 Sa/s% FFT size = 40% two 1Hz sine waves with 1V RMS, but different phasea = sqrt(2)*cos(2*pi*[0:39]/40); % 1Hzb = sqrt(2)*sin(2*pi*[0:39]/40); % 1Hz% two 10Hz sine waves with 1V RMS, but different phasec = sqrt(2)*cos(2*pi*[0:39]/40*10); % 10Hzd = sqrt(2)*sin(2*pi*[0:39]/40*10); % 10Hz% periodic variant of the window functionw = kaiser(41,14)(1:40)'; w /= mean(w);clfhold onplot([-20:1:19],fftshift(abs(fft(w.*a)/length(x))),";a;")plot([-20:1:19],fftshift(abs(fft(w.*b)/length(x))),";b;")plot([-20:1:19],fftshift(abs(fft(w.*c)/length(x))),";c;")plot([-20:1:19],fftshift(abs(fft(w.*d)/length(x))),";d;")grid on

author: TheoB
author_rank: Regular Contributor
timestamp: January 26, 2025, 12:15:25 pm
content: Yes a window function can introduce a DC term when the input data is not symmetrical around the x=0 axis (the peak of the window). That is exactly the reason why I apply a window and then remove the DC term. That order works. I still see bleeding between the first bins. So I tend to get more consistent results when capturing a few periods and ignore the first bins instead of capturing a single edge. I've also implemented a filter to only use a single point per harmonic. This prevents the staircase we saw before. I only do that for the first 50 harmonics due to limited accuracy in determining the exact frequency (I interpolate between three bins)

author: gf
author_rank: Super Contributor
timestamp: January 26, 2025, 12:54:48 pm
content: Quote from: TheoB on January 26, 2025, 12:15:25 pmThis is exactly my point. Don't try to fix the first few bins. You can't. Rather ignore them and capture at least a few periods (>= 3 periods with Blackman, >= 4 periods with Blackman-Harris or Blackman-Nuttall, >= 5 periods with Kaiser(beta=14)). As a side effect, the DC bin will then be correct as well (although you won't be interested in the DC bin for the Bode plot).

author: gf
author_rank: Super Contributor
timestamp: January 27, 2025, 07:10:19 am
content: Quote from: TheoB on January 26, 2025, 12:15:25 pmCode:...cpg = sum(win) / len(win) # coherent power gainy_windowed=data["y"] * win / cpgy_windowed=y_windowed - np.mean(y_windowed)ywf = rfft(y_windowed)...What your code is doing is equivalent to setting the DC bin to zero after the FFT (which has no effect for the Bode plot as you don't care about DC). But it does not remove leakage from DC to other bins.Let me demonstrate with a comparison between 1) no DC removal, 2) proper subtraction of estimated DC and 3) your method. The signal are two 1mV RMS sine waves sitting on a 1V DC offset. I used a low beta to exaggregate the effect. W/o DC subtraction, the peaks from the 2 frequencies drown in leakage from DC. Your method does not fix that, it just sets the DC bin to zero. However, proper DC subtraction nicely uncovers the peaks.EDIT:Btw, in this context it may be noteworthy that theperiodic variantsof cosine-based window functions (Hann, Blackman, Blackman-Harris, Blackman-Nuttall, Flattop, etc.) have per se zero leakage from DC to other bins (except for the few low frequency bins near DC), due to the location of the zeros in their frequency response. With these window functions, the benefit of DC subtraction vanishes. [ I tried, of course, to show a prominent example where subtraction of the DC estimate really helps. ]EDIT:Due to this property (most of the zeros in the frequency response are located at bin frequencies), I generally recommend using the periodic variant of the cosine sum window functions for this application (argument sym=False when you use the SciPy functions). For Kaiser, symmetric versus periodic does not make a big difference -- the zeros are not located at bin frequencies anyway.Code:% 40 Sa/s% FFT size = 40% Two small sine waves sitting on a large DC offset% 6.5Hz 1mVrms + 12Hz 1mVrms + 1V DCa = 0.001*sqrt(2)*cos(2*pi*[0:39]/40*6.5) + ...0.001*sqrt(2)*cos(2*pi*[0:39]/40*12) + 1;% window function (using periodic variant)% normalized to mean(w) == 1w = kaiser(41,7)(1:40)';w /= mean(w);% windowed signalaw = a .* w;% windowed signal with DC removedaw_DC_removed = aw - mean(aw) .* w;% what you are doingaw_yours = aw - mean(aw);clfsubplot(3,1,1)plot([-20:1:19],fftshift(20*log10(abs(fft(aw)/length(a)))))title("Without DC removal")ylim([-100 0])grid onsubplot(3,1,2)plot([-20:1:19],fftshift(20*log10(abs(fft(aw_DC_removed)/length(a)))))title("With subtraction of estimated DC")ylim([-100 0])grid onsubplot(3,1,3)plot([-20:1:19],fftshift(20*log10(abs(fft(aw_yours)/length(a)))))title("Yours")ylim([-100 0])grid on

author: iMo
author_rank: Super Contributor
timestamp: January 27, 2025, 09:59:25 pm
content: Let me ask here in the hardcore FFT thread:1. my DDS siggen is at 10.000000Hz, plus minus 10Hz let say..2. doing FFT in the o'scope (804/824 here), 1.25GSa/s and 1Mpts, Blackman or Hanning, scope counter shows 10.0001MHz:3. when I set time base such it shows "1us" I can see 10.0749MHz in the peak search4. when I set time base such it shows "2us" I can see 9.99999MHz in the peak search5. when I set time base such it shows "5us" I can see 10.0012MHz in the peak search6. when I set time base such it shows "10us" I can see 10.0097MHz in the peak searchetc.So the closest is at "2us". Now, what is the formula giving all that params together, telling me "the 10MHz peak is at the right frequency in the FFT"?PS: the "peak search" is the table o'scope shows on the display..

author: TheoB
author_rank: Regular Contributor
timestamp: January 28, 2025, 01:03:40 pm
content: With those timebase settings, what you capture is exactly 1M/1.25G=800us. 10 divisions of the timebase values you gave is always less than this 800us. All timebase settings 50us/div or faster should give the exact same result (that is what I use in my program). The bins are 1/800us=1.25kHz wide.The scope however uses the screen, not the memory to calculate FFT. Simply watch the full scan from 0-625MHz with setting 5ns/div. I think you only get 32 points or so. Then set your timebase to 50us/div to have the full-span at 1.25kHz resolution. Even in stop mode you can increase the timebase and see the resolution increase. No idea why Rigol choose to do that as the scope is fully capable to do FFT fast with 1M points that are captured anyhow. No need to use less samples at faster timebase settings. Anyhow, this lower resolution affect the frequency resolution. The best resolution you get at 50us/div, not 2us/div.Try also to change your DDS with 1kHz steps at 2us/div setting. I see 39kHz steps (bin size). 1.25Gs/39kHz=32kSamples.

author: iMo
author_rank: Super Contributor
timestamp: January 28, 2025, 01:33:45 pm
content: With the (all w/ 1Mpts, averaging 64)1.25GS I get in the table 9.99999MHz at 2us1.25GS 9.99999MHz at 20us625MS 9.99999MHz at 100us156MS 10.0001MHz at 500us62.5MS 10.0000MHz at 1ms31.25MS 10.0000MHz at 2ms..PS: the same with none averaging..

author: TheoB
author_rank: Regular Contributor
timestamp: January 28, 2025, 02:53:39 pm
content: The FFT will not show any difference unless a neighbor bin becomes larger. The frequency quantitation is thus equal to the bin size. The bin size is 1.25kHz @ 50us/s.At 2ms/s the sample rate is 31.25Ms/s. I would expect a bin size of 31.25M/1M=31.25Hz. That's indeed what the scope does, but it displays the 10MHz with limited resolution (100Hz). At 1MHz 10Hz resolution etc, limited to 6 digits.

author: gf
author_rank: Super Contributor
timestamp: January 30, 2025, 09:12:45 am
content: In this context I wonder what the Rigol DHO actually displays on the screen when a narrow span of only (say) 10...15 FFT bins is selected.[ E.g. ~10MHz input signal, 1250MSa/s, 1Mpts, center=10MHz, span=15kHz, Blackman window ]Does it just connect the few frequency domain samples (FFT bins) that fall within the span with straight lines (linear interpolation), or does it "properly" interpolate between FFT bins with some sort of Zoom FFT or CZT?EDIT: what I mean is, does it display something like the blue trace, or more like the orange trace in the attached figure?

author: TheoB
author_rank: Regular Contributor
timestamp: January 30, 2025, 07:29:17 pm
content: When you interpolate between two bins I consider that not 'proper'. Lines between the points makes it easier to watch. I do interpolate between multiple bins but under the assumption that this is a single frequency tone spread over multiple bins. In this way I try to calculate the fundamental of the signal with higher resolution than the bins. If you just calculate a DFT from random data there is no way to predict sub-bin points. The window function does smooth the bins though.You have the scope, so see for yourself how Rigol does it. Best visible when at 5ns/div due to the low amount of FFT points (stop the scope to investigate in detail).

author: TheoB
author_rank: Regular Contributor
timestamp: February 28, 2025, 06:15:39 pm
content: I updated dho-remote to v1.4Harmonic filtering improved. Now only local maxima in the spectrum on CH1 are used.Auto setup now tries to set the timebase such that multiple periods are capturedDemo mode added. No scope needed: python dho-remote.py -d examples/LRC.bpltInstallation instruction added here.After a regular system update it stopped working. Turned out that python updated from 3.12 to 3.13.1. And pyvisa was no longer presentRe-installing the package installed it for the newer version.Once you know, it's not a big deal but it shows that relying on the correct python libraries can be tricky.Seems to be a common thing with Python and virtual environments have been invented to work around it. I added instructions and a package dependency list.

author: gf
author_rank: Super Contributor
timestamp: March 01, 2025, 10:12:08 am
content: Quote from: TheoB on February 28, 2025, 06:15:39 pmV1.3 shows commit dates of "2 months ago". Isn't the new one v1.4?But the v1.4 commit does not belong to any branch in the repository.

author: iMo
author_rank: Super Contributor
timestamp: March 01, 2025, 12:08:32 pm
content: ..there are the v1.4 zip and gz files for the dwldhttps://github.com/tband/dho-remote/tags

author: TheoB
author_rank: Regular Contributor
timestamp: March 01, 2025, 05:46:50 pm
content: Ah yes, it should be v1.4 and I forgot to 'push'. Should now be correct :-)