2
$\begingroup$

I know that I am not the first one with this problem, but I didn't manage to find the proper answer, so I hope you would like to help me.

From an electric circuit simulator, I simulated my circuit in time domain and saved a signal in a .txt file (which contains both time and signal values). Based on that .txt file, I would like to produce a FFT to see the signal in the frequency domain.

Unfortunately, the circuit simulator has a variable time step, which gives me a headache to obtain the correct FFT values (two-sided or one-sided is second priority at this point)

I managed to get some FFT out of my data, but I am having difficulties verifying the correctness of it, and hence appreciate any help on this matter. My simulation files give me different values for Average and RMS values, hence the confusion. Thank you. The link to my .txt file is given below (since I couldn't find a way to upload the txt file here directly): https://file.io/PXuD9sWR

My Python code:

import numpy as np
from scipy.fftpack import fft
import matplotlib.pyplot as plt
import pandas as pd


buckstruct = pd.read_csv('buck.txt', sep = '\\t', engine='python')
bucktime = buckstruct.iloc[:,0]
bucktime = bucktime.values.tolist()
buckcurrent = buckstruct.iloc[:,1]
buckcurrent = buckcurrent.values.tolist()
bucktime_flt = []
buckcurrent_flt = []

# Filter out the start-up transient. Save only steady state values
for i in range(len(bucktime)):
    if bucktime[i] > 0.002:
        bucktime_flt.append(bucktime[i])
        buckcurrent_flt.append(buckcurrent[i])

plt.plot(bucktime_flt, buckcurrent_flt)
buckfft = fft(buckcurrent_flt)
buckfft_flt = []

# Double the amplitude for harmonics as a first step to converter from two-sided to single-sided FFT
for i in range(len(buckfft)):
    if i == 0:
        buckfft_flt.append(1.0/len(bucktime_flt)*abs(buckfft[i]))
    else:
        buckfft_flt.append(2.0/len(bucktime_flt)*abs(buckfft[i]))

plt.plot(buckfft_flt) 

Simulation results from my circuit simulator

$\endgroup$
3
$\begingroup$

Thank you for your response. I registered to this website, so not sure if my name is the same one or not anymore, but I am the OP. I'm not quite sure I fully understand the solution on how the resampling and interpolation can help me. If I downsample (let's say every 100th sample), I will have a 100x smaller Dataframe size, but the elements are not evenly separated, i.e. the sampling time between two samples is not constant.

If I upsample afterwards to create empty entries in my Datafra, I get my original Dataframe size. So far, so good. But the interpolate function somehow seem not to work in my case. I'm new to Pandas, so please be patient :)

import math
import numpy as np
from scipy.fftpack import fft
import matplotlib.pyplot as plt
import pandas as pd


buckstruct = pd.read_csv('buck.txt', sep = '\\t', engine='python')
buckstruct.index = pd.to_datetime(buckstruct.index, unit='s')
buckstruct_downsampled = buckstruct.resample('100s').sum()
buckstruct_upsampled = buckstruct_downsampled.resample('10s').sum()
buckstruct_interpolated = buckstruct_upsampled.interpolate()

enter image description here

EDIT1: Previous contribution merged into this answer:

Thanks for your reply. Yes it's LTSpice, and thanks for the tool. I will look at it. Though it would be nice to also have a solution in Python.

Let me ask the question the other way around, since I am no expert on FFT. Maybe there is no need for this kind of workaround after all.

Given the RAW data samples from my .txt file, i.e. not resampled, interpolated or whatever, and by simply running the FFT, I receive the following FFT result: enter image description here

So, I have something around 1.75 (y-Axis) at 0 (x-Axis), and something around 0.5 (y-Axis) at 300 (x-Axis).

The 300 is NOT the frequency, because the frequency of my electronic circuit is 100kHz. So I need to find a way to map the 300 in the graph to 100kHz to match my simulations. This is fairly easy if the sample rate was constant. But is it also possible if the sample rate in the .txt is non-uniform?

Thank you,

EDIT2: Solution to the problem: Translating a non-uniformed dataset into a uniformed dataset.

This can be achieved using Scipy's interpolation function. Below is a minimal working example that does not need the initial dataset, but uses 10 consecutive data samples coming from the initial dataset. As one can see, the sample time is not constant which makes it difficult to perform a FFT and to extract the frequencies. Using the interpolation function, constant sample time can be achieved and the resulting data set can be used for FFT calculation. The procedure is to obtain the interpolation function first, and then for a given data length to perform the actual interpolation and to use the interpolated data set for FFT purposes. I hope this minimum working example can be useful for others. Thank you,

enter image description here

# Minimum working example to show the effectiveness of the interpolation function

import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np

time_raw = [0.0009999650511607746,
 0.000999965566641282,
 0.0009999660821217893,
 0.0009999672947956213,
 0.0009999682836389868,
 0.0009999696068367931,
 0.0009999715724226046,
 0.000999978044536965,
 0.0009999994033983994,
 0.001]

signal_raw = [1.132772,
 1.132642,
 1.132511,
 1.132205,
 1.1319549999999998,
 1.13162,
 1.131124,
 1.1294879999999998,
 1.124091,
 1.12394]

fxxx = interpolate.interp1d(time_raw, signal_raw)

num = len(time_raw)
xx = np.linspace(time_raw[0], time_raw[-1], num)
yy = fxxx(xx)

plt.figure(1)
plt.plot(time_raw, signal_raw,'bo-', label='Original')
plt.plot(xx,yy,'g.-', label='Interpolated')
plt.ylim([1.120, 1.135])
plt.legend();

The resulting FFT for the initial problem with the actual data set from the simulation is then as expected. My apologies for this messy thread. enter image description here

$\endgroup$
2
$\begingroup$

It looks like you're using LTspice. If so, in the LTspice group you'll find a little free utility, ltsputil, which does exactly what you want, maybe a bit more. There are also questions in the group about its usage, if you have problems understanding how it works, though it should be pretty easy.


First, this little command-line utility can pack a punch, so you'd do yourself a favour if you read the help file, ltsputil_help.txt. In that file, search for Export data from raw file, and you'll get to the relevant part. Here's an example usage for a test.raw file that has been saved by LTspiceXVII in .TRAN, and has only one variable saved, V(out):

  1. first run ltsputil17raw4.exe test.raw tmp.raw. This will convert the XVII-style .raw data to IV-style (ltsputil was written for LTspiceIV). Obviously, if the .raw file has been saved with LTspiceIV, this step is not needed.

  2. then apply equal spacing by running 'ltsputil.exe -eo tmp.raw out.raw 131072 "". This will $e$qualize the timesteps, while $o$verwriting the output file, if exists. Since test.raw has 145746 points, I've chosen the next lower power of 2, for the sake of exemplification, even though Octave can do fft just fine with that number of points.

  3. run ltsputil.exe -xo0 out.raw data.txt "%14.6e" "," "" 0 1. This will e$x$tract the data, $o$verwriting the output (if exists), and writing $0$nly the SPICE data. The other command-line arguments are for number formatting, delimiting, and which traces to save. By default, the first column is saved for the time variable (in .TRAN), or freq (in .AC), so the $0$ and $1$ at the end signify saving the 1st column ($0$, time), and the actual data, the 2nd column ($1$, V(out)).

There may be some leftover .tmp files, they can be safely deleted. For comparison, exporting the data from LTspice as rawdata.txt will preserve the variable timesteps, and their derivative (right), compared to the resulting one (left), look like this:

comp

It's not perfectly linear, but it's certainly not the original. The thickness comes from small bumps where the interpolation happened.

As for the frequency, you have to know beforehand the total simulation time. For this case, it was $t=800 \mathrm{\mu s}$, and the chosen number of points $N=131072$. These give the lower frequency $f_{min}=\frac{1}{t}=1.25\mathrm{kHz}$ and the upper frequency $f_{max}=\frac{N}{2t}=81.92\mathrm{MHz}$. With these you can generate an appropriate linspace() or logspace().

But, about this part, I'm not so sure, but most probably I'm doing it wrong, because using f = linspace(1250, 81920000, 65536) (half the number of points to plot only the first half of the fft) doesn't properly align the peaks.

$\endgroup$
0
$\begingroup$

Since the OP is using Pandas within Python already, Pandas supports variable resampling; there are more details on this here and in the resample documentation for pandas

https://machinelearningmastery.com/resample-interpolate-time-series-data-python/

For the OP's purpose the most straightforward approach would be to resample to a fixed rate and then from that data use the standard FFT algorithm to compute the DFT.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.