4
\$\begingroup\$

I've been experimenting with Fourier transforms, and I'd like to understand some of the results I'm seeing.

I've written a simulator which generates a data stream of length 1/120 seconds, sampled at 33.92 nS intervals. The data represents a half-sine wave generated by an inverter. I concatenated this stream with its own negative inverse, to create a single perfectly symmetrical 60 Hz sine wave 491,352 samples long.

enter image description here

I then ran an FFT of the results using numpy. Most of the results made sense to me: the FFT was the same length as the original signal. Discrete Fourier results are symmetrical about the fundamental, so I have half that many "bins" (about 246,000) to represent harmonics. The lowest frequency content in the original signal is 60 Hz. Per Nyqist, the original signal can not represent frequencies higher than 1/(2*33.92 nS) = 14.74 MHz, which is also the roughly 246,000 harmonic of 60 Hz. So each bin represents one harmonic of the 60 Hz fundamental, with no skipped bins. If I tried transforming five full sine waves instead of just one, four out of every five bins was zero, which makes sense; those bins represent fractional harmonics, which are meaningless.

Going back to my single-wave FFT, every even-index (index from zero) entry of my FFT results is zero. Every odd entry is non-zero, with index 1 being by far the largest. This makes sense if 1 is the fundamental, because the even harmonics won't show up in a symmetrical sine wave. But why is the first entry in the FFT results not the fundamental?

After this, I applied an LC filter, by iterating through the FFT results and multiplying each bin by the filter gain at that frequency. I then did an inverse FFT, giving me the filtered time-domain signal. The result has both real and imaginary components. The real component and the imaginary component, when taken separately, each look like what I expected the time domain signal to look like: a better looking sine wave than I started with, shifted by 90 degrees relative to each other.

enter image description here

But the magnitude is pretty much constant, rather than looking at all line a sine wave. This is not at all what I was expecting. What is the significance of this result?

\$\endgroup\$
  • 2
    \$\begingroup\$ A couple of plots would make this easier to follow. \$\endgroup\$ – Matt Young Oct 27 '15 at 15:13
  • 1
    \$\begingroup\$ Is numpy storing a complex number in each array element? In the C implementations I worked with so far you always had to be careful where the real and complex part ended up in the array. \$\endgroup\$ – Arsenal Oct 27 '15 at 16:00
  • \$\begingroup\$ IIRC, numpy offers several different FFT's. For example, rfft(...) assumes the time-domain data is real-valued so that the negative frequency terms don't need to be computed and stored. It would help to know exactly which FFT routine you are using. \$\endgroup\$ – The Photon Oct 27 '15 at 16:22
  • \$\begingroup\$ @ThePhoton I'm using fft() \$\endgroup\$ – Stephen Collings Oct 27 '15 at 16:28
  • \$\begingroup\$ @Arsenal numpy is storing complex numbers, yes. \$\endgroup\$ – Stephen Collings Oct 27 '15 at 16:28
4
\$\begingroup\$

But why is the first entry in the FFT results not the fundamental?

Typically the 0'th entry stores the DC term. In some schemes, the [N/2]'th entry might be the DC term and the [N/2+1]'th entry would be the fundamental.

I applied an LC filter, by iterating through the FFT results and multiplying each bin by the filter gain at that frequency. I then did an inverse FFT, ... The result has both real and imaginary components.

Did you consider the phase effect of the filter? You should be multiplying by a complex value accounting for both the magnitude and phase response of the LC filter.

Even then, due to finite precision issues, you will often still get non-zero imaginary components in the inverse-transformed time-domain signal. But usually these are very small, for example on the order of \$10^{-15}\times\$ the real components. These are usually dealt with by simply ignoring them (taking the real part of the result only). Some tools even offer a built-in function (Mathematica's Chop, for example) to eliminate these artifacts.

\$\endgroup\$
  • 1
    \$\begingroup\$ Ahha! I did not, in fact, account for the phase effect of the filter! \$\endgroup\$ – Stephen Collings Oct 27 '15 at 16:30
5
\$\begingroup\$

The first entry [1] in the FFT is your fundamental, but the zeroth entry [0] is DC!

A standard FFT will have the same number of output samples as input samples, and they will all be complex.

The results aren't necessarily symmetrical about DC. However in the special case of a real only input, they are complex conjugate about DC, which looks for all the world symmetrical if you take the power, or don't notice the sign of the imaginary part. Most FFT packages will have a special 'real input' mode which saves on processing time and array size by only producing the +ve half of the results when given a real input.

When you inverse FFT'd back, you were presumably using only the +ve half of the frequency coefficients. This will give you an analytic signal in the time domain. This is as you describe a sine and cosine, at right angles, with a constant modulus. The real input signal has both +ve and -ve frequency components, which the FFT separates.

If you instead inverse FFT using the whole of the frequency spectrum, either by doing a full complex to full complex FFT to get the frequency coefficients, or by complex conjugate reflecting them through zero, you will reconstruct your original real time waveform (with some fiddling 1e-15s kicking around where there should be zeros due to finite precision). The IFFT of the +ve half spectrum produces the result you saw, the IFFT of the -ve half produces a similar signal with the cosines in phase, but the sines in inverse phase, so the resulting sum is pure cos and zero sine.

\$\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.