Found the answer in numpy documents for fft:
# python to perform dft
# from import numpy.fft import *
A = fft(a, n)
A[0] contains the zero-frequency term (the sum of the signal), which is always purely real for real inputs.
A[1:n/2] contains the positive-frequency terms
A[n/2+1:] contains the negative-frequency terms, in order of decreasingly negative frequency.
For an even number of input points, A[n/2] represents both positive and negative Nyquist frequency, and is also purely real for real input.
For an odd number of input points, A[(n-1)/2] contains the largest positive frequency, while A[(n+1)/2] contains the largest negative frequency.
There are many ways to define the DFT, varying in the sign of the exponent, normalization, etc. In this implementation, the DFT is defined as
$$
A_k = \sum_{m=0}^{n-1} a_m exp \left\{ -2 \pi i \frac{mk}{n} \right\},\ \ \ \ k=0,...,n-1
$$
The DFT is in general defined for complex inputs and outputs, and a single-frequency component at linear frequency is represented by a complex exponential , where is the sampling interval.
The values in the result follow so-called “standard” order:
Correction to code above:
# python DFT Sin Test
# Bill Moore
# 4/11/2019
import sys
import numpy as np
import matplotlib.pyplot as plt
def dsp_mag_and_phase(DIN, N, filename):
"""
Cleanup and plots DFT and FFT results
N : Number of frequency bins
filename : png image file to save dft magnitute plot
"""
print("=> DSP_FREQPLOT")
XK = DIN
K = range(0, N)
# Swap Positive and Negative Frequencies of DFT/FFT
# (to get standard spectrum of -pi to pi)
# Assign a positive or negative frequency to each point
KK = []
for k in K:
if k>=0 and k <= int(N/2):
KK.append(k)
else:
KK.append(k - int(N))
#print("KK:", KK)
#print("len(KK)=", len(KK))
#sys.exit()
AM = np.zeros(N)
AP = np.zeros(N)
AM = np.zeros(N)
AP = np.zeros(N)
#Positive Frequences
AX_pos = XK[0:int(N/2)+1]
AK_pos = KK[0:int(N/2)+1]
#Negative Frequences
AK_neg = KK[int(N/2)+1:]
AX_neg = XK[int(N/2)+1:]
# Concatenate negative then positive
AX = np.concatenate((AX_neg, AX_pos))
AK = np.concatenate((AK_neg, AK_pos))
# Convert Complex Frequency into Magnitude and Phase
for k in range(0, len(AK)):
AM[k] = np.sqrt((AX[k].real)**2 + (AX[k].imag)**2)
AP[k] = np.arctan2(AX[k].imag, AX[k].real)
return (AK, AM, AP)
def dsp_dft(DIN, N, filename):
"""
plots N-point DFT
N : Number of frequency bins
filename : png image file to save dft magnitute plot
"""
print("=> DSP_DFT_MAGNITUDE")
DFT = np.zeros(N, dtype=np.complex)
K = range(0, N)
w0 = 2 * np.pi / N #Fundamental Frequency (Radians)
# DFT
for k in K:
DFT[k]=0
for n in range(0,len(DIN)):
DFT[k] = DFT[k] + DIN[n]*np.exp(-1j*k*w0*n)
DFT[k] = (1/N)*DFT[k]
# Covert Complex to Magnitude and Phase
(AK, AM, AP) = dsp_mag_and_phase(DFT, N, filename)
plot_mag_and_phase(AK, AM, AP, "DFT", filename + ".png")
return (AK, AM, AP)
def dsp_fft(DIN, N, filename):
"""
plots N-point FFT
(FFT is just a computationally faster DFT)
N : Number of frequency bins
filename : png image file to save dft magnitute plot
"""
print("=> DSP_FFT_MAGNITUDE")
# FFT Points equal to length of DIN (default for fft function)
if N == 0:
N = len(DIN)
FFT = np.fft.fft(DIN, N)
(AK, AM, AP) = dsp_mag_and_phase(FFT, N, filename)
plot_mag_and_phase(AK, AM, AP, "FFT", filename + ".png")
return (AK, AM, AP)
def plot_stem(x, y, title, filename):
print("=> PLOT_STEM")
fig = plt.figure()
plt.title(title)
plt.stem(x, y)
print("creating file: ", filename)
fig.savefig(filename)
plt.close()
def plot_mag_and_phase(k, mag, phase, title, filename):
print("=> plot_mag_and_phase")
fig = plt.figure()
plt.clf()
plt.subplot("211")
plt.title(title)
plt.ylabel("Magnitude")
plt.plot(k,mag)
plt.subplot("212")
plt.ylabel("Phase")
plt.plot(k,phase)
print("creating file: ", filename)
fig.savefig(filename)
plt.close()
def dsp_range(sampleN, sampleF):
"""
return : time points for sampling at a given frequency
sampleN : Number of Samples
sampleF : Sample Frequency in Hz
"""
sampleT = 1 / sampleF
time_range = np.linspace(0.0, sampleN*sampleT, sampleN)
return time_range;
def run():
print("=> RUN")
sampleF = 1E3
sampleN = 100
Fsin = sampleF/4
# sample function
tvec = dsp_range(sampleN, sampleF)
y1 = np.sin(2*np.pi*Fsin*tvec)
plot_stem(tvec, y1, "samples", "plot_test0.png")
dsp_dft(y1, 100, "plot_test1")
dsp_fft(y1, 0, "plot_test2")
sys.exit()
run()
