Creating the Frequency Vector
The arrangement of the output of fft() depends on whether you use an odd or even number of points for your fft. I think this post nicely summarises how the frequencies are arranged. Have a look at it.
- Since you are using an even number of points, the Nyquist frequency, $F_N = F_s/2$, is present in the output of your fft, and is the maximum value in your frequency vector. Your sampling frequency is $N/T=100/10=10\;\textrm{Hz}$, and so your maximum detectable frequency in the vector will be $F_N=5\;\textrm{Hz}$.
- Secondly, no matter whether you use even or odd points, the DC value is always the first entry in the fft output.
- The frequency resolution $df = F_s/N$ is the separation between fft output bins in frequency space. For you, this will be $0.1\;\textrm{Hz}$.
- Finally, it is also most common to display the frequency spectrum in a symmetric, centered way, with the DC frequency in the middle - the function fftshift does this for you.
Accounting for all this, your frequency vector corresponding to your fft output (after you have applied fftshift() to centre it) will be
$$
f = [-F_N : df : (F_N-df)]
$$
There is some discussion about fftshift in this thread if you are interested. Notice that after fftshift() the component corresponding to the Nyquist, $F_N$, is placed at the beginning of the vector, and so is considered a negative frequency from the point of view of fftshift() - there is no Nyquist at the end of the vector in the positive frequencies (then in contrast, for symmetry, the DC component is viewed as a positive frequency).
Plotting the Spectrum with Correct Scaling
Personally, I prefer to deal with these things in terms of the power of a wave (rate of change of energy) instead of the amplitude of a component in a fft bin. This just helps me to keep track of the units, and to keep things physical. The following code calculates the power spectral density, $S_{xx}$, for your signal using a simple periodogram estimate (I assume your signal is in volts - may or may not be true - but could be anything: temperature, stock prices, etc). Sorry, but my code is in Matlab, not Python, but hopefully you can follow the steps without problem.
N = 100; % Number of samples
T = 10; % Record window duration
dt = T/N; % Sampling period
Fs = 1/dt; % Sampling frequency
t = 0:dt:(T-dt); % Time vector for sampling
% generate samples at the specified times
x = 5*sin(2*pi*3*t - 2); % units: [V]
df = Fs/N; % frequency resolution (bin width in frequency space)
% generate frequency vector for 2-sided spectrum (NOTE, this arrangement
% only works for even number of points - otherwise, use f = -(Fs/2-df/2):df:(Fs/2-df/2))
f = -(Fs/2):df:(Fs/2-df);
% Calculate Fourier transform (approximating CFT), and shift DC term to centre
X = fftshift(fft(x))*dt; % units: [V sec]
X((abs(X)<1e-10)) = 0; % kill values below threshold, so phase is well-behaved
% Calculate power spectral density using periodogram estimation
Sxx = (X.*conj(X))/(N*dt); % units: [V^2 / Hz]
figure; stem(f,Sxx) % Plot power spectral density
figure; stem(f,angle(X)) % Plot phase
The variable $X$ in the code is a discrete approximation of the continuous Fourier transform integral, which is why it is multiplied by the sampling period $dt$ (the approximation is a Riemann sum). Then the periodogram can be calculated from this - again, in a discrete version of its continuous counterpart, which is explained very nicely I think in this answer. Note, Matlab also has a built-in function periodogram()
, which does the calculation as well.
You can see that there are peaks correctly at $\pm 3\;\textrm{Hz}$, matching exactly your input signal (remember the sin wave is actually a sum of complex exponentials).
-- Note that in signal processing, the "energy" is defined as [signal squared multipled by time], and so therefore power is given as [energy per time], which is then [signal squared]. This is why you see "power" in my answer given in units of $[V^2]$. To find the physical power in [Watts] you would simply need to scale by the resistance of whatever the load is you are driving. This is usually set to 1 in signal analysis --
Verifying Parseval's Theorem
You can also verify the scaling of the fft output by checking that the power and energy in both the time domain and frequency domain are equal, as they should be according to Parseval's Theorem. Remember that if you integrate the power spectral density over all frequencies, you should obtain the total power that was present in the input signal. For example, using
Energy_timedomain = sum(x.*conj(x))*dt = 125 [V^2 sec]
Power_timedomain = Energy_timedomain/T = 12.5 [V^2]
Energy_freqdomain = sum(X.*conj(X))*df = 125 [V^2 sec]
Power_freqdomain = sum(Sxx)*df = 12.5 [V^2]
Note that if you take a longer time window duration $T$ for your input signal then the "energy" will increase (because there is more of it), but the average power will stay the same (because that is the rate of change of energy).
Recovering the Amplitude
The value of each peak in the power spectral density plot is $62.5\; [\textrm{V}^2 / \textrm{Hz}]$. To convert this to power, we need to multiply by the bin width (essentially integrating the power spectral density over that bin). In your case, the bin width is $df=F_s/N = 10\;\textrm{Hz}/100 = 0.1\;\textrm{Hz}$. Therefore, the total power in the input signal at that frequency is
$$ P = 2 \times 62.5\;[V^2/\textrm{Hz}] \times 0.1\;[\textrm{Hz}] = 12.5\;[V^2] $$
where the factor of $2$ is because there are 2 peaks - half of the power in the negative part of the spectrum and half in the positive part. Note that if your input signal is real, then the output spectrum will always be symmetric, and you will often see the negative frequencies (which don't contain any extra information) discarded. If you choose to throw away the negative frequencies then you should multiply the power in the unique positive frequencies that are left by 2 to compensate (but don't multiply the DC or the Nyquist by 2, because there are only one of each of those in the fft output - they don't have negative counterparts).
We know that for sinusoids the power and amplitude $A$ are related by $P = A^2/2$ (again, just setting any load resistance to $1$). Therefore, rearranging this, your amplitude of the 3 Hz wave is
$$A = \sqrt{2\times 12.5} = 5\;[V]$$
Recovering Phase
As Hilmar said, the phase of $-2$ can be recovered by taking the phase value at $3\;\textrm{Hz}$, which is 2.712, and adding $\pi/2$ then subtracting $2\pi$. The $\pi/2$ is because your input signal is a sine wave, which essentially includes a phase shift already because
$$
\sin(2\pi f_0 t + \phi) = \cos(2\pi f_0 t + (\phi - \pi/2)) = \cos(2\pi f_0 t + \phi_{\textrm{fft}})
$$
so you need to add $\pi/2$ to the phase returned by the fft() to get your input phase
$$\phi = \phi_{\textrm{fft}} + \pi/2 = 2.712 + \pi/2 = 4.283.$$
The final $2\pi$ subtraction is because after doing this you are left with something greater than $\pi$, and so you just need to unwrap the phase to bring it back into the range $[-\pi ... +\pi]$.
A FINAL NOTE OF CAUTION
It all works nicely in this case because all of your signal energy falls exactly in a single fft bin, due to the combination of the sampling rate, number of points, time window, and your input frequency wave (see here). Things are more difficult if you have any windowing or zero-padding applied to your input signal before you pass it to fft(). There are some enlightening answers here and here, when you get that far.