2

I am struggling with numpy's implementation of the fast Fourier transform. My signal is not of periodic nature and therefore certainly not an ideal candidate, the result of the FFT however is far from what I was expecting. It is the same signal, simply stretched by some factor. I plotted a sinus curve, approximating my signal next to it which should illustrate, that I use the FFT function correctly:

import numpy as np
from matplotlib import pyplot as plt

signal = array([[ 0.], [ 0.1667557 ], [ 0.31103874], [ 0.44339886], [ 0.50747922],
    [ 0.47848347], [ 0.64544846], [ 0.67861755], [ 0.69268326], [ 0.71581176],
    [ 0.726552  ], [ 0.75032795], [ 0.77133769], [ 0.77379966], [ 0.80519187],
    [ 0.78756476], [ 0.84179849], [ 0.85406538], [ 0.82852684], [ 0.87172407],
    [ 0.9055542 ], [ 0.90563205], [ 0.92073452], [ 0.91178145], [ 0.8795554 ],
    [ 0.89155587], [ 0.87965686], [ 0.91819571], [ 0.95774404], [ 0.95432073],
    [ 0.96326252], [ 0.99480947], [ 0.94754962], [ 0.9818627 ], [ 0.9804966 ],
    [ 1.], [ 0.99919711], [ 0.97202208], [ 0.99065786], [ 0.90567128],
    [ 0.94300558], [ 0.89839004], [ 0.87312245], [ 0.86288378], [ 0.87301008],
    [ 0.78184963], [ 0.73774451], [ 0.7450479 ], [ 0.67291666], [ 0.63518575],
    [ 0.57036157], [ 0.5709147 ], [ 0.63079811], [ 0.61821523], [ 0.49526048],
    [ 0.4434457 ], [ 0.29746173], [ 0.13024641], [ 0.17631683], [ 0.08590552]])

sinus = np.sin(np.linspace(0, np.pi, 60))

plt.plot(signal)
plt.plot(sinus)

The blue line is my signal, the green line is the sinus.

raw.pdf

transformed_signal = abs(np.fft.fft(signal)[:30] / len(signal))
transformed_sinus = abs(np.fft.fft(sinus)[:30] / len(sinus))

plt.plot(transformed_signal)
plt.plot(transformed_sinus)

The blue line is transformed_signal, the green line is the transformed_sinus.

fft.pdf

Plotting only transformed_signal illustrates the behavior described above:

enter image description here

Can someone explain to me what's going on here?

UPDATE

I was indeed a problem of calling the FFT. This is the correct call and the correct result:

transformed_signal = abs(np.fft.fft(signal,axis=0)[:30] / len(signal))

enter image description here

Paul
  • 999
  • 1
  • 12
  • 29

2 Answers2

4

Numpy's fft is by default applied over rows. Since your signal variable is a column vector, fft is applied over the rows consisting of one element and returns the one-point FFT of each element.

Use the axis option of fft to specify that you want FFT applied over the columns of signal, i.e.,

transformed_signal = abs(np.fft.fft(signal,axis=0)[:30] / len(signal))
Stelios
  • 5,271
  • 1
  • 18
  • 32
  • Or, if the intent was for `signal` to be a 1D vector, transform it with something like `np.array([x[0] for x in signal])` – mtrw Aug 07 '16 at 09:41
1

[EDIT] I overlooked the crucial thing stated by Stelios! Nevertheless I leave my answer here, since, while not spotting the root cause of your trouble, it is still true and contains things you have to reckon with for a useable FFT.

As you say you're tranforming a non-periodical signal. Your signal has some ripples (higher harmonics) which nicely show up in the FFT. The sine does have far less higher freq's and consists largely of a DC component.

So far so good. What I don't understand is that your signal also has a DC component, which doesn't show up at all. Could be that this is a matter of scale.

Core of the matter is that while the sinus and your signal look quite the same, they have a totally different harmonic content.

Most notable none of both hold a frequency that corresponds to the half sinus. This is because a 'half sinus' isn't built by summing whole sinusses. In other words: the underlying full sinus wave isn't in the spectral content of the sinus over half the period.

BTW having only 60 samples is a bit meager, Shannon states that your sample frequency should be at least twice the highest signal frequency, otherwise aliasing will happen (mapping freqs to the wrong place). In other words: your signal should visually appear smooth after sampling (unless of course it is discontinuous or has a discontinuous derivative, like a block or triangle wave). But in your case it looks like the sharp peaks are an artifact of undersampling.

Jacques de Hooge
  • 6,750
  • 2
  • 28
  • 45
  • I'm quite new to the FFT and don't completely understand your answer. I think I understand that the signals look completely different to the FFT and therefor will produce completely different results. However I'm not sure what you mean with the DC component. Why do you think there should be one? And what would you suggest to do in order to alleviate this possible problem of scale? I will try to find a longer signal to account for the aliasing. – Paul Aug 07 '16 at 09:26
  • @Paul Jacques is referring to the [Nyquist–Shannon sampling theorem](https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem). Your sampling frequency needs to be twice the frequency of the highest frequency component in your signal that you want to capture. – PM 2Ring Aug 07 '16 at 09:30
  • As an indication: Use at least 20 full periods of the lowest freq you want to detect, and at least 2 samples per full period of the highest freq. And attenuate your signal gradually at the start and at the end to prevent discontinuities in the signal and it's derivatives (so cutting off at zero won't work, because the derivatives will make jump then) – Jacques de Hooge Aug 07 '16 at 09:43
  • 1
    A DC component is an average value, denoted by a cosinus of frequency 0. – Jacques de Hooge Aug 07 '16 at 09:56
  • I see that my signal may be too short for reliable results. Thank you for your answer and yes, please leave it. I think it is very helpful to understand what is going on. – Paul Aug 07 '16 at 10:01