4
votes

I'm plotting sine waves (left column) and their respective frequency domain representations (right column):

  • The first wave (amplitude: 10; frequency: 0.5) has a somewhat messed up fft representation
  • The second wave (amplitude: 15; frequency: 5.0) looks absolutely as expected.
  • The third wave is just the first and the second wave summed up and inherits the problems

The second frequency plot has exactly one peak at x=5 (frequency), y=15 (amplitude).

Why does the first frequency plot have multiple peaks when there's only one frequency?

enter image description here

import numpy as np
import matplotlib.pyplot as plt

def sine(freq, time_interval, rate, amp=1):
    w = 2. * np.pi * freq
    t = np.linspace(0, time_interval, time_interval*rate)
    y = amp*np.sin(w * t)
    return y

def buildData():
    secs = 3
    Fs = 44100
    # frequency, duration, sampling rate, amplitude
    y1 = sine(0.5, secs, Fs, 10)
    y2 = sine(5, secs, Fs, 15)
    y3 = y1 + y2
    signals = [y1, y2, y3]
    showSignals(signals, Fs, secs)

def showSignals(signals, fs, secs):
        nrSigs = len(signals)
        fig = plt.figure()
        fig.subplots_adjust(hspace=.5)
        for i in range(len(signals)):
            cols=2
            pltIdc = []
            for col in range(1,cols+1):
                pltIdc.append(i*cols+col)
            s = signals[i]
            t = np.arange(0, secs, 1.0/fs)
            ax1 = plt.subplot(nrSigs, cols, pltIdc[0])
            ax1.set_title('signal')
            ax1.set_xlabel('time')
            ax1.set_ylabel('amplitude')
            ax1.plot(t, s)

            amps = 2*abs(np.fft.fft(s))/len(s)  # scaled power spectrum
            amps = amps[0:len(amps)/2]  # because of the symmetry
            amps = amps[0:50]  # only the first 50 frequencies, arbitrarily chosen
            # this should be close to the amplitude:
            print 'magnitude of amplitudes: ' + str(sum(amps*amps)**0.5)
            freqs=np.arange(0, len(amps), 1)/secs
            ax2 = plt.subplot(nrSigs, cols, pltIdc[1])
            ax2.grid(True)
            ax2.set_title(r"$\frac{2 \cdot fft(s)}{len(s)}$")
            ax2.set_xlabel('freq')
            ax2.set_ylabel('amplitude')
            ax2.stem(freqs, amps)
        plt.show()

buildData()
2

2 Answers

2
votes

The FFT routine performs a (fast implementation) discrete Fourier transform, which decomposes a time-series signal into a N-length orthonormal basis consisting of the Fourier "roots of unity".

You will get a discrete, single value of the FFT output if and only if you input a signal that is one of the Fourier basis functions (or a phase-rotated version thereof) because it will have a non-zero inner product with one and only one member of the basis set (by definition).

Your first example has 1.5 cycles within the analysis window, so it cannot be a root of unity (one property of the Fourier basis functions is that they have integral cycle counts within the analysis window). Consequently, there is a non-zero "DC offset" (the average over the analysis window is not exactly zero), which will always yield a "DC" term (nonzero Fourier contribution at index 0 corresponding to a DC offset). Because it's a non-integral cycle count within the analysis window, you also get contributions from other frequencies out of the FFT in addition to the dominant contribution from the frequency nearest that of your sinusoid. This is as expected - any sinusoid that is not itself a fourier basis function will have non-zero inner product with multiple fourier basis functions (and hence multiple spectral contributions in the FFT output).

Your 3rd example is just the sum of the two others, so by linearity of the Fourier transform the output of the FFT is simply the sum of the FFTs of the two individual signals. This is also expected: FFT(a+b) = FFT(a) + FFT(b).

1
votes

A DFT or FFT will only produce a single point result (spike in the graph) from a sinusoid if the frequency is periodic in exactly an integer number of periods within the FFTs length. Otherwise the energy will get spread out among all the other FFT result bins (but mostly in nearby result frequency bins). This is not "messed up" but normal expected behavior for finite length DFTs.