跳转至

实信号与复信号的频谱实验

大二下,信号与系统作业。

  1. 画出 \(\cos(\omega_0 t + \phi)\) 的频谱,其中 \(\omega_0\) 的数值分别为 \(1,3,6\)\(\phi\) 的数值分别为 \(15^\circ, -30^\circ, 60^\circ\)
  2. 画出 \(\exp(j(\omega_0 t + \phi))\) 的频谱,其中 \(\omega_0\) 的数值分别为 \(1,3,6\)\(\phi\) 的数值分别为 \(15^\circ, -30^\circ, 60^\circ\)
  3. 将第二问中的 3 个复信号两两组合,写出获得的 3 个信号的表达式,分别画出其实部信号的频谱。

打开 Jupyter Notebook 版

前置准备

# 导入必要的库
import numpy as np
import matplotlib.pyplot as plt

# 题目中的 omega 和 phi 的取值
omega_values = [1, 3, 6]
phi_values = list(map(np.radians, [15, -30, 60]))

因为题目给的函数对应的 \(F(j\omega)\) 只在某几个点处有取值,所以可以用 DFT 计算它。

先在一个周期 \([0, 2\pi)\) 里以 \(1000\) 的采样率进行均匀采样,把函数 \(f\) 离散化,再用 FFT 算法计算其 DFT。需要注意的是,由于采样率为 \(1000\),所以实际上是在对 \(f \left(\dfrac{k}{1000} \right)\) 做 DFT,算出来的结果需要除掉采样率做修正。

TOTAL_TIME = 2 * np.pi # 一个周期
SAMPLE_RATE = 1000     # 采样率

# 对函数进行傅里叶变换
def compute_fourier_transform(f):
    t = np.linspace(0, TOTAL_TIME, int(SAMPLE_RATE * TOTAL_TIME),
                    endpoint=False)
    samples = f(t)

    freq = np.fft.fftfreq(len(samples), 1 / SAMPLE_RATE)
    freq *= 2 * np.pi # 转换为角频率

    dft = np.fft.fft(samples) / SAMPLE_RATE
    nonzero = np.where(np.abs(dft) > 1e-10)
    return freq[nonzero], dft[nonzero]

下面写了一个 plot_signals 函数,用于同时绘制多个信号的频谱。

class SignalInfo:
    def __init__(self, title, f):
        self.title = title
        self.f = f # 关于时间的函数

# 绘制频谱图
def plot_signals(signals: list[SignalInfo]):
    plt.figure(figsize=(4 * len(signals), 4 * 2))

    for i, sig in enumerate(signals):
        freq, values = compute_fourier_transform(sig.f)
        magnitude = np.abs(values)
        phase = np.angle(values, deg=True)

        # 绘制幅度
        plt.subplot(2, len(signals), i + 1)
        plt.stem(freq, magnitude)
        plt.title(sig.title)
        plt.ylabel(r'Magnitude')
        plt.grid(True)

        for x, y in zip(freq, magnitude):
            plt.annotate(f'{y:.2f}', xy=(x, y),
                        ha='center', va='bottom', color='red')

        # 绘制相位
        plt.subplot(2, len(signals), i + 1 + len(signals))
        plt.stem(freq, phase)
        plt.xlabel(r'Angular Frequency (rad/s)')
        plt.ylabel(r'Phase (deg)')
        plt.grid(True)

        for x, y in zip(freq, phase):
            plt.annotate(f'{y:.2f}', xy=(x, y),
                        ha='center', va='bottom', color='red')

    plt.tight_layout()
    plt.show()

第一问

\[ \cos(\omega_0 t + \phi) \longleftrightarrow \pi \left[ \delta(\omega+\omega_0) e^{-j\phi} + \delta(\omega-\omega_0) e^{j\phi} \right] \]
plot_signals([
    SignalInfo(
        title=rf'$\omega_0$ = {omega} rad/s, '
            rf'$\phi$ = {np.round(np.degrees(phi))}°',
        f=lambda t, omega=omega, phi=phi: np.cos(omega * t + phi)
    )
    for omega, phi in zip(omega_values, phi_values)
])

频谱 1
频谱 1

第二问

\[ e^{j(\omega_0 t + \phi)} \longleftrightarrow 2\pi \delta(\omega-\omega_0) e^{j\phi} \]
plot_signals([
    SignalInfo(
        title=rf'$\omega_0$ = {omega} rad/s, '
            rf'$\phi$ = {np.round(np.degrees(phi))}°',
        f=lambda t, omega=omega, phi=phi: np.exp(1j * (omega * t + phi))
    )
    for omega, phi in zip(omega_values, phi_values)
])

频谱 2
频谱 2

第三问

\[ \begin{align} f_1(t) &= e^{j(t + 15^\circ)} + e^{j(3t - 30^\circ)} \\ f_2(t) &= e^{j(t + 15^\circ)} + e^{j(6t + 60^\circ)} \\ f_3(t) &= e^{j(3t - 30^\circ)} + e^{j(6t + 60^\circ)} \end{align} \]
from itertools import combinations

plot_signals([
    SignalInfo(
        title=f'$\\omega_1$ = {omega1} rad/s, '
            f'$\\phi_1$ = {np.round(np.degrees(phi1))}°\n'
            f'$\\omega_2$ = {omega2} rad/s, '
            f'$\\phi_2$ = {np.round(np.degrees(phi2))}°',
        f=lambda t, omega1=omega1, phi1=phi1, omega2=omega2, phi2=phi2:
            np.real(np.exp(1j * (omega1 * t + phi1)) + \
                np.exp(1j * (omega2 * t + phi2)))
    )
    for ((omega1, phi1), (omega2, phi2)) in combinations(
        zip(omega_values, phi_values), 2)
])

频谱 3
频谱 3

结论

实信号的幅值频谱是偶函数,相位频谱是奇函数。复信号的频谱没有这样的特点。

评论