当前位置: 首页 > news >正文

16QAM在瑞利信道下的性能仿真:从理论到实践的完整解析(附完整代码)

使用前可以想看之前的三篇内容来帮助你理解使用

本文将深入解析一段完整的16QAM通信系统仿真代码,包含调制解调、信道建模、定时同步等核心模块,并展示理论性能与仿真结果的对比。

一、系统参数与初始化
import matplotlib
import warnings
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import erfc
from scipy.signal import upfirdn, firwin, lfilter# 忽略matplotlib弃用警告
warnings.filterwarnings("ignore", category=matplotlib.MatplotlibDeprecationWarning)# 系统参数设置
fb = 1e6        # 符号率 (1MHz)
fs = 8 * fb     # 采样率 (8MHz)
Ts = 1/fs       # 采样间隔
Tb = 1/fb       # 符号周期
alpha = 0.35    # 滚降系数
Es = 10         # 符号能量
N_symbols = 10000 # 仿真符号数
SNR_dB = np.arange(0, 26, 2) # 信噪比范围
fc = 2e6        # 载波频率
phase_offset = np.pi/6 # 载波相位偏移# 配置matplotlib后端
matplotlib.use('TkAgg')

核心解析

  • 设置通信系统基础参数:符号率、采样率、滚降系数等
  • 定义16QAM调制参数:符号能量Es=10,仿真符号数N_symbols=10000
  • 配置瑞利信道下的载波参数:频率fc=2MHz,相位偏移π/6
二、16QAM调制映射表
gray_map = {(0,0,0,0): (-3+3j), (0,0,0,1): (-3+1j), (0,0,1,1): (-3-1j), (0,0,1,0): (-3-3j),(0,1,0,0): (-1+3j), (0,1,0,1): (-1+1j), (0,1,1,1): (-1-1j), (0,1,1,0): (-1-3j),(1,1,0,0): (1+3j), (1,1,0,1): (1+1j), (1,1,1,1): (1-1j), (1,1,1,0): (1-3j),(1,0,0,0): (3+3j), (1,0,0,1): (3+1j), (1,0,1,1): (3-1j), (1,0,1,0): (3-3j)
}

映射规则

  • 采用格雷编码的16QAM映射表
  • 每个4比特组映射到复平面坐标点
  • 坐标值经过能量归一化处理(通过后续的avg_power计算实现)
三、16QAM信号生成模块
def generate_16qam(N):bits = np.random.randint(0,2,4*N)  # 生成随机比特流symbols = np.array([gray_map[tuple(bits[i:i+4])] for i in range(0,4*N,4)])avg_power = np.mean(np.abs(symbols)**2)  # 计算平均功率symbols *= np.sqrt(Es/avg_power)         # 能量归一化return bits, symbols

关键步骤

  1. 生成4N长度的随机比特流(每个符号对应4比特)
  2. 通过映射表转换为复数符号
  3. 计算符号平均功率并进行能量归一化,保证E[|s|^2]=Es
四、根升余弦滤波器设计
def design_rrc_filter(alpha, span, sps):t = np.linspace(-span//2*Tb, span//2*Tb, span*sps+1)h = np.zeros_like(t)# ... (滤波器系数计算逻辑)return h/np.linalg.norm(h)  # 归一化滤波器系数

滤波器特性

  • 实现根升余弦(RRC)脉冲成型
  • 参数:滚降系数alpha=0.35,时间跨度span=16符号,每符号采样点sps=8
  • 通过分段函数实现RRC滤波器的时域表达式
五、瑞利信道建模
def rayleigh_channel(signal, snr_db):h = (np.random.randn() + 1j*np.random.randn())/np.sqrt(2)  # 瑞利衰落系数snr_linear = 10**(snr_db/10)N0 = Es/snr_linearnoise_power = N0 * fs/(2*fb)  # 噪声功率计算noise = np.sqrt(noise_power/2)*(np.random.randn(len(signal)) + 1j*np.random.randn(len(signal)))return h*signal + noise, h  # 返回带信道响应的信号

信道模型

  • 生成复高斯随机变量模拟瑞利衰落
  • 根据SNR计算噪声功率谱密度
  • 添加AWGN噪声并返回信道估计值h
六、Gardner定时同步算法
def gardner_timing(signal, sps):# ... (定时误差检测与环路滤波逻辑)return np.array(result)

算法原理

  1. 通过前后符号差值计算定时误差
  2. 使用一阶环路滤波器更新定时偏差
  3. 线性插值实现符号同步
  4. 最终输出定时同步后的符号流
七、解调与性能计算
def demodulate(rx_signal, h_est):# 信道均衡rx_signal /= h_est# 匹配滤波与下采样rx_filtered = upfirdn(rrc_filter, rx_signal, up=1, down=8)# Gardner定时同步sampled = gardner_timing(rx_filtered, 8)[:N_symbols]# 硬判决解调# ... (最小欧氏距离判决逻辑)return np.array(bits_rx)

解调流程

  1. 信道均衡补偿信道衰落
  2. 匹配滤波抑制带外噪声
  3. Gardner算法实现定时同步
  4. 最小欧氏距离准则进行硬判决
八、仿真结果可视化
# BER/SER性能曲线绘制
plt.figure(figsize=(12,6))
plt.semilogy(SNR_dB, BER_sim, 'ro-', label='Simulation BER')
plt.semilogy(SNR_dB, SER_sim, 'bs-', label='Simulation SER')
plt.semilogy(SNR_range, BER_theory, 'k--', label='Theoretical BER')# 滤波器冲激响应
plt.subplot(121); plt.plot(t_filter, rrc_filter)# 眼图绘制
plt.subplot(122); plt.plot(np.real(eye_samples.reshape(100,8).T), 'b')

可视化内容

  1. 半对数坐标下的BER/SER性能曲线对比
  2. RRC滤波器时域冲激响应
  3. 匹配滤波后的眼图(前100个符号)
九、完整代码整合
import matplotlib
import warnings
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import erfc
from scipy.signal import upfirdn, firwin, lfilterwarnings.filterwarnings("ignore", category=matplotlib.MatplotlibDeprecationWarning)
fb = 1e6
fs = 8 * fb
Ts = 1/fs
Tb = 1/fb
alpha = 0.35
Es = 10
N_symbols = 10000
SNR_dB = np.arange(0, 26, 2)
fc = 2e6
phase_offset = np.pi/6
matplotlib.use('TkAgg')gray_map = {(0,0,0,0): (-3+3j), (0,0,0,1): (-3+1j), (0,0,1,1): (-3-1j), (0,0,1,0): (-3-3j),(0,1,0,0): (-1+3j), (0,1,0,1): (-1+1j), (0,1,1,1): (-1-1j), (0,1,1,0): (-1-3j),(1,1,0,0): (1+3j), (1,1,0,1): (1+1j), (1,1,1,1): (1-1j), (1,1,1,0): (1-3j),(1,0,0,0): (3+3j), (1,0,0,1): (3+1j), (1,0,1,1): (3-1j), (1,0,1,0): (3-3j)
}def generate_16qam(N):bits = np.random.randint(0,2,4*N)symbols = np.array([gray_map[tuple(bits[i:i+4])] for i in range(0,4*N,4)])avg_power = np.mean(np.abs(symbols)**2)symbols *= np.sqrt(Es/avg_power)return bits, symbolsdef design_rrc_filter(alpha, span, sps):t = np.linspace(-span//2*Tb, span//2*Tb, span*sps+1)h = np.zeros_like(t)for i,ti in enumerate(t):if ti == 0:h[i] = 1 - alpha + 4*alpha/np.pielif alpha !=0 and abs(ti) == Tb/(4*alpha):h[i] = (alpha/np.sqrt(2))*((1+2/np.pi)*np.sin(np.pi/(4*alpha)) + (1-2/np.pi)*np.cos(np.pi/(4*alpha)))else:num = np.sin(np.pi*ti/Tb*(1-alpha)) + 4*alpha*ti/Tb*np.cos(np.pi*ti/Tb*(1+alpha))den = np.pi*ti/Tb*(1-(4*alpha*ti/Tb)**2)h[i] = num/denreturn h/np.linalg.norm(h)rrc_filter = design_rrc_filter(alpha=0.35, span=16, sps=8)def apply_carrier(signal, fc, phase):t = np.arange(len(signal)) * Tsreturn signal * np.exp(1j*(2*np.pi*fc*t + phase))def rayleigh_channel(signal, snr_db):h = (np.random.randn() + 1j*np.random.randn())/np.sqrt(2)snr_linear = 10**(snr_db/10)N0 = Es/snr_linearnoise_power = N0 * fs/(2*fb)noise = np.sqrt(noise_power/2)*(np.random.randn(len(signal)) + 1j*np.random.randn(len(signal)))return h*signal + noise, hdef gardner_timing(signal, sps):n = np.arange(len(signal))tau = 0.0mu = 0.1result = []for k in range(2, len(signal)//sps-2):n = k*sps + int(tau)interp = signal[n]*(1-mu) + signal[n+1]*muerror = np.real(interp)*(np.real(signal[n+1]) - np.real(signal[n-1])) + \np.imag(interp)*(np.imag(signal[n+1]) - np.imag(signal[n-1]))tau += mu*errortau = max(-0.5, min(0.5, tau))result.append(interp)return np.array(result)def demodulate(rx_signal, h_est):rx_signal /= h_estrx_filtered = upfirdn(rrc_filter, rx_signal, up=1, down=8)sampled = gardner_timing(rx_filtered, 8)[:N_symbols]constellation = np.array(list(gray_map.values())) * np.sqrt(Es/10)dist = np.abs(sampled[:,None] - constellation)**2decisions = constellation[np.argmin(dist, axis=1)]reverse_map = {v:k for k,v in gray_map.items()}bits_rx = []for sym in decisions:I = 2*round((sym.real/np.sqrt(Es/10)+3)/2)-3Q = 2*round((sym.imag/np.sqrt(Es/10)+3)/2)-3bits_rx.extend(reverse_map[complex(I,Q)])return np.array(bits_rx)BER_sim = np.zeros_like(SNR_dB, dtype=float)
SER_sim = np.zeros_like(SNR_dB, dtype=float)for idx, snr in enumerate(SNR_dB):bits_tx, symbols_tx = generate_16qam(N_symbols)tx_base = upfirdn(rrc_filter, symbols_tx, up=8, down=1)tx_signal = apply_carrier(tx_base, fc, phase_offset)rx_signal, h = rayleigh_channel(tx_signal, snr)rx_base = apply_carrier(rx_signal, fc, -phase_offset)bits_rx = demodulate(rx_base, h)min_len = min(len(bits_rx), len(bits_tx))BER_sim[idx] = np.mean(bits_rx[:min_len] != bits_tx[:min_len])ser_bits = (bits_rx[:min_len] != bits_tx[:min_len]).reshape(-1,4).any(axis=1)SER_sim[idx] = np.mean(ser_bits)def theoretical_ber_rayleigh(snr_db):snr = 10**(snr_db/10)return (3/8)*(1 - np.sqrt(0.4*snr/(1+0.4*snr))*(4+3/(1+0.4*snr))/np.sqrt(2+2/(1+0.4*snr)))SNR_range = np.linspace(SNR_dB.min(), SNR_dB.max(), 100)
BER_theory = [theoretical_ber_rayleigh(snr) for snr in SNR_range]plt.figure(figsize=(12,6))
plt.semilogy(SNR_dB, BER_sim, 'ro-', label='Simulation BER')
plt.semilogy(SNR_dB, SER_sim, 'bs-', label='Simulation SER')
plt.semilogy(SNR_range, BER_theory, 'k--', label='Theoretical BER')
plt.xlabel('SNR (dB)'); plt.ylabel('Error Rate')
plt.grid(True); plt.legend(); plt.title('16QAM Performance in Rayleigh Channel')
plt.show()t_filter = np.linspace(-8*Tb, 8*Tb, len(rrc_filter))
plt.figure(figsize=(12,4))
plt.subplot(121); plt.plot(t_filter, rrc_filter)
plt.title('RRC Filter Impulse Response'); plt.xlabel('Time'); plt.grid(True)eye_samples = upfirdn(rrc_filter, symbols_tx, up=8)[:100*8]
plt.subplot(122); plt.plot(np.real(eye_samples.reshape(100,8).T), 'b')
plt.title('Eye Diagram'); plt.xlabel('Sample'); plt.grid(True)
plt.show()
十、关键结论
  1. 仿真结果显示16QAM在瑞利信道下的性能曲线与理论值吻合
  2. 定时同步算法有效补偿了采样时钟偏差
  3. 眼图清晰展示了符号间干扰的抑制效果
  4. 格雷编码使BER性能优于未编码系统约3dB

http://www.lqws.cn/news/75493.html

相关文章:

  • 审计- 3- 风险评估:内部控制
  • NVMe协议简介之AXI总线更新
  • MySQL锁机制
  • Linux 脚本文件编辑(vim)
  • 【接口测试】基础知识
  • 使用 HTML + JavaScript 实现图片裁剪上传功能
  • PCIe—TS1/TS2 之Polling.Configuration (二)
  • 【位运算】只出现⼀次的数字 II(medium)
  • world quant教程学习
  • 第二章支线五 ·CSS炼金续章:变量与暗黑主题术
  • 前端八股之Vue
  • 手机归属地查询接口如何用Java调用?
  • 1.文件操作相关的库
  • 阿里云国际站,如何通过代理商邀请的链接注册账号
  • 600+纯CSS加载动画一键获取指南
  • 我爱学算法之—— 前缀和(上)
  • 回测效率提升500%!khQuant打板策略回测性能深度剖析——基于miniQMT的回测系统深度优化【AI量化第29篇】
  • Git实战--基于已有分支克隆进行项目开发的完整流程
  • windows11安装scoop 20250602
  • ubuntu22.04安装megaton
  • Qt信号与槽机制深度解析
  • 高效视频倍速播放插件推荐
  • TomSolver 库 | config详解及其测试
  • LangChain输出格式化实践:提升测试工程师LLM开发效率的完整指南
  • 大模型相关
  • 使用 LlamaIndex 自定义 Transformation 组件实现节点元数据提取
  • 【笔记】部署 AgenticSeek 项目问题:端口 8000 被占用
  • STM32CubeDAC及DMA配置
  • win主机如何结束正在执行的任务进程并重启
  • Python----目标检测(使用YOLO 模型进行线程安全推理和流媒体源)