返回博主主页

实时动画版本,动态展示 QPSK 与 16QAM 在调制过程中的“星座点跃迁”过程

点击查看代码
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.gridspec as gridspec
from matplotlib.patches import Rectangle, Circle
import random
matplotlib.use('TkAgg')
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用于显示中文
plt.rcParams['axes.unicode_minus'] = False  # 用于显示负号

# 创建图形和布局
fig = plt.figure(figsize=(14, 10), facecolor='#0f0f1a')
gs = gridspec.GridSpec(3, 2, height_ratios=[1, 1, 1])

# 创建子图
ax_qpsk = fig.add_subplot(gs[0, 0])
ax_16qam = fig.add_subplot(gs[0, 1])
ax_bits = fig.add_subplot(gs[1, :])
ax_info = fig.add_subplot(gs[2, :])

# 设置整体背景色
for ax in [ax_qpsk, ax_16qam, ax_bits, ax_info]:
    ax.set_facecolor('#0f0f1a')
    ax.tick_params(colors='white')
    for spine in ax.spines.values():
        spine.set_color('white')

# 设置QPSK星座图
ax_qpsk.set_title('QPSK 调制星座图', color='cyan', fontsize=14, pad=15)
ax_qpsk.set_xlim(-1.5, 1.5)
ax_qpsk.set_ylim(-1.5, 1.5)
ax_qpsk.grid(True, alpha=0.3, color='gray')
ax_qpsk.axhline(y=0, color='white', alpha=0.5)
ax_qpsk.axvline(x=0, color='white', alpha=0.5)
ax_qpsk.set_xlabel('同相分量 (I)', color='white')
ax_qpsk.set_ylabel('正交分量 (Q)', color='white')
ax_qpsk.set_aspect('equal', adjustable='datalim')

# 绘制QPSK星座点
qpsk_points = [(-1, -1), (-1, 1), (1, -1), (1, 1)]
for i, point in enumerate(qpsk_points):
    color = ['#FF6666', '#66FF66', '#6666FF', '#FF66FF'][i]
    ax_qpsk.scatter(*point, s=150, color=color, edgecolor='white', zorder=10)
    ax_qpsk.text(point[0] + 0.1, point[1] - 0.1, f"{i:02b}", color='white', fontsize=10)

# 设置16QAM星座图
ax_16qam.set_title('16QAM 调制星座图', color='yellow', fontsize=14, pad=15)
ax_16qam.set_xlim(-4, 4)
ax_16qam.set_ylim(-4, 4)
ax_16qam.grid(True, alpha=0.3, color='gray')
ax_16qam.axhline(y=0, color='white', alpha=0.5)
ax_16qam.axvline(x=0, color='white', alpha=0.5)
ax_16qam.set_xlabel('同相分量 (I)', color='white')
ax_16qam.set_ylabel('正交分量 (Q)', color='white')
ax_16qam.set_aspect('equal', adjustable='datalim')

# 绘制16QAM星座点
qam_points = [(-3, -3), (-3, -1), (-3, 1), (-3, 3),
              (-1, -3), (-1, -1), (-1, 1), (-1, 3),
              (1, -3), (1, -1), (1, 1), (1, 3),
              (3, -3), (3, -1), (3, 1), (3, 3)]
colors = ['#FF6666', '#FF9966', '#FFCC66', '#FFFF66',
          '#99FF66', '#66FF66', '#66FF99', '#66FFCC',
          '#66CCFF', '#6699FF', '#6666FF', '#9966FF',
          '#CC66FF', '#FF66FF', '#FF66CC', '#FF6699']

for i, point in enumerate(qam_points):
    ax_16qam.scatter(*point, s=100, color=colors[i], edgecolor='white', zorder=10)
    ax_16qam.text(point[0] - 0.25, point[1] - 0.35, f"{i:04b}", color='white', fontsize=8)

# 设置比特流显示
ax_bits.set_title('二进制比特流', color='#FF8C00', fontsize=14, pad=15)
ax_bits.set_xlim(0, 20)
ax_bits.set_ylim(0, 6)
ax_bits.set_xticks([])
ax_bits.set_yticks([])
ax_bits.text(0.5, 5.5, "当前传输比特:", color='cyan', fontsize=12, ha='left')

# 设置信息显示区域
ax_info.set_axis_off()
info_text = ax_info.text(0.02, 0.8, "", color='white', fontsize=12, ha='left', va='top',
                         transform=ax_info.transAxes)

# 调制信息
mod_info = [
    "调制技术说明:",
    "• QPSK (Quadrature Phase Shift Keying): 四相相移键控",
    "   - 每个符号传输 2 比特",
    "   - 4 个星座点分布在单位圆的四相上",
    "   - 抗噪声性能中等,频谱效率较高",
    "",
    "• 16QAM (16-Quadrature Amplitude Modulation): 正交振幅调制",
    "   - 每个符号传输 4 比特",
    "   - 16 个星座点分布在网格上",
    "   - 高频谱效率,但抗噪声性能较弱"
]

# 添加调制原理说明
for i, text in enumerate(mod_info):
    color = 'white' if len(text) < 2 or text[1] == ' ' else '#FF9999' if 'QPSK' in text else '#99FF99'
    ax_info.text(0.02, 0.65 - i * 0.075, text, color=color, fontsize=11, ha='left')

# 添加动画控制按钮
button_ax = fig.add_axes([0.75, 0.05, 0.15, 0.05])
button = Rectangle((0, 0), 1, 1, facecolor='#3366CC', edgecolor='white')
button_ax.add_patch(button)
button_ax.text(0.5, 0.5, "暂停/继续", color='white', ha='center', va='center', fontsize=11)
button_ax.set_axis_off()

# 初始化变量
current_symbol = 0
symbol_history = []
qpsk_lines = []
qam_lines = []
bit_texts = []
paused = False

# 初始比特流显示
bit_positions = []
for i in range(16):
    x = 0.5 + i
    y = 3.5
    rect = Rectangle((x, y), 0.8, 0.8, facecolor='#222233', edgecolor='#555555')
    ax_bits.add_patch(rect)
    bit_text = ax_bits.text(x + 0.4, y + 0.4, "", color='white', ha='center', va='center', fontsize=11)
    bit_texts.append(bit_text)
    bit_positions.append((x + 0.4, y + 0.4))


# 动画更新函数
def update(frame):
    global current_symbol, symbol_history, paused

    if paused:
        return

    # 生成随机比特
    bit_value = random.randint(0, 15)
    symbol_history.append(bit_value)

    # 更新比特流显示
    for i in range(16):
        if i < len(symbol_history):
            bit_str = f"{symbol_history[i]:04b}"
            bit_texts[i].set_text(bit_str)
            # 高亮当前比特
            if i == current_symbol:
                bit_texts[i].set_color('cyan')
                bit_texts[i].set_fontweight('bold')
            else:
                bit_texts[i].set_color('white')
                bit_texts[i].set_fontweight('normal')
        else:
            bit_texts[i].set_text("")

    # 解码比特
    bits = f"{bit_value:04b}"

    # QPSK映射 (使用第一个2比特)
    qpsk_bit = bits[:2]
    qpsk_index = int(qpsk_bit, 2)
    qx, qy = qpsk_points[qpsk_index]

    # 16QAM映射 (使用全部4比特)
    qam_index = bit_value
    qamx, qamy = qam_points[qam_index]

    # 清除旧线条
    for line in qpsk_lines:
        line.remove()
    qpsk_lines.clear()

    for line in qam_lines:
        line.remove()
    qam_lines.clear()

    # 绘制新线条
    line_qpsk, = ax_qpsk.plot([0, qx], [0, qy], 'w-', linewidth=1.5, alpha=0.8)
    line_qam, = ax_16qam.plot([0, qamx], [0, qamy], 'w-', linewidth=1.5, alpha=0.8)

    qpsk_lines.append(line_qpsk)
    qam_lines.append(line_qpsk)

    # 绘制点
    point_qpsk = ax_qpsk.scatter(qx, qy, s=100, color=colors[qam_index], edgecolor='white', zorder=10)
    point_qam = ax_16qam.scatter(qamx, qamy, s=100, color=colors[qam_index], edgecolor='white', zorder=10)

    qpsk_lines.append(point_qpsk)
    qam_lines.append(point_qam)

    # 更新信息
    info = f"当前符号: {current_symbol}\n"
    info += f"二进制比特: {bits}\n"
    info += f"QPSK映射: {qpsk_bit} → 星座点 ({qx}, {qy})\n"
    info += f"16QAM映射: {bits} → 星座点 ({qamx}, {qamy})\n"
    info += f"传输比特总数: {4 * len(symbol_history)}"

    info_text.set_text(info)

    current_symbol = (current_symbol + 1) % 16


# 点击事件处理
def on_click(event):
    global paused
    if event.inaxes == button_ax:
        paused = not paused


fig.canvas.mpl_connect('button_press_event', on_click)

# 创建动画
ani = FuncAnimation(fig, update, interval=1000, cache_frame_data=False)

# 添加标题
fig.suptitle('QPSK vs 16QAM 星座点跃迁过程', color='white', fontsize=18, y=0.97)

plt.tight_layout(rect=[0, 0.05, 1, 0.95])
plt.subplots_adjust(hspace=0.25)
plt.show()

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

Welcome to here

主页