HARQ链路仿真:软合并增益计算与MATLAB实现详解
2026/9/14 15:08:35 网站建设 项目流程

简介:一份以HARQ(混合自动重传请求)为核心的MATLAB仿真资源,压缩包共1个m脚本文件、大小仅6KB,专门面向通信系统设计者、信号处理学习者以及无线通信方向的学生,用于快速掌握HARQ原理与系统级性能评估,也可作为课程设计、课题研究或工程预研的起点。脚本以信号处理为基础,围绕HARQ系统的吞吐量分析和重要参数提取展开,覆盖Chase合并、停等ARQ、增量冗余等典型HARQ类型,并集成FEC编码(如Turbo码、LDPC码、卷积码)、AWGN与Rayleigh信道模型,以及基于反馈的重传决策逻辑;同时定义了误码率、帧错误率、信噪比、重传次数、编码速率等关键参数的统计口径和输出方式。已有162人浏览/学习。运行主脚本后,读者能直观获得各指标随信道条件和系统配置的变化曲线,理解重传次数、编码速率与有效吞吐量之间的权衡关系;还可通过修改参数观察不同HARQ策略对可靠性和效率的影响,为课程实验、毕业设计或论文仿真提供可直接修改的基础模板。整体结构紧凑,便于在此基础上扩展新的信道模型、调制方式或协议算法。

1. HARQ 在链路仿真里为什么不能省

做物理层链路仿真的人迟早会遇到这个场景:AWGN 信道下 BER 曲线已经贴着理论值走了,一换到衰落信道,相同 SNR 下的误块率突然抬高一到两个数量级。这时候单纯加大发射功率往往不是最优解,因为时变信道下的深衰落是功率换不回来的,真正有效的手段是把时间分集用起来——HARQ 做的就是这件事。HARQ 是混合自动重传请求,本质是把前向纠错(FEC)和自动重传(ARQ)揉在同一个闭环里,让接收端把错误帧的软信息缓存下来,和重传帧合并后再译码。它和普通 ARQ 的差别在于:重传不是把旧数据丢掉了重来,而是带着上一次译码失败的“记忆”参与联合译码。对于手里有一份yingteng.zip解出来的 HARQ MATLAB 代码、想在仿真环境里复现或改造它的人来说,这篇文章会把 HARQ 的类型、软合并增益计算、Turbo 码和 LDPC 码两条实现路径、5G Toolbox 的标准进程都拆开讲清楚,并且给出可以直接跑的脚本骨架。内容面向做链路级仿真和算法验证的工程师,也面向正在啃 HARQ 相关代码的研究生。

2. HARQ 的三种合并方式与 MATLAB 仿真选型

2.1 类型 I、Chase 合并与增量冗余的本质区别

HARQ 从实现机制上分三代,MATLAB 代码里最常见的三种写法对应三种不同的重传策略。

类型 I HARQ 最朴素:接收端译码失败后直接丢弃当前帧,请求发送端重传同样的编码数据,重传帧独立译码。这种方式的缓存开销最小,但没有任何合并增益,本质上和普通 ARQ 加 FEC 没有区别,唯一的价值是实现了“重传”这个闭环动作。类型 II HARQ 也叫增量冗余(Incremental Redundancy,IR),每次重传发送的是上一帧没有发过的校验比特,接收端把多次传输的软比特拼在一起,拼出一个更低码率的码字再译码。Chase 合并(类型 II 的特例,也叫软合并)则是对每个比特的多个副本做加权平均,重传内容与首传完全相同,合并后等效获得分集增益。

三种方式里 IR 的频谱效率最高,因为每次重传都在贡献新的校验信息;Chase 合并实现最简单,只需要一个加法器;类型 I 在三者中性能最差,已经被前两者取代。MATLAB 中判断一份代码用的是哪种方案,只需要看一件事——接收端有没有对上一次的 LLR 做缓存。

2.2 软合并的增益到底怎么算

Chase 合并的增益可以精确算出来。假设第 i 次传输时某个比特的 LLR 值为 L_i,那么合并后的 LLR 是:

L_combined = sum(L(1:N)) % N 次传输的 LLR 直接相加

这个式子成立的前提是各次传输经历的是独立衰落,且噪声方差不变。在 AWGN 信道下,L_combined 的信噪比是单次的 N 倍,也就是 10*log10(N) dB 的增益。但 Rayleigh 信道下这个增益不固定,因为各次传输的信道系数不同,合并后等效信道系数变为各次信道系数的模平方和。实际仿真中观察到的 Chase 合并增益通常在 1.5 到 3 dB 之间,具体取决于重传次数和信道的相关时间。

IR 的增益来源不同。每次重传增加新的校验比特,等效码率从 R 降到 R/(N),距离 Shannon 限更近,所以 IR 在低码率区域的表现优于 Chase 合并。MATLAB 仿真里验证这一点的方法是对同一份数据分别跑 Chase 和 IR 的 BLER 曲线,观察固定 BLER=0.1 处的 SNR 差值。

2.3 用哪个工具箱:Communications Toolbox 还是 5G Toolbox

写 HARQ 仿真代码前先确认手里的工具箱版本。Communications Toolbox 提供的是底层的 FEC 函数,比如comm.TurboEncodercomm.TurboDecodercomm.LDPCEncoder,这些函数只管编解码,不管 HARQ 进程管理,所有重传时序、软合并缓存、RV 选择都要手工写。5G Toolbox 则是把 HARQ 进程整个封装好了,nrDLSCHnrULSCH这些 System object 内部已经包含 CRC、码块分段、速率匹配、RV 轮转、软合并逻辑,适合做标准符合性测试。

选型的判断标准很简单:如果目标是验证 HARQ 算法本身的性能,比如对比合并方式、设计新的重传策略,用 Communications Toolbox 手写控制逻辑;如果目标是和 5G 协议对齐,用 5G Toolbox。前者灵活但代码量大,后者省事但对算法级别的修改不友好。

提示:检查工具箱版本用ver('5G Toolbox'),如果返回空,说明当前 MATLAB 环境没有安装该工具箱。

3. 用 MATLAB 手写 Type-II HARQ:Turbo 码 + 速率匹配 + 软合并

3.1 发射端:CRC 追加、Turbo 编码与打孔

Type-II HARQ 的发射端核心是“编码一次,多次打孔”。先看完整的发射端代码:

% 发射端参数 K = 1024; % 信息比特长度 R = 1/3; % Turbo 母码码率 crvList = [0 2 3 1]; % RV 轮转序列,5G 标准定义 nMax = 4; % 最大重传次数 % 信息比特流 data = randi([0 1], K, 1); % 1. CRC 追加 crcGen = comm.CRCGenerator('Polynomial', [1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1]); dataCrc = crcGen(data); % 2. Turbo 编码(母码码率 1/3) turboEnc = comm.TurboEncoder('TrellisStructure', poly2trellis(4, [13 15], 13), ... 'InterleaverIndicesSource', 'Random'); coded = turboEnc(dataCrc); % 3. 速率匹配:根据 RV 选择打孔位置 [rvIdx, rmBits] = rateMatchTurbo(coded, K, crvList(1), R); % 4. QPSK 调制 modulator = comm.QPSKModulator('BitInput', true); txSignal = modulator(rmBits);

这里的rateMatchTurbo需要自己实现,它的核心逻辑是把母码比特循环缓冲到一个虚拟环形缓冲区中,RV 决定从哪个位置开始截取。5G NR 标准里 RV=0 从缓冲区起点取,RV=2 从 1/2 位置取,RV=3 从 1/4 位置取,RV=1 从 3/4 位置取。这样做的好处是不同 RV 取出的比特段重叠度不同,RV 之间距离越远,重传提供的新校验比特越多。

3.2 接收端:LLR 解调和重传软合并

接收端的核心是软合并缓存和去速率匹配。每次重传到达后,先把新传输的 LLR 填入环形缓冲区对应位置,再和缓存里已有的 LLR 相加:

% 接收端软合并缓存初始化 llrBuffer = zeros(3*length(dataCrc), 1); % 存放合并后的 LLR alpha = zeros(3*length(dataCrc), 1); % 记录每个位置的有效合并次数 % 对每次传输 for tx = 1:nMax % 1. QPSK 软解调得到 LLR demodulator = comm.QPSKDemodulator('BitOutput', true, 'DecisionMethod', 'LLR'); rxLLR = demodulator(rxSignal); % 2. 去速率匹配:把接收 LLR 放回母码位置 [deRateLLR, validBits] = deRateMatchTurbo(rxLLR, K, crvList(mod(tx-1, 4)+1), R); % 3. 软合并:相同位置的 LLR 叠加 idx = find(validBits); llrBuffer(idx) = llrBuffer(idx) + deRateLLR(idx); alpha(idx) = alpha(idx) + 1; % 4. 归一化(可选):避免 LLR 幅度不一致 llrNorm = llrBuffer ./ max(alpha, 1); % 5. Turbo 译码 turboDec = comm.TurboDecoder('TrellisStructure', poly2trellis(4, [13 15], 13), ... 'InterleaverIndicesSource', 'Random', 'NumIterations', 6); decoded = turboDec(llrNorm(1:2*length(dataCrc))); % 6. CRC 校验 crcDet = comm.CRCDetector('Polynomial', [1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1]); [~, err] = crcDet(decoded); if ~err % 译码成功,清空缓存 llrBuffer = zeros(size(llrBuffer)); break; end end

软合并时有一个关键细节:llrBuffer必须存原始 LLR,不要提前归一化。因为 Turbo 译码器对 LLR 输入的幅度尺度有一定容忍范围,但多次重传后 LLR 幅度会自然增长,如果每次合并后都做归一化,会丢失合并次数的信息。正确做法是译码前一次性归一化,或者干脆不归一化,直接给译码器。

3.3 主循环脚本:重传次数与误块率统计

完整仿真链路需要一个外层循环来统计不同信噪比下的 BLER,以及一个内层循环来跑足够多的帧:

snrVec = 0:1:6; % 仿真 SNR 范围 numFrames = 1000; % 每个 SNR 点帧数 bler = zeros(size(snrVec)); for snrIdx = 1:length(snrVec) errors = 0; for frameIdx = 1:numFrames % 生成数据、编码、调制、过信道 % ...(此处省略信道模型和收发代码,见上文) % HARQ 重传循环 for txIdx = 1:4 % 发射端:根据 RV 打孔并发送 % 接收端:软合并 + 译码 + CRC % 如果 CRC 通过,errors 不变,break % 如果 4 次都失败,errors = errors + 1 end end bler(snrIdx) = errors / numFrames; end figure; semilogy(snrVec, bler, 'o-', 'LineWidth', 1.5); grid on; xlabel('SNR (dB)'); ylabel('BLER');

统计 BLER 时有个常见误区:把每次重传当作独立试验来计数。比如一帧数据第一次传输就失败、第二次成功,这不叫 0.5 个错误,而是 0 个错误(最终正确接收)。BLER 的分子是“经过全部重传后仍然失败的帧数”,分母是总帧数,不要和 PER(Packet Error Rate)混为一谈。

4. 基于 5G Toolbox 的标准 HARQ 进程实现

4.1 nrDLSCH 和 nrRateMatch 的参数对照

如果手里代码是用 5G Toolbox 写的,核心对象是nrDLSCH,它的参数比手写方案多了很多协议概念。先看一个典型配置:

% 创建 DLSCH 对象 dlsch = nrDLSCH; dlsch.TargetCodeRate = 0.5; dlsch.Modulation = 'QPSK'; % 配置 HARQ 进程 harq = HARQProcess; harq.NRed = 2; % 冗余版本数 harq.RVSequence = [0 2 3 1]; % 传输块处理 [trBlk, trBlkInfo] = dlsch.encode(tb, harq);

TargetCodeRate决定速率匹配的基准码率,Modulation决定调制阶数,二者共同决定传输块能承载的比特数。HARQ 进程中RVSequence是关键参数,5G 标准里定义 RV 的轮转顺序是 0→2→3→1,每次重传按这个顺序取下一个 RV。

对比手写代码和 5G Toolbox 的参数,可以发现以下对应关系:

参数手写方案5G Toolbox说明
母码码率R = 1/3内部固定Turbo 母码固定 1/3,LDPC 内部处理
目标码率打孔比例决定TargetCodeRate数值越大,有效传输比特越少
冗余版本crvList = [0 2 3 1]RVSequence决定每次重传的打孔起点
交织器自定义协议内建5G 的交织模式由协议规定
CRC 多项式自行选择协议内建5G 使用标准 CRC24A/B/C

4.2 HARQ 进程时序:RV 轮转和 NDl 标志

5G 标准中的 HARQ 是同步的,发送端和接收端对重传的时序有严格约定。MATLAB 里模拟这个过程时,harqProcess对象需要跟踪每个进程的状态:

classdef HARQProcess < handle properties RVSequence = [0 2 3 1] CurrentRV = 0 NDI = 0 % New Data Indicator Buffer end methods function retransmit = step(obj, crcPass) if crcPass % 译码成功,重置为新的传输 obj.CurrentRV = obj.RVSequence(1); obj.NDI = mod(obj.NDI + 1, 2); % NDI 翻转 retransmit = false; else % 译码失败,取下一个 RV idx = find(obj.RVSequence == obj.CurrentRV); obj.CurrentRV = obj.RVSequence(mod(idx, length(obj.RVSequence)) + 1); retransmit = true; end end end end

NDI(New Data Indicator)是 HARQ 进程里的一个重要标志位:NDI 不变表示当前传输是上一次数据的重传,NDI 翻转表示这是一个新的传输块。接收端靠 NDI 来判断软合并缓存是否需要清空,而不是靠 RV 是否为 0——因为 RV=0 也可能是重传。

4.3 用 nrDLSCH 跑一次完整的重传链路

结合nrDLSCH和自定义 HARQ 进程管理,可以搭一条完整的链路:

% 系统参数 nFrames = 100; snr = 2.0; % dB % 初始化收发对象 dlsch = nrDLSCH; dlsch.TargetCodeRate = 0.5; dlsch.Modulation = 'QPSK'; harq = HARQProcess; rxBuffer = []; % 主循环 for n = 1:nFrames % 生成传输块 tbSize = 512; tb = randi([0 1], tbSize, 1); % 编码并调制 [coded, info] = dlsch.encode(tb, harq); txSig = nrSymbolModulate(coded, 'QPSK'); % 过信道(AWGN + Rayleigh) chGain = 0.5 + 0.5*randn; % 信道系数 rxSig = txSig.*chGain + 10^(-snr/20)*randn(size(txSig)); % 解调和译码 rxLLR = nrSymbolDemodulate(rxSig, 'QPSK', 10^(-snr/10)); % 如果存在待合并的缓存,先合并 if ~isempty(rxBuffer) rxLLR = rxLLR + rxBuffer; end % 译码 [tbHat, crcPass] = dlsch.decode(rxLLR, harq, 'TargetCodeRate', 0.5); % 更新 HARQ 进程状态 retransmit = harq.step(crcPass); % 如果需要重传,保存当前 LLR if retransmit rxBuffer = rxLLR; else rxBuffer = []; end end

这段代码的帧结构做了极大的简化,真实 5G NR 链路还需要考虑 OFDM 调制的资源映射、PDSCH 的时频资源分配等内容。这里把物理层简化成符号级,是为了把 HARQ 进程的逻辑单独拎出来。实际项目中,dlsch.encode返回的info结构体里有G(传输块编码后的总比特数)等重要参数,用于后续的速率匹配和资源映射,不要忽略。

5. HARQ 仿真的 4 个常见坑:从误码平台到符号对齐

5.1 打孔表读错导致的高误码平台

这是手写速率匹配时最隐蔽的坑。Turbo 码的循环缓冲区长度是3*K + 12(包含尾部比特),如果打孔索引计算错误,重传数据会和首传数据撞在同一个位置,导致合并时某些位置的 LLR 被重复相加,而另一些位置始终没有数据。表象是误码率在高 SNR 区域出现平台,且 BLER 曲线斜率变小。排查方法是在发射端打印每次传输实际取出的比特索引,检查 RV=0 和 RV=2 取出的比特重叠率是否和理论一致——重叠率过高说明打孔起点算错了。

5.2 软合并时没有做符号对齐

从 zip 包移植代码时常见的错误是直接把解调后的 LLR 相加,但两次传输经过的信道和调制过程不同,LLR 的幅度尺度就不一致。比如第一次传输经过衰落信道,第二次是 AWGN,两次 LLR 的方差不同,直接相加会让合并结果偏向方差大的一方。解决方法是先估计每帧的噪声方差,对 LLR 做归一化再合并:

% 对每帧的 LLR 做噪声归一化后再合并 llrNorm = rxLLR ./ noiseVar; llrBuffer = llrBuffer + llrNorm;

对应地,发射端所有重传的调制方式必须相同,否则 LLR 的映射规则不一致,合并数学上不再成立。5G 标准允许 RV 不同但调制方式不变,就是这个原因。

5.3 RV 顺序和冗余版本的映射错误

RV 的轮转顺序不是 0→1→2→3,而是 0→2→3→1。这个顺序是经过优化的:RV=0 和 RV=1 的比特重叠最少(约 25%),保证重传提供最多新校验信息;RV=2 和 RV=3 之间的间距也类似。如果代码里把 RV 顺序写成自然序,前两次重传的增益会下降 0.5 到 1 dB。检查方法很简单:打印每次重传取出的比特在环形缓冲区中的起始位置,应该分别是 0、1/2、3/4、1/4 总长度处。

5.4 从 zip 包移植代码时的浮点与定点问题

最后是代码移植中一个容易忽略但很实际的场景:很多导师或同事给的 MATLAB 代码里同时包含浮点链路和定点链路两套实现。yingteng.zip这类包里,浮点版本通常用来验证算法性能,定点版本需要转成 C 或 Verilog。做定点化时,软合并缓冲区的位宽选择会直接影响性能:位宽太窄,多次重传累加后溢出,高 SNR 下 BLER 反升;位宽太宽,硬件面积浪费。经验法则是缓冲区位宽比单次 LLR 位宽多 log2(最大重传次数) 比特,比如 LLR 是 6 bit、最大 4 次重传,缓冲区用 8 bit 就不会溢出。用 MATLAB 验证定点性能最直接的方式是把每帧的 LLR 用quantizer对象做量化后再合并,对比浮点和定点下的 BLER 差异是否在 0.1 dB 以内。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询