
简介这是一份面向通信工程专业学生与研究人员的MATLAB仿真资源围绕16QAM调制下HARQ与LDPC编码的完整实现展开适合用于无线通信课程设计、毕业设计或相关科研入门。压缩包内共包含19个文件其中16个.mat文件保存不同码率与码长的LDPC校验矩阵如H_matrix_*系列覆盖从短码到长码、不同码率的对照场景3个.m脚本则分别负责校验矩阵生成genH.m、全调制方式下的LDPC编码发送transmit_ldpc_all_mod.m以及ARQ主循环ARQmain_ldpc_all_mod.m整体压缩后仅18KB内容轻量易读。目前已有211人学习下载代码结构清晰便于按模块拆解与研究。通过这套程序读者可快速掌握HARQ重传合并流程、LDPC编译码在16QAM链路中的调用方式并借助多种校验矩阵对比不同码长、码率参数下的纠错性能后续还可在此基础上修改调制阶数或替换校验矩阵进行扩展实验。1. 在MATLAB里把HARQ、LDPC、16QAM串成一条链路先解决“能跑”再谈“跑得快”做无线通信物理层仿真的人大多经历过这个阶段单独写一个16QAM解调、单独调一个LDPC译码器都能跑通但一旦把HARQ重传机制加进去代码就从“脚本”变成“状态机”。HARQ、LDPC、16QAM这三个词的组合在MATLAB生态里很常见网上能搜到不少类似“HARQ_LDPC_16QAM_6”的压缩包但真正打开后你会发现大部分资源要么只给出发送端代码要么只把HARQ做成无脑循环重发完全没考虑合并增益。这篇文章的目标是给出一个可以直接在MATLAB里复现的LDPC编码、16QAM软解调、HARQ重传合并的仿真链路并且说清楚每个参数的物理含义以及最容易被忽略的噪声方差匹配问题。适合做无线链路预算、算法验证、毕业设计以及想从“跑通”走向“跑准”的工程师。2. LDPC编码和16QAM调制在MATLAB里的基础实现2.1 用comm.LDPCEncoder和dvbs2ldpc搭最小编码链路MATLAB通信系统工具箱提供了现成的LDPC编解码对象。常见做法是先用dvbs2ldpc生成一个符合DVB-S2标准的稀疏校验矩阵再把它传给comm.LDPCEncoder和comm.LDPCDecoder。之所以选DVB-S2矩阵而不是随手构造的稀疏矩阵是因为它的码长、行重列重都是经过工程优化的译码收敛快而且不同MATLAB版本之间行为一致不需要自己手工检查H矩阵是否满秩。% 配置 LDPC 编码器码长 64800码率 1/2 H dvbs2ldpc(1/2); ldpcEncoder comm.LDPCEncoder(H); ldpcDecoder comm.LDPCDecoder(H, MaximumIterationCount, 50); % 信息位长度由 H 的列数减行数决定 K size(H, 2) - size(H, 1); % 32400 infoBits randi([0 1], K, 1); % 编码得到 64800 个比特 codedBits ldpcEncoder(infoBits);这里的H是DVB-S2标准里的校验矩阵size(H,1)是校验位个数K用总列数减去行数得到。MaximumIterationCount控制译码迭代次数5G NR场景下一般取8到20DVB-S2码长较长设50能获得更好性能但仿真时间会明显上升。如果你用的是老版本MATLAB例如R2018b之前dvbs2ldpc只支持少数固定码率这种情况下可以改用ldpcQuasiCyclicMatrix手动构造准循环LDPC码。注意infoBits必须是列向量randi生成的正好满足要求。在循环仿真里这行代码建议放到for循环外避免重复生成H矩阵。我在实际项目中会把编解码器对象封装成一个简单的struct这样切换码率时只改H就好。2.2 16QAM调制应该用bit输入还是symbol输入16QAM调制在MATLAB里有两种等价写法一种是把比特分组后查表映射成十进制符号再用qammod的symbol输入另一种是直接把比特列向量交给qammod并指定InputType,bit。后者会自动按Gray顺序映射省掉手动处理坐标映射的中间代码。% 单位平均功率下的 16QAM 调制 modSym qammod(codedBits, 16, InputType, bit, UnitAveragePower, true); % 经过 AWGN 信道snr 为 dB 值 rxSym awgn(modSym, snr, measured); % 计算每个比特的 LLR 软量 llr qamdemod(rxSym, 16, OutputType, llr, ... UnitAveragePower, true, NoiseVariance, 10^(-snr/10));解码端使用llr输出比硬判决好很多因为LDPC译码器内部是概率域迭代译码输入软比特能保留信道置信度信息。这里NoiseVariance必须是线性噪声方差如果给的是dB值译码性能会迅速恶化。常见的错误是把snr直接填进去或者开了UnitAveragePower却忘了星座平均功率不是1。qamdemod输出的LLR默认没有做符号功率归一化但对于固定调制阶数这个缩放不影响LDPC译码的判决结果因为LLR之间是等比例变化的。2.3 LDPC译码输入必须是LLR不要用硬判决绕过很多初学者为了“省事”直接在qamdemod里用OutputType,bit再喂给LDPC译码器。这样做其实也能跑但误码性能会损失1.5到2dB。HARQ场景下硬判决就更不适用了Chase合并要求把多次接收的软信息相加硬判决只能做投票信息量远低于LLR累加。所以在搭链路之前建议单独写一个软解调函数function llr softDemod16QAM(rxSym, noiseVar) llr qamdemod(rxSym, 16, OutputType, llr, ... UnitAveragePower, true, NoiseVariance, noiseVar); end这样在HARQ循环里每次调用都只用传两个参数避免那一长串qamdemod参数把主循环淹没。另外在AWGN信道下如果信噪比定义为符号能量与噪声功率之比那么噪声方差就是10^(-snr/10)如果定义为比特能量信噪比Eb/N0还需要除以log2(16)再乘以编码率。这个换算关系会在整个仿真里反复用到建议写注释标注清楚否则以后回看脚本会一头雾水。表LDPC16QAM链路里最常调的参数参数常见取值影响ParityCheckMatrixdvbs2ldpc(1/2)决定码率、码长和误码性能MaximumIterationCount8~50迭代越多译码越准仿真越慢UnitAveragePowertrue星座均方根为1噪声方差直接对应SNRNoiseVariance10^(-snr/10)必须与星座平均功率匹配否则LLR失真3. 在MATLAB里搭建HARQ重传从“重发”到真正“合并”3.1 Type-I 和 Chase 合并的取舍HARQ分两大类Type-I是出错就丢弃接收端只解当前收到的帧重传与之前没有关系Type-II里最常用的是Chase合并重传的是相同符号接收端把每次LLR逐位累加后统一译码。对于LDPC16QAM链路几乎所有人都会选Chase合并因为LDPC译码器本身就输入LLR合并只是一个加法器。增量冗余IR需要改变每次重传的编码速率MATLAB里得重新配置LDPC矩阵复杂度高但增益通常只有零点几个dB除非你在复现5G NR标准协议否则不建议一上来就做IR-HARQ。这里有一个容易混淆的点HARQ与ARQ的区别。ARQ不合并重传的帧如果还错就再丢弃HARQ会利用之前没有解出来的软信息。仿真时必须把合并逻辑写在解码之前不能“解码失败就清零接收缓冲”。我在不少示例代码里见过类似错误重传次数一多噪声反复累加性能反而比重传一次还差。3.2 用状态循环模拟发送端缓冲区和接收端LLR累加发送端需要把调制符号保存下来接收端要维护一个列向量rxLLRSum来累计软信息。下面这段代码是完整的Chase合并HARQ核心逻辑maxRetries 4; txBuffer modSym; % 保存待重传符号 rxLLRSum zeros(length(modSym), 1); % LLR 合并缓冲 retryCount 0; ackFlag false; while ~ackFlag retryCount maxRetries % 每次重传都经过独立 AWGN 信道 rxSym awgn(txBuffer, snr, measured); % 软解调得到当前帧 LLR llr qamdemod(rxSym, 16, OutputType, llr, ... UnitAveragePower, true, NoiseVariance, 10^(-snr/10)); % Chase 合并直接累加 rxLLRSum rxLLRSum llr; % 用合并后的 LLR 解码 decodedBits ldpcDecoder(rxLLRSum); % 用误码判断 ACK/NACK实际系统更严格会加 CRC if isequal(decodedBits, infoBits) ackFlag true; else retryCount retryCount 1; % 如果达到最大重传仍然失败一般直接宣告失败 if retryCount maxRetries break; end end end这里的isequal在仿真里模拟物理层CRC校验。实际LDPC译码即使没有正确收敛也有可能因为迭代次数限制而返回一组不满足校验方程的比特在纯仿真环境下用信息比特逐位对比是最可靠的方式。注意rxLLRSum不能清零否则第二次重传和第一次发送完全独立HARQ就退化成了ARQ。表HARQ仿真关键参数参数含义建议值maxRetries最大重传次数2~5再大吞吐量上不去合并方式LLR逐位相加ChaseACK延迟模拟反馈时延0静态仿真可不考虑重传粒度按码字还是按符号重发按码字方便对齐LLR如果要在仿真中加入反馈时延最简单的方法是在awgn循环里插入固定延迟例如每轮重传前先发送若干空闲帧。对于纯性能评估这种延迟可以忽略因为它不改变合并后的信噪比只影响吞吐量计算的分子分母。3.3 LLR合并之前要确认符号对齐和比特交织顺序Chase合并假设不同重传的比特到LLR的位置是一一对应的。如果你的链路里加了随机交织器重传时必须使用相同的交织表否则LLR加在错误的位置上。另一个常见坑是qammod的InputType,bit会自动把比特按列优先映射如果之前手动做了比特分组顺序可能不一致。保险做法是发送端把调制符号存下来重传时直接复用同一个txBuffer这样就从根上避免符号错位。4. 完整链路仿真SNR扫描、误码率统计和“假收敛”排查4.1 主循环怎么写才能既准又快完成单帧HARQ逻辑后下一步就是把它放进SNR扫描循环里。统计指标一般有两个误码率BER和吞吐量。BER用信息比特错误数除以信息比特总数吞吐量用成功解码的码字数除以总发射次数——重发次数越多吞吐量越低。snrVec 5:2:15; maxFrame 500; targetErr 50; for snrIdx 1:length(snrVec) snr snrVec(snrIdx); totalBitErr 0; totalBitTx 0; totalFrameTx 0; successFrame 0; for frame 1:maxFrame % 生成随机信息比特 infoBits randi([0 1], K, 1); codedBits ldpcEncoder(infoBits); modSym qammod(codedBits, 16, InputType, bit, ... UnitAveragePower, true); % 发送一次原始码字 txCount 1; rxLLRSum zeros(length(modSym), 1); needRetx true; while needRetx txCount maxRetries rxSym awgn(modSym, snr, measured); llr qamdemod(rxSym, 16, OutputType, llr, ... UnitAveragePower, true, NoiseVariance, 10^(-snr/10)); rxLLRSum rxLLRSum llr; decodedBits ldpcDecoder(rxLLRSum); if isequal(decodedBits, infoBits) needRetx false; successFrame successFrame 1; else txCount txCount 1; end end % 统计误比特即使重传失败也算错误 totalBitErr totalBitErr sum(decodedBits ~ infoBits); totalBitTx totalBitTx K; totalFrameTx totalFrameTx txCount; end ber(snrIdx) totalBitErr / totalBitTx; throughput(snrIdx) successFrame * K / totalFrameTx; end这段代码中各变量的作用txCount记录当前码字实际发射次数successFrame用于计算成功接收的帧数totalFrameTx则把所有重发次数加总。吞吐量的单位是“每个传输时隙成功的信息比特数”如果你知道具体符号速率再乘上符号速率就是bps。这里没有把HARQ反馈等待时隙算进去如果仿真目的是对比协议开销需要额外加一个等待因子。4.2 常见假象无噪声不收敛、高SNR反而变差下面这张表整理了我在调试HARQLDPC16QAM链路时最常碰到的问题。它们会伪装成“算法不收敛”或者“SNR高但性能差”。现象可能原因解决办法无噪声时误码率约为0.5LLR正负号反了检查qamdemod星座映射方向或尝试翻转所有LLR高SNR出现误码地板LLR噪声方差不准确认NoiseVariance是线性值且与UnitAveragePower匹配重传次数越多BER越高每次重传前清零了LLR缓冲确保rxLLRSum只在码字开始时清零吞吐量在低SNR反而高于高SNR统计公式分母用了编码比特而不是信息比特检查throughput计算里是否乘以了编码率解码结果时好时坏不稳定LDPC迭代次数太少把MaximumIterationCount提高到20以上其中“重传次数越多BER越高”这个现象尤其隐蔽。如果rxLLRSum被意外清零每次重传只是独立尝试多次尝试后误码率确实会下降但吞吐量下降很多性能曲线看起来像没有HARQ增益。另外使用parfor并行SNR扫描时要确保随机数种子独立否则多轮仿真结果完全一致统计没有意义。4.3 用单次全噪声仿真正确性在跑完整SNR曲线前建议先固定一个SNR比如10dB只跑100帧观察每一帧的重传次数和误码数。把disp加进主循环可以直观看到哪些帧需要重传两次哪些帧一次就过。如果所有帧一次就过说明SNR点取得太高不能体现HARQ增益如果几乎所有帧都重传4次以上说明SNR点过低。用这个简单步骤就能确定SNR扫描范围是否合理省得整条曲线跑完才发现数组全是0。5. 让HARQLDPC仿真“可信”的三个验证技巧LDPC、16QAM和HARQ组合在一起可以调节的参数非常多因此仿真结果必须先验证再使用。我一般会做以下三件事无噪闭环测试、理论曲线对照、用吞吐量而不是BER展示HARQ增益。无噪闭环测试是把snr设成100dB或直接在信道之前把接收信号赋值为发送信号。此时LLR应该非常大LDPC一次译码必成功。如果这个测试失败问题一定出在qammod/qamdemod参数或LLR正负号上而与HARQ逻辑无关。这个测试还能确认K的计算是否正确如果信息位数算错isequal永远不会成立。理论曲线对照是让LLR计算回归基础。在没有HARQ的普通LDPC16QAM链路上误码率曲线应该与文献里同码长同码率的性能曲线趋势一致。如果仿真的BER在10dB时还在0.1以上多半是噪声方差或星座功率没有对齐。这时候不要急着找HARQ的问题先让基础链路净增益看起来正常。常见做法是找一个不含HARQ的简单误码率脚本仅替换LDPC矩阵作对比。第三个技巧关于HARQ效果验证。BER曲线只能说明重传后每比特错误率下降但无法反映重传的资源浪费。正确指标是“吞吐量 vs SNR”曲线。当SNR低时重传次数增多吞吐量随之下跌SNR高时吞吐量趋于K除以一次发射。通过这条曲线能够直观看出在哪个SNR条件下引入HARQ是划算的。具体代码只需在主循环保存totalFrameTx和successFrame不需要额外改动链路结构。举个例子仿真后如果发现SNR8dB时平均每个码字发射1.8次而SNR10dB时平均1.1次两点的吞吐量差异比BER差异更明显。报告这种结果时我会把表头写成“平均发射次数”和“归一化吞吐量”这样读者可以快速评估HARQ增益。最终你得到的不仅是一条能跑的MATLAB链路而是一套能够支撑链路预算和算法选型决策的仿真工具。本文还有配套的精品资源点击获取