I'm doing an FFT using Python and Numpy on one machine, and C# on another. I'm using some dummy data that mimics how I'll eventually be gathering data from sensors in the C#/UWP application. The two methods yield consistently identical results on the dummy data, which is great. However, the phase at the sine wave's frequency is always -90°.
Here's the code. Ignore how it essentially does the same thing 7 times, eventually those arrays will be filled with data from 7 different sensors. Sampling frequency is 4096 Hz, signals are gathered for 1 second and are on the interval [-32768,32768]. Note that the x-axes on the Magnitude and Phase graphs are zoomed in to the center frequency plus or minus several Hertz.
import numpy as np
import matplotlib.pyplot as plt
import math
centerf = 1350
span = 2*np.pi*centerf*np.linspace(0,4095,4096)/4096
testData = 32768*np.sin(span)
testData = np.array([math.trunc(x) for x in testData])
plt.close("all")
sensors = ["Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7"]
data = []
data.append(testData)
data.append(testData)
data.append(testData)
data.append(testData)
data.append(testData)
data.append(testData)
data.append(testData)
theTime = np.linspace(0,len(data[0]),len(data[0]))/len(data[0])
thisFFT = [np.fft.fft(x) for x in data]
f, a = plt.subplots(7, 1,figsize=(10,8),sharex=True)
f2,a2 = plt.subplots(7,1,figsize=(10,8),sharex=True)
f3,a3 = plt.subplots(7,1,figsize=(10,8),sharex=True)
mag = [np.abs(x)/2048 for x in thisFFT]
phase2 = [np.arctan2(x.imag,x.real)*180/np.pi for x in thisFFT]
mag = [x[:2048] for x in mag] # removed mirrored upper half
phase2 = [x[:2048] for x in phase2]
for x in mag:
x[0] = 0 # remove DC component
[aa.plot(theTime,d,c='r',lw=0.5) for (aa,d) in zip(a,data)]
[aa.plot(d,c='g',lw=0.75) for (aa,d) in zip(a2,mag)]
[aa.plot(d,c='b',lw=0.75) for (aa,d) in zip(a3,phase2)]
for ch,ax in zip(sensors,a.flat):
ax.set(ylabel=ch)
for ch,ax in zip(sensors,a2.flat):
ax.set(ylabel=ch)
for ch,ax in zip(sensors,a3.flat):
ax.set(ylabel=ch)
a.flat[0].set(title="Amplitude over 1s")
a2.flat[0].set(title="FFT Magnitude")
a3.flat[0].set(title="FFT Phase")
a.flat[5].set(xlabel='Time')
[aa.axvline(x=centerf,lw=0.25) for aa in a2]
[aa.axvline(x=centerf,lw=0.25) for aa in a3]
[aa.set_xlim([centerf-20,centerf+20]) for aa in a2]
[aa.set_xlim([centerf-20,centerf+20]) for aa in a3]
f.tight_layout()
f2.tight_layout()
f3.tight_layout()
print("Magnitude at centerf: {c:1.3f}.".format(c=mag[0][centerf]))
print("Phase at centerf: {c:1.3f}°.".format(c=phase2[0][centerf]))
The output from the print statements is
Magnitude at centerf: 32767.397.
Phase at centerf: -90.000°.
The magnitude is fine, the graph always shows a nice peak right where it should be.
My question: since the input data is a sine wave that always starts at sin(0)=0 and the FFT algorithm uses a rectangular window, shouldn't the phase at the fundamental frequency be 0°? Is there something about phase that I'm not understanding?