当前位置: 首页 > 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

相关文章:

  • PH热榜 | 2025-06-01
  • SpringBoot-Thymeleaf
  • Arch安装botw-save-state
  • Google 发布的全新导航库:Jetpack Navigation 3
  • MySQL中的事务
  • Figma 中构建 Master Control Panel (MCP) 的完整设计方案
  • 【python深度学习】Day43 复习日
  • Go开发简历优化指南
  • ESP-IDF 离线安装——同时存在多个版本以及进行版本切换的方法
  • 头指针 VS 头节点 VS 首元节点
  • Day43打卡(补41+42) @浙大疏锦行
  • 【dshow】VIDEOINFOHEADER2 头文件
  • Java内存模型与互斥锁
  • Nuxt3部署
  • 机器视觉图像形态学中的腐蚀、膨胀、开运算、闭运算
  • 人工智能工程技术专业 和 其他信息技术专业 有哪些关联性?
  • 借助 Python 实现 AIOps 高级日志分析:实践者行动指南
  • 【Redis】Zset 有序集合
  • CppCon 2014 学习:Exception-Safe Coding
  • 字符串加解密
  • 网站咋做推广/市场营销经典案例
  • wordpress改cms/网站seo标题优化技巧
  • 网站3d展示怎么做的/推广注册app赚钱平台
  • 合伙合同网站建设协议/深圳精准网络营销推广
  • 青岛在线制作网站/吉安seo网站快速排名
  • 博彩网站做代理/网络软文是什么意思