Home > Software engineering >  Scaling issue taking derivative in frequency domain and transforming back to time using numpy fft
Scaling issue taking derivative in frequency domain and transforming back to time using numpy fft

Time:12-09

I am trying to take the derivative of a signal in the frequency domain using numpy. For a continuous signal this is just a multiplication of the spectrum by 2*pi*i*f.

For a discrete signal, considering the numpy FFT implementation, I thought it would be this:

enter image description here

i.e. a multiplication of the FFT by 2*pi*i*k/n

But it's not: the division by n is not required.

This is shown in the plot below: compare the green curve (with division by n) to the red-dashed curve (no division by n). I plotted the time domain derivative in black as a sanity check.

Why is the division by n not required? I feel I'm missing something obvious but can't figure it out. Have I made a silly math error that I can't see, or perhaps my understanding of numpy implementation of the DFT is incorrect?

(I'm only dealing with real input and thus using rfft/irfft, but i've tested with fft/ifft and have the same result.)

enter image description here

Here's my test code.

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn')

def sin(t, amp, freq):
    return amp * np.sin(2 * np.pi * freq * t)

dt = 0.002
t = np.arange(0,1,dt)
n = len(t)              # n is even

s = sin(t, 1, 1)   sin(t, 1, 2)
ds_grad = np.gradient(s, dt)    # time derivative

S = np.fft.rfft(s)
freqs = np.fft.rfftfreq(n, dt)

dS = np.ones(len(S), dtype='complex')    # freq derivative
for i in np.arange(len(dS)):
    dS[i] = 1/n * 2* np.pi * 1j * freqs[i] * S[i]
ds = np.fft.irfft(dS)

dS_ = np.ones(len(S), dtype='complex')    # freq deriv without 1/n
for i in np.arange(len(dS_)):
    dS_[i] = 2 * np.pi * 1j * freqs[i] * S[i]
ds_ = np.fft.irfft(dS_)

plt.figure(figsize=(12,6))
plt.plot(t, s, label='s')
plt.plot(t, ds_grad, 'k', label='s\' (t domain)')
plt.plot(t, ds, label='s\' (freq domain)' )
plt.plot(t, ds_, '--', label='s\' (freq domain: no div by n)' )
plt.legend(loc='lower left')
plt.show()

CodePudding user response:

The multiplication by 2*pi*i*k/n is correct for a time-domain signal sampled at integer positions (ie the signal is s[i] for i=0..n-1). If you want to take the sample locations into account, you have to additionally divide by dt, which in your case happens to be equivalent to multiplying by n.

  • Related