Closed a-z-e-r-i-l-a closed 4 years ago
In particular, unlike the FFT, the NFFT cannot exactly reconstruct a given signal from a band-limited frequency representation, because in general the effective Nyquist frequency of an unevenly-sampled signal is very large. Your code is working as expected, and if you evaluate the NFFT at a wider frequency grid, your reconstruction will have more fidelity. For example:
N = 100000
k = -N / 2 + np.arange(N)
r_hat = nfft.nfft_adjoint(z, r, N)
r_reconstructed = nfft.nfft(z, r_hat) / N
plt.plot(z, r, '.', color='blue')
plt.plot(z, r_reconstructed, '-', color='red')
@jakevdp Thank you. How about if we want to reconstruct the signal by sum of Sines or Cosines functions how can we find the coefficients regarding amplitude, phase shift, and frequency.
Something similar to:
s = r0
for i in range(N):
s += amp[i]*np.sin(w[i]*z +phi[i])
That's precisely what the NFFT calculates (well, generalized to complex exponentials).
If we use original fft for example and assume the sampling period is constant 13 ( which is not true), I reproduce the signal like the following using a cosine function:
from scipy.fftpack import fft
Ts = 13.00
fs = 1/Ts
rf = fft(r)
amp = np.abs(rf)/r.shape[0]
phi = np.angle(rf)
s = amp[0]
for i in range(n//2):
if i>0:
k = i*fs/n
s += 2*amp[i]*np.cos(2*np.pi*k *(z) +phi[i])
plt.plot(z, s, label='fft result')
plt.plot(z, r, label='data')
plt.legend()
which because of the non-uniform sampling period, the reconstruction is not good.
But if I use nfft:
N = 100000
r_hat = nfft.nfft_adjoint(z, r, N)
r_reconstructed = nfft.nfft(z, r_hat) / N
I haven't been able to do the same. What doesn't make sense to me is the amplitudes first. For example I don't find the DC constant amplitude which is approx. 110.
np.abs(r_reconstructed)
array([119.05166608, 119.79233619, 119.77732404, 119.85819179, 119.23368786, 119.98718045, 119.28788431, 117.64866232, 114.24173965, 109.24599923, 104.89324134, 101.60449633, 100.463555 , 99.78755809, 99.97843652, 100.06441681, 100.13686047, 100.07421509, 100.25667645, 100.5371113 , 101.24062906, 102.73378377, 105.07478861, 108.06663284, 111.67608816, 114.89775712, 116.99564855, 118.2330805 , 118.81533953, 119.31003932, 119.38999452, 119.54839654, 119.61643871, 119.68894329, 119.75074337, 119.48817088, 118.7799233 , 116.77295372, 113.30843251, 107.94473218, 104.48122313, 101.93228577, 100.72753523, 100.1778597 , 100.09855226, 100.00882029, 100.11827024, 100.16555776, 100.38244013, 100.62187254, 100.92887294, 101.50904132, 103.42187277, 105.59074733, 108.4194843 , 111.86892277, 114.65794209, 116.70597202, 117.95747533, 118.77837264, 119.15335268, 119.39858413, 119.57918671, 119.61015863])
or:
np.abs(r_hat)
array([ 953.28285834, 847.26002895, 1514.53901906, ..., 659.27302669, 1514.53901906, 847.26002895])
The DC offset is here:
>>> r_hat[len(r_hat) // 2] / len(z)
(110.40382390478732+0j)
If you want to understand what's going on mathematically, I'd suggest starting with the two formulas in the documentation: https://github.com/jakevdp/nfft#about
All this package does is compute those sums in ~O[Mlog(N)]
rather than ~O[MN]
if you have problems that you can express in terms of those sums, this package will help you. Otherwise, it won't.
I have a signal r evaluated at time z which looks like the below graph for each time step z.
Since the sampling period is not constant, I'm using the nfft from which I should get the frequency components with complex numbers that represent a magnitude and phase.
rf = nfft.nfft(z, r)
Then I want to reproduce the signal with the nfft result:But it looks not at all like the original signal.