A wave can be defined by:
What is the phase spectrum?
We know that the fourier transform provides the frequency domain information about the signal. The phase spectrum provides the individual phases of each individual frequency component in the frequency domain representation. To gain a better understanding, let’s visualize it in a few plots.
Let’s start by visualizing the amplitude (of frequency) and phase spectrum of a simple cosine wave.
Note: All of the function definitions can be found at the bottom of this page.
plot_spectrums(= [1], # frequency in hertz
frequencies = [1],
amplitudes = [0] # phase shifts in radians
phases )
Now in order to obtain a sine wave, we can just shift the phase (forward or backward) by 90 degrees (pi/2 radians).
plot_spectrums(= [1], # frequency in hertz
frequencies = [1],
amplitudes = [np.pi/2] # phase shifts in radians
phases )
Now let’s add one more wave to the mix:
plot_spectrums(= [1, 3], # frequency in hertz
frequencies = [1, 0.5],
amplitudes = [np.pi/2, 0] # phase shifts in radians
phases )
Watch what happens when we change the phase of only the newly added wave.
animate_spectrums(= [1, 3] # Frequencies in Hertz
frequencies = [1, 0.5], # Amplitudes
amplitudes = [np.pi/2, 0], # Initial phase shifts in radians
initial_phases =2,
cycles=30,
fps=8
duration )
In order to gain a better intuition, let’s also see what happens when we vary the amplitude of the new wave.
animate_amplitude_variation(= [1, 3], # Frequencies in Hertz
frequencies = [1, 0.5], # Initial Amplitude
initial_amplitudes = [np.pi/2, 0], # Phase shifts in radians
phases =2,
cycles=30,
fps=10
duration )
This is my conclusion:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation
import numpy as np
import matplotlib.pyplot as plt
def plot_spectrums(frequencies, amplitudes, phases):
"""
Plot the amplitude spectrum, phase spectrum, and reconstructed signal
from the given frequencies, amplitudes, and phase spectrum.
"""
# Ensure inputs are numpy arrays for easier math operations
= np.array(frequencies)
frequencies = np.array(amplitudes)
amplitudes = np.array(phases)
phases
# Number of sample points for reconstruction
= 500
N # Time vector for reconstruction
= np.linspace(0, 2*np.pi, N)
t
# Reconstruct signal from its Fourier components
= np.zeros(N)
signal for i in range(len(frequencies)):
+= amplitudes[i] * np.cos(frequencies[i] * t + phases[i])
signal
# Plotting
= plt.subplots(3, 1, figsize=(10, 8))
fig, axs
# Amplitude Spectrum
0].stem(frequencies, amplitudes, basefmt=" ", use_line_collection=True)
axs[0].set_title('Amplitude Spectrum')
axs[0].set_xlabel('Frequency (Hz)')
axs[0].set_ylabel('Amplitude')
axs[0].grid(True)
axs[
# Phase Spectrum
1].stem(frequencies, phases, basefmt=" ", use_line_collection=True)
axs[1].set_title('Phase Spectrum')
axs[1].set_xlabel('Frequency (Hz)')
axs[1].set_ylabel('Phase (radians)')
axs[1].grid(True)
axs[
# Reconstructed Signal
2].plot(t, signal)
axs[2].set_title('Reconstructed Signal from Amplitude and Phase Spectrums')
axs[2].set_xlabel('Time')
axs[2].set_ylabel('Signal Amplitude')
axs[2].grid(True)
axs[
plt.tight_layout()
plt.show()
# Adjusted function for animation
def animate_spectrums(frequencies, amplitudes, initial_phases, cycles=1, fps=30, duration=10):
"""
Create an animation varying the second phase from 0 to 2*pi radians and back.
Parameters:
frequencies (array-like): Frequency components.
amplitudes (array-like): Amplitude of each frequency component.
initial_phases (array-like): Initial phase (in radians) of each frequency component.
cycles (int): Number of cycles of phase variation.
fps (int): Frames per second in the animation.
duration (int): Duration of the animation in seconds.
"""
= plt.subplots(3, 1, figsize=(10, 8))
fig, axs
# Total frames for the animation
= fps * duration
frames
def update(frame):
# Clear previous plots
for ax in axs:
ax.clear()
# Calculate current phase for the second component
= (frame % (frames // (2 * cycles))) / (frames // (2 * cycles))
t = 2 * np.pi * t
phase_shift if frame >= frames // 2:
= 2 * np.pi - phase_shift
phase_shift = initial_phases.copy()
phases 1] += phase_shift
phases[
# Reconstruct signal with updated phase
= np.zeros(500)
signal = np.linspace(0, 2*np.pi, 500)
t for i in range(len(frequencies)):
+= amplitudes[i] * np.cos(frequencies[i] * t + phases[i])
signal
# Update plots
0].stem(frequencies, amplitudes, basefmt=" ", use_line_collection=True)
axs[1].stem(frequencies, phases, basefmt=" ", use_line_collection=True)
axs[2].plot(t, signal)
axs[
# Set titles and labels
0].set_title('Amplitude Spectrum')
axs[0].set_xlabel('Frequency (Hz)')
axs[0].set_ylabel('Amplitude')
axs[0].grid()
axs[
1].set_title('Phase Spectrum')
axs[1].set_xlabel('Frequency (Hz)')
axs[1].set_ylabel('Phase (radians)')
axs[1].set_ylim(0, 2 * np.pi*1.05)
axs[1].grid()
axs[
2].set_title('Reconstructed Signal')
axs[2].set_xlabel('Time')
axs[2].set_ylabel('Signal Amplitude')
axs[2].grid()
axs[2].set_ylim(-2, 2)
axs[
plt.tight_layout()
= FuncAnimation(fig, update, frames=np.arange(0, frames), blit=False)
anim
# Save animation
= animation.FFMpegWriter(fps=fps, codec='libx264', extra_args=['-preset', 'veryslow', '-qp', '0'])
FFwriter 'phase_animation.mp4', writer=FFwriter)
anim.save(
def animate_amplitude_variation(frequencies, initial_amplitudes, phases, cycles=1, fps=30, duration=10):
"""
Create an animation varying the amplitude of the second frequency component.
Parameters:
frequencies (array-like): Frequency components.
initial_amplitudes (array-like): Initial amplitude of each frequency component.
phases (array-like): Phase (in radians) of each frequency component.
cycles (int): Number of cycles of amplitude variation.
fps (int): Frames per second in the animation.
duration (int): Duration of the animation in seconds.
"""
= plt.subplots(3, 1, figsize=(10, 8))
fig, axs
# Total frames for the animation
= fps * duration
frames
def update(frame):
# Clear previous plots
for ax in axs:
ax.clear()
# Calculate current amplitude for the second component
= (frame % (frames // (2 * cycles))) / (frames // (2 * cycles))
t = np.abs(np.sin(np.pi * t))
amplitude_modulation = initial_amplitudes.copy()
amplitudes 1] *= amplitude_modulation
amplitudes[
# Reconstruct signal with updated amplitude
= np.zeros(500)
signal = np.linspace(0, 2*np.pi, 500)
t for i in range(len(frequencies)):
+= amplitudes[i] * np.cos(frequencies[i] * t + phases[i])
signal
# Update plots
0].stem(frequencies, amplitudes, basefmt=" ", use_line_collection=True)
axs[1].stem(frequencies, phases, basefmt=" ", use_line_collection=True)
axs[2].plot(t, signal)
axs[
# Set titles and labels
0].set_title('Amplitude Spectrum')
axs[0].set_xlabel('Frequency (Hz)')
axs[0].set_ylabel('Amplitude')
axs[0].grid()
axs[
1].set_title('Phase Spectrum')
axs[1].set_xlabel('Frequency (Hz)')
axs[1].set_ylabel('Phase (radians)')
axs[1].set_ylim(0, 2 * np.pi*1.05)
axs[1].grid()
axs[
2].set_title('Reconstructed Signal')
axs[2].set_xlabel('Time')
axs[2].set_ylabel('Signal Amplitude')
axs[2].grid()
axs[2].set_ylim(-2, 2)
axs[
plt.tight_layout()
= FuncAnimation(fig, update, frames=np.arange(0, frames), blit=False)
anim
# Save animation
= animation.FFMpegWriter(fps=fps, codec='libx264', extra_args=['-preset', 'veryslow', '-qp', '0'])
FFwriter 'amplitude_variation_animation.mp4', writer=FFwriter) anim.save(