首页 / 资讯中心 / 文章详情

16QAM全链路MATLAB仿真:软解调、扩频、VV同步与LDPC编译码

16QAM全链路MATLAB仿真:软解调、扩频、VV同步与LDPC编译码 ★ FEATURED ARTICLE
简介本资源面向通信系统仿真学习者与工程实践者提供一套完整的16QAM调制软解调、扩频解扩、VV相位同步、FFT频偏估计与LDPC编译码联合链路用于误码率性能验证。仿真流程覆盖随机二进制序列生成、LDPC编码、扩频、16QAM调制、相位偏差与频偏注入、AWGN信道加噪以及接收端FFT频偏估计、VV相位估计、软解调、LDPC译码、解扩与误码统计可帮助读者理解各模块协同关系与算法实现细节。资源包共28个文件含17个m脚本、10个mat数据文件与1个txt说明压缩包约299KB脚本按功能拆分便于逐模块调试与二次修改。程序基于MATLAB 2024b编写附中文注释与操作视频已有126人学习。读者可获得可运行工程、参数配置参考与排错思路适合课程设计、毕业设计及通信算法入门进阶使用。1. 从一张误码率曲线说起16QAM 全链路仿真到底在解决什么问题很多人第一次做 16QAM 误码率仿真跑出来的曲线跟理论值差了三四个 dB第一反应是「信道模型写错了」其实十有八九是同步环节没做对。16QAM 调制软解调 扩频解扩 VV 相位同步 FFT 频偏估计 LDPC 编译码这套组合本质上是在回答一个问题当信号同时经历频偏、相偏、扩频和信道编码时接收端怎么一步步把原始比特恢复出来并且让误码率曲线尽量贴近理论下界。这套链路适合两类人一类是做通信系统课程设计或毕业设计的同学需要一套能跑通、能改参数、能出图的完整 MATLAB 仿真另一类是做基带算法验证的工程师想快速搭一个包含同步和编码的参考链路用来对比自己模块的性能。它不追求实时性追求的是每个环节可观测、可替换、可复现。下面按信号流向拆开讲每一步都给可执行的 MATLAB 代码和参数说明。2. 发射端链路搭建16QAM 映射、扩频与帧结构2.1 为什么先定帧结构再写调制仿真翻车最常见的原因不是算法错而是帧结构没定清楚。接收端做频偏估计和相位同步时需要知道哪些符号是导频、哪些是数据。我一般把一帧分成三段前导导频段用于 FFT 频偏估计中间导频段用于 VV 相位跟踪数据段承载经过 LDPC 编码和扩频后的信息。导频用 QPSK 映射数据用 16QAM 映射这样同步和判决互不干扰。帧结构参数建议这样设前导导频 64 个符号中间导频每 128 个数据符号插入 8 个导频符号数据段总长取 1024 的整数倍方便 FFT 处理。采样率归一化为 1载波频率用归一化频率表示避免引入真实频段带来的额外复杂度。% 帧结构参数定义 frame.preambleLen 64; % 前导导频长度 frame.pilotInterval 128; % 导频插入间隔 frame.pilotLen 8; % 每组导频长度 frame.dataLen 1024; % 数据段长度 frame.totalLen frame.preambleLen ... (frame.dataLen / frame.pilotInterval) * (frame.pilotLen frame.pilotInterval); % 总帧长 64 8*(8128) 1152 符号这段代码定义了整帧的时间结构。preambleLen决定频偏估计的精度越长估计越准但开销越大pilotInterval决定相位跟踪的更新速率间隔越小跟踪越快但频谱效率越低。实际调参时先把导频开销控制在 10% 以内再根据相位噪声带宽调整间隔。2.2 16QAM 格雷映射与软解调比特 LLR 计算16QAM 每个符号携带 4 比特映射方式直接决定误码率。格雷映射让相邻星座点只差一个比特降低误比特率。软解调的核心是计算每个比特的对数似然比而不是硬判决。LLR 的符号表示比特是 0 还是 1绝对值表示置信度这个置信度后面要送给 LDPC 译码器。% 16QAM 格雷映射星座表归一化平均功率为1 constellation [-3-3i, -3-1i, -33i, -31i, ... -1-3i, -1-1i, -13i, -11i, ... 3-3i, 3-1i, 33i, 31i, ... 1-3i, 1-1i, 13i, 11i] / sqrt(10); % 软解调计算每个比特的LLR function llr qam16_soft_demod(rxSym, noiseVar) % rxSym: 接收符号含噪 % noiseVar: 噪声方差 llr zeros(4, length(rxSym)); for k 1:length(rxSym) r rxSym(k); % 比特0和比特2由实部决定比特1和比特3由虚部决定 llr(1,k) 4*real(r)/sqrt(10)/noiseVar; % 实部最高位 llr(2,k) 4*imag(r)/sqrt(10)/noiseVar; % 虚部最高位 llr(3,k) (4/sqrt(10)/noiseVar)*(abs(real(r))/sqrt(10) - 2/sqrt(10)); llr(4,k) (4/sqrt(10)/noiseVar)*(abs(imag(r))/sqrt(10) - 2/sqrt(10)); end endnoiseVar必须和实际信道噪声功率一致否则 LLR 的尺度会偏LDPC 译码性能会明显下降。常见错误是用信噪比直接代替噪声方差正确做法是noiseVar 10^(-snr/10)信号功率归一化后。LLR 计算里的系数 4 来自 max-log 近似如果追求更精确可以用 log-sum-exp但复杂度会上升。2.3 扩频与解扩把窄带信号摊到宽带上扩频用伪随机序列对每个符号做乘法接收端用相同序列做相关解扩。扩频的好处是抗窄带干扰代价是占用带宽。仿真里用 Gold 序列或 m 序列都行关键是收发两端序列必须同步对齐。% 扩频因子和序列生成 sf 8; % 扩频因子 pnSeq randi([0 1], 1, sf) * 2 - 1; % BPSK调制的PN序列 % 扩频每个符号乘以PN序列 txSpread kron(txSymbols, pnSeq); % 解扩相关后取平均 rxDespread zeros(1, length(rxSpread)/sf); for k 1:length(rxDespread) seg rxSpread((k-1)*sf1 : k*sf); rxDespread(k) sum(seg .* pnSeq) / sf; end扩频因子sf越大抗干扰增益越高但可容纳的用户数越少。仿真里sf8对应 9dB 扩频增益足够观察解扩前后的星座图变化。解扩后的符号再送软解调注意解扩会改变噪声方差LLR 计算时要除以sf。3. 接收端同步VV 相位同步与 FFT 频偏估计3.1 FFT 频偏估计为什么时域相关不如频域峰值频偏估计常用两种方法时域自相关和 FFT 峰值检测。时域方法适合大频偏粗估FFT 方法精度更高但需要足够长的导频。前导导频 64 个符号做 FFT频率分辨率是采样率的 1/64配合插值可以把估计误差压到 1% 以内。% FFT频偏估计 function freqOffset fft_freq_estimate(rxPreamble, fs) % rxPreamble: 接收前导导频 % fs: 采样率 N length(rxPreamble); spectrum fftshift(abs(fft(rxPreamble .* conj(rxPreamble(1))))); % 自相关后做FFT峰值位置对应频偏 [~, peakIdx] max(spectrum); freqOffset (peakIdx - N/2 - 1) * fs / N; end这里用自相关再做 FFT 是为了消除调制相位的影响。peakIdx偏离中心的位置就是频偏对应的频点。如果频偏超过 FFT 分辨率的一半会出现峰值模糊这时候要先用时域方法粗估再精估。实际调试时先看频谱峰值是否尖锐如果峰值展宽说明导频太短或信噪比太低。3.2 VV 相位同步判决反馈环的参数怎么设VV 相位同步是一种判决反馈环用判决后的符号估计相位误差再通过环路滤波器更新相位。它的优点是能跟踪动态相位变化缺点是在低信噪比下判决错误会导致环失锁。环路带宽和阻尼系数是两个关键参数。% VV相位同步环 function [phaseOut, phaseEst] vv_phase_sync(rxSym, constellation, loopBW, damping) % loopBW: 归一化环路带宽 % damping: 阻尼系数 Kp 4 * damping * loopBW / (1 2*damping*loopBW loopBW^2); Ki 4 * loopBW^2 / (1 2*damping*loopBW loopBW^2); phaseEst 0; phaseOut zeros(size(rxSym)); integrator 0; for k 1:length(rxSym) % 相位补偿 phaseOut(k) rxSym(k) * exp(-1i*phaseEst); % 判决 [~, idx] min(abs(phaseOut(k) - constellation)); decision constellation(idx); % 相位误差检测 phaseError imag(phaseOut(k) * conj(decision)); % 环路滤波 integrator integrator Ki * phaseError; phaseEst integrator Kp * phaseError; end endloopBW一般取 0.01 到 0.05太大跟踪快但噪声敏感太小跟踪慢但稳态误差小。damping取 0.707 是经典值对应最佳二阶响应。调试时先固定damping从小到大调loopBW观察星座图是否收敛。如果星座点旋转说明频偏没补干净如果星座点发散说明环路带宽太大。3.3 频偏和相位联合处理的顺序问题频偏和相位不能同时估计必须先补频偏再补相位。频偏是线性相位变化相位是固定偏移顺序反了会导致相位环一直追不上。我一般先用前导导频做 FFT 频偏估计补偿后再用中间导频做 VV 相位跟踪。如果频偏估计有残差相位环的loopBW要适当加大来吸收残差。4. LDPC 编译码从校验矩阵到置信度传播4.1 校验矩阵构造与编码实现LDPC 码的性能取决于校验矩阵的度分布和围长。仿真里常用准循环 LDPC码率 1/2码长 1024 或 2048。编码用高斯消元把校验矩阵变成系统形式再算校验比特。% 准循环LDPC编码简化版 function codeword ldpc_encode(infoBits, H) % H: 校验矩阵m x n % infoBits: 信息比特1 x k [m, n] size(H); k n - m; % 高斯消元得到系统形式 H_sys gf(H, 1); [H_sys, ~] gflineq(H_sys); % 伪代码实际用gfrank和gfdiv P H_sys(:, k1:n); % 校验比特 P * infoBits parityBits mod(P * infoBits, 2); codeword [infoBits, parityBits]; end实际工程里不会每次编码都做高斯消元而是预先算好生成矩阵。H的构造要避免短环围长至少为 6。如果误码率曲线出现错误平底多半是校验矩阵有短环导致译码器陷入局部最优。4.2 置信度传播译码与迭代次数选择LDPC 译码用置信度传播在变量节点和校验节点之间传递 LLR。迭代次数一般取 20 到 50次数越多性能越好但延迟越大。仿真里可以设最大迭代次数 50同时加早停条件如果校验方程全部满足就提前退出。% LDPC置信度传播译码简化 function decodedBits ldpc_decode(llr, H, maxIter) [m, n] size(H); L llr; % 初始化变量节点消息 for iter 1:maxIter % 校验节点更新 for j 1:m idx find(H(j,:)); for i idx others setdiff(idx, i); signProd prod(sign(L(others))); minVal min(abs(L(others))); L_check(i) signProd * minVal; end end % 变量节点更新 L llr L_check; % 硬判决 decodedBits (L 0); % 早停检查 if all(mod(H * decodedBits, 2) 0) break; end end endmaxIter设 50 是性能和复杂度的折中。如果迭代 10 次就收敛说明信噪比高如果 50 次还没收敛说明信噪比太低或者 LLR 尺度不对。调试时打印每次迭代的校验方程满足数能直观看到收敛过程。5. 避坑与排查仿真跑不通时先看这五条5.1 误码率曲线在高信噪比处出现平底现象信噪比超过 10dB 后误码率不再下降曲线变平。原因通常是 LDPC 校验矩阵有短环或者扩频序列没对齐。解决检查校验矩阵围长用gfrank确认矩阵满秩检查收发两端 PN 序列是否完全一致扩频和解扩的起始位置要对齐。5.2 星座图旋转但频偏估计显示为零现象VV 相位环补偿后星座图仍然缓慢旋转。原因频偏估计的 FFT 分辨率不够残差频偏被相位环当成相位误差跟踪。解决加长前导导频到 128 符号或者在 FFT 峰值附近做抛物线插值提高估计精度。5.3 LDPC 译码完全不收敛现象迭代 50 次后校验方程满足数始终为零。原因LLR 的符号定义和译码器内部约定相反。解决确认软解调输出的 LLR 是「比特为 0 的概率除以比特为 1 的概率取对数」如果反了译码器会把所有比特判反。5.4 扩频后误码率反而变差现象加扩频后误码率比不加还高。原因解扩时噪声方差没有重新计算LLR 尺度偏大导致 LDPC 译码器过置信。解决解扩后噪声方差除以扩频因子LLR 计算时用新的噪声方差。5.5 仿真速度慢到无法调参现象跑一次误码率曲线要几个小时。原因LDPC 译码用了三重循环MATLAB 解释执行效率低。解决把校验节点更新向量化用稀疏矩阵存储H或者把译码核心写成 MEX 函数。信噪比扫描时先用少量帧粗扫找到拐点再精细扫描。6. 进阶技巧用外部信息比验证同步与译码的边界仿真跑通之后真正有价值的是知道每个模块的性能边界。我习惯用外部信息比来量化同步误差对译码的影响。具体做法是在 LDPC 译码器输入端和输出端分别计算 LLR 与真实比特的互信息两者之差就是译码增益。如果同步没做好输入互信息会明显下降译码增益再大也补不回来。% 计算外部信息比 function exInfo extrinsic_info(llrIn, llrOut, trueBits) % llrIn: 译码器输入LLR % llrOut: 译码器输出LLR % trueBits: 真实比特 % 计算输入互信息 I_in mutual_info(llrIn, trueBits); I_out mutual_info(llrOut, trueBits); exInfo I_out - I_in; end function I mutual_info(llr, bits) % 简化的互信息计算 p0 1 ./ (1 exp(-llr)); p1 1 - p0; I mean(bits .* log2(p0) (1-bits) .* log2(p1)) 1; end这个指标的好处是把同步和译码解耦。如果I_in已经很低说明问题在同步或解调如果I_in正常但exInfo很小说明 LDPC 译码器没起作用。调参时先保证I_in接近理论值再优化译码迭代次数。另一个实用技巧是扫频偏和相偏的二维网格画出误码率热力图。频偏从 -0.01 到 0.01 步进 0.001相偏从 -pi 到 pi 步进 pi/8每个点跑 100 帧。热力图能直观看到同步算法的捕获范围和失锁边界。我一般把捕获范围控制在理论值的 80% 以内留 20% 余量给噪声和实现误差。最后说一个血泪教训不要等到整条链路搭完才做误码率测试。每加一个模块就单独测一次确认该模块的输入输出符合预期。扩频单独测解扩星座图LDPC 单独测译码收敛曲线同步单独测相位跟踪误差。模块级验证花的时间比整链路调试省下来的时间多得多。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站