返回博主主页

QPSk与16QAM调制的信号涌现的差异

通过一个动态信号波形模拟 + 星座图演变的方式,直观展示 QPSK16QAM 在调制过程中信号的“涌现”差异。


🎯 目标:

用 Python 模拟以下过程:

  1. 生成一段随机比特流;
  2. 分别用 QPSK 和 16QAM 调制;
  3. 展示:
    • 比特 → 符号映射过程(星座点跳跃);
    • 实际调制后的时域信号波形(载波调制);
    • 星座图对比(静态+动态演化);
  4. 直观看出两者的信号复杂度差异

✅ 完整代码如下(可运行):

import numpy as np
import matplotlib.pyplot as plt

# 设置随机种子便于复现
np.random.seed(42)

def generate_bits(n):
    return np.random.randint(0, 2, n)

def qpsk_modulate(bits):
    # 每 2 bit 映射为一个 QPSK 符号
    assert len(bits) % 2 == 0, "QPSK 要求比特数为偶数"
    symbols = []
    for i in range(0, len(bits), 2):
        b1, b2 = bits[i], bits[i+1]
        # 映射规则:00->(1,1), 01->(-1,1), 10->(-1,-1), 11->(1,-1)
        if b1 == 0 and b2 == 0:
            sym = complex(1, 1)
        elif b1 == 0 and b2 == 1:
            sym = complex(-1, 1)
        elif b1 == 1 and b2 == 0:
            sym = complex(-1, -1)
        else:
            sym = complex(1, -1)
        symbols.append(sym)
    return np.array(symbols)

def qam16_modulate(bits):
    # 每 4 bit 映射为一个 16QAM 符号
    assert len(bits) % 4 == 0, "16QAM 要求比特数为4的倍数"
    symbols = []
    levels = [-3, -1, 1, 3]  # 16QAM 的实/虚部取值
    # 归一化因子(使平均能量为1)
    scale = np.sqrt(10)
    for i in range(0, len(bits), 4):
        b1, b2, b3, b4 = bits[i:i+4]
        # 二进制转索引:b1 b2 -> I, b3 b4 -> Q
        idx_i = b1 * 2 + b2
        idx_q = b3 * 2 + b4
        i_val = levels[idx_i]
        q_val = levels[idx_q]
        sym = complex(i_val, q_val) / scale
        symbols.append(sym)
    return np.array(symbols)

def add_noise(signal, snr_db):
    power = np.mean(np.abs(signal)**2)
    noise_power = power / (10**(snr_db/10))
    noise = np.sqrt(noise_power/2) * (np.random.randn(len(signal)) + 1j*np.random.randn(len(signal)))
    return signal + noise

def plot_signals_and_constellations(bits_qpsk, bits_16qam, snr_db=20):
    # 1. 调制
    qpsk_symbols = qpsk_modulate(bits_qpsk)
    qam16_symbols = qam16_modulate(bits_16qam)

    # 2. 添加噪声(模拟信道)
    noisy_qpsk = add_noise(qpsk_symbols, snr_db)
    noisy_16qam = add_noise(qam16_symbols, snr_db)

    # 3. 画图:星座图 + 信号波形(简化版)

    fig, axes = plt.subplots(2, 2, figsize=(14, 10))

    # --- 子图 1:QPSK 星座图 ---
    axes[0,0].scatter(qpsk_symbols.real, qpsk_symbols.imag, c='blue', s=60, label='Original', marker='o')
    axes[0,0].scatter(noisy_qpsk.real, noisy_qpsk.imag, c='red', s=30, label='Noisy', alpha=0.7)
    axes[0,0].set_title(f'QPSK Constellation (SNR={snr_db}dB)', fontsize=12)
    axes[0,0].set_xlabel('I')
    axes[0,0].set_ylabel('Q')
    axes[0,0].grid(True, alpha=0.3)
    axes[0,0].legend()
    axes[0,0].axis('equal')

    # --- 子图 2:16QAM 星座图 ---
    axes[0,1].scatter(qam16_symbols.real, qam16_symbols.imag, c='blue', s=60, label='Original', marker='o')
    axes[0,1].scatter(noisy_16qam.real, noisy_16qam.imag, c='red', s=30, label='Noisy', alpha=0.7)
    axes[0,1].set_title(f'16QAM Constellation (SNR={snr_db}dB)', fontsize=12)
    axes[0,1].set_xlabel('I')
    axes[0,1].set_ylabel('Q')
    axes[0,1].grid(True, alpha=0.3)
    axes[0,1].legend()
    axes[0,1].axis('equal')

    # --- 子图 3:QPSK 信号波形(简化)---
    t = np.linspace(0, 0.01, 1000)  # 时间轴
    carrier_freq = 1000  # Hz
    carrier = np.exp(1j * 2 * np.pi * carrier_freq * t)
    # 取前几个符号做波形演示
    symbol_duration = 0.001
    symbols_to_plot = qpsk_symbols[:4]
    signal_wave = np.zeros_like(t, dtype=complex)
    for i, sym in enumerate(symbols_to_plot):
        start_idx = int(i * symbol_duration * len(t))
        end_idx = int((i+1) * symbol_duration * len(t))
        signal_wave[start_idx:end_idx] = sym * carrier[start_idx:end_idx]

    axes[1,0].plot(t, signal_wave.real, label="I component", lw=1.5)
    axes[1,0].plot(t, signal_wave.imag, label="Q component", lw=1.5, linestyle="--")
    axes[1,0].set_title("QPSK Time-Domain Signal (I & Q)", fontsize=12)
    axes[1,0].set_xlabel("Time (s)")
    axes[1,0].set_ylabel("Amplitude")
    axes[1,0].legend()
    axes[1,0].grid(True, alpha=0.3)

    # --- 子图 4:16QAM 信号波形 ---
    symbol_duration = 0.001
    symbols_to_plot = qam16_symbols[:4]
    signal_wave_16 = np.zeros_like(t, dtype=complex)
    for i, sym in enumerate(symbols_to_plot):
        start_idx = int(i * symbol_duration * len(t))
        end_idx = int((i+1) * symbol_duration * len(t))
        signal_wave_16[start_idx:end_idx] = sym * carrier[start_idx:end_idx]

    axes[1,1].plot(t, signal_wave_16.real, label="I component", lw=1.5)
    axes[1,1].plot(t, signal_wave_16.imag, label="Q component", lw=1.5, linestyle="--")
    axes[1,1].set_title("16QAM Time-Domain Signal (I & Q)", fontsize=12)
    axes[1,1].set_xlabel("Time (s)")
    axes[1,1].set_ylabel("Amplitude")
    axes[1,1].legend()
    axes[1,1].grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

# === 主程序:生成数据并绘图 ===
if __name__ == "__main__":
    # 生成足够多的比特
    total_bits = 1000
    bits_qpsk = generate_bits(total_bits)
    bits_16qam = generate_bits(total_bits)

    # 只取前 1000 位用于 QPSK(每 2 位一组)
    num_qpsk_symbols = len(bits_qpsk) // 2
    bits_qpsk = bits_qpsk[:num_qpsk_symbols * 2]

    # 只取前 1000 位用于 16QAM(每 4 位一组)
    num_16qam_symbols = len(bits_16qam) // 4
    bits_16qam = bits_16qam[:num_16qam_symbols * 4]

    # 绘制信号与星座图
    plot_signals_and_constellations(bits_qpsk, bits_16qam, snr_db=20)

📈 运行后你会看到什么?

图像 内容说明
✅ 左上:QPSK 星座图 4 个清晰点,分布均匀,点间距离大
✅ 右上:16QAM 星座图 16 个点呈 4×4 网格,中心密集,边缘稀疏
✅ 左下:QPSK 时域波形 波形跳变少,信号平滑,相位变化较慢
✅ 右下:16QAM 时域波形 波形跳变频繁,幅度波动更大,更“剧烈”

🔍 关键差异总结:

方面 QPSK 16QAM
星座点数 4 16
每符号比特数 2 4
信号复杂度 低(平稳) 高(剧烈跳变)
抗干扰能力 强(点远) 弱(点密)
数据速率 较低 较高
适用场景 无线、弱信号 光纤、高信噪比

💡 一句话理解“信号涌现差异”:

QPSK 的信号像“稳重的散步”,一步一停;
16QAM 的信号像“快速跑动的舞步”,步伐多变、节奏快,但容易踩空。


需要我再加一个“实时动画”版本(用 matplotlib.animation 动态展示星座点跳跃)吗?或者想看看误码率对比曲线?😊

posted @ 2025-08-14 16:30  懒惰的星期六  阅读(24)  评论(0)    收藏  举报

Welcome to here

主页