资讯详情

5G NR DL-SCH LDPC编码处理链详解与MATLAB仿真实战

📅 2026/10/15 6:21:50 | 华诺云谱 👁 阅读
5G NR DL-SCH LDPC编码处理链详解与MATLAB仿真实战
做5G物理层仿真的朋友应该都绕不过LDPC编码这道坎。我最初接触5G LDPC的时候对着协议文档里的基矩阵、循环移位、速率匹配这些概念折腾了很久尤其是在MATLAB里把整个DL-SCH处理链跑通踩的坑比想象中多得多。这篇内容就围绕5G NR下行共享信道DL-SCH的LDPC编码处理链结合MATLAB 5G Toolbox里提供的函数把从传输块Transport Block到最终码字的完整过程拆开讲清楚包括每个环节的原理、工具箱里对应的函数怎么用、以及我在实际调试中遇到的一些容易让人卡住的问题。如果你正在做LDPC相关的链路级仿真或者刚接触5G物理层想找一个能直接跑的编码参考实现这篇文章应该能帮你省下不少时间。1. 为什么5G数据信道偏偏选LDPC从Turbo到LDPC的换代逻辑1.1 高吞吐是5G的第一需求在LTE时代数据信道用的是Turbo码性能其实不错在中低码率下离香农限很近。但Turbo码有个天生的问题——迭代解码的延迟和吞吐量很难做到极致。5G的峰值速率目标比LTE高了一个数量级要求单用户下行速率达到Gbps级别Turbo码在这种吞吐压力下要么需要超大并行度的迭代器要么延迟会高到影响HARQ调度。LDPC码在解码上天然适合高并行。它的校验矩阵是稀疏的BPBelief Propagation解码算法可以在多个校验节点和变量节点之间并行更新硬件实现上用分层调度或全并行架构吞吐量可以做到很高。这也是3GPP在5G NR标准讨论时最终选定LDPC作为数据信道编码方案的核心原因——不是Turbo码不够好而是LDPC在高吞吐、低延迟场景下更有潜力。1.2 LDPC码的并行解码优势LDPC解码的核心是消息传递每个变量节点和校验节点之间交换概率信息。这个结构天然是并行的所有的校验节点可以同时更新所有的变量节点也可以同时更新。相比之下Turbo码的迭代结构是串行的每次迭代要依次完成前后两个分量解码器的软输入软输出SISO计算虽然也有并行实现但复杂度高得多。MATLAB 5G Toolbox里提供的LDPC解码函数底层实现也充分体现了这种并行优势。不过软件仿真里体现的主要是功能正确性吞吐量的优势主要还是在硬件实现上体现。但在仿真阶段LDPC相对Turbo在帧错误率FER性能上的表现也是非常重要的参考——尤其是高码率场景下LDPC的性能可以比Turbo好不少。1.3 BG1和BG2的设计逻辑5G NR标准里定义了两套LDPC基矩阵BG1Base Graph 1和BG2Base Graph 2。BG1的基矩阵大小是46行68列适用于大码块和高码率场景最大码率可以支持到接近1BG2的基矩阵大小是42行52列适用于小码块和低码率场景最低码率可以到1/5左右。为什么需要两套基矩阵因为码块长度和码率跨度太大一套矩阵难以在所有场景下都保持良好性能。BG1在高信噪比、高码率场景下性能更优BG2在低信噪比、低码率场景下性能更优两者在中间区域性能接近。实际使用中如果码块长度超过某个阈值或者码率较高就选BG1码块小或者码率低就选BG2。这个选择逻辑在协议里有明确表格MATLAB的nrDLSCH系统对象和nrCodeBlockSegmentLDPC函数都内置了自动选择逻辑但你最好也清楚背后的判断依据方便调试时排错。2. 从传输块到码字DL-SCH的LDPC处理链逐段拆解2.1 传输块CRC和码块分段在LDPC编码之前物理层要先对来自MAC层的传输块TB做处理。第一步是加CRC循环冗余校验传输块级别的CRC长度为24比特用于传输块整体的错误检测。CRC的生成多项式在协议里有明确规定MATLAB里用nrCRCEncode函数就能完成。加完TB级CRC之后如果传输块加上CRC的长度超过最大码块长度就需要做码块分段Code Block Segmentation。这里有个容易忽略的细节——分段的数量不是随便定的要满足一定的约束条件而且分段之后每个码块都要追加一个24比特的CRC这是码块级CRC用于解码时提前判断码块是否正确方便HARQ合并。LTE时代码块级CRC长度可以选择0、8、16或24比特但5G NR统一为24比特。这是因为5G的码块大小跨度大统一的24比特CRC可以在码块级提供足够可靠的错误检测能力。2.2 码块级CRC和LDPC编码的衔接完成码块分段和码块级CRC之后每个码块就可以交给LDPC编码器了。这里需要注意的是LDPC编码器的输入长度必须和基矩阵的信息列数匹配。BG1的信息列数是22列BG2是10列。也就是说无论码块的实际长度是多少编码时都要先把数据填充到K个信息比特BG1为22×ZcBG2为10×ZcZc是提升因子。所以码块分段之后还要做一个关键操作——填充比特Filler Bits。如果码块长度加上CRC之后不到K就需要在前面填充比特这些填充比特在编码后会被打掉不参与后续的速率匹配和传输。这个点很多人第一次做的时候容易疏忽后面我会专门讲。2.3 LDPC编码系统比特和校验比特LDPC编码本身在MATLAB里用nrLDPCEncode函数就能搞定。输入是填充后的信息比特序列输出是完整的编码后比特序列。BG1编码后的码字长度是66×ZcBG2是52×Zc。编码后的码字包含两部分系统比特和校验比特。系统比特就是原始信息比特的副本校验比特是编码器计算出的冗余信息。5G LDPC的校验矩阵是双对角结构的这使得编码可以像Turbo码一样用递推方式高效完成避免了高复杂度的高斯消元这是5G LDPC选型时的一个重要考虑——不仅要解码高效编码也不能成为瓶颈。2.4 速率匹配与比特选择编码后的码字长度远大于实际可用的物理资源承载能力所以必须做速率匹配。5G NR LDPC的速率匹配并不是简单的打孔Puncturing或重复Repetition而是基于循环缓冲区的比特选择。具体来说编码后的比特会按照协议规定的顺序写入一个循环缓冲区这个顺序不是简单的先系统比特后校验比特而是有一套特定的交织规则。BG1和BG2的比特选择顺序不同而且要根据冗余版本RVRedundancy Version从缓冲区不同位置开始读取。MATLAB的nrRateMatchLDPC函数封装了整个速率匹配过程只需要传入编码后的码字、BG类型、速率匹配输出长度和RV值就可以。速率匹配输出的比特再经过加扰、调制、层映射、预编码、资源映射最后变成OFDM符号发射出去这就是完整的下行数据链路。MATLAB 5G Toolbox的nrDLSCH系统对象把从传输块到速率匹配的全过程封装成了一个整体方便做高层仿真但如果你要深入学习或者做某些定制化处理还是有必要自己把每个步骤拆开实现一遍。3. MATLAB工具箱实操核心函数用法与参数理解3.1 nrCodeBlockSegmentLDPC码块分割函数nrCodeBlockSegmentLDPC的作用是把带CRC的传输块分割成码块并完成LDPC编码前的填充。函数的输入是完整的传输块比特包括TB级CRC输出是分割后的码块集合以及相关的元信息比如每个码块的长度、是否有填充比特、选择的BG类型等。这里最关键的是搞清楚函数的两个判断逻辑选择BG1还是BG2确定提升因子ZcBG的选择依据是码块长度和码率。函数内部会根据输入传输块长度、目标码率等信息自动判断但你可以在调用前显式指定TargetCodeRate参数来控制这个决策。Zc的选择则是基于码块长度查表得到的标准里定义了8个Zc取值范围每个范围对应一组提升因子。3.2 nrLDPCEncode编码函数nrLDPCEncode接收的信息比特长度必须是K 22×ZcBG1或K 10×ZcBG2不能多也不能少。这也是为什么在分割时必须做填充操作——如果码块数据不够K比特就必须填充到K后再编码。一个常见的困惑是填充比特填的是什么值在5G NR中填充比特的值为0但它和普通的信息比特0是有区别的。填充比特在编码后会被打掉它只参与编码计算、不参与传输这个信息在nrCodeBlockSegmentLDPC的输出带了个参数cbsInfo.NumFillerBits会标注清楚。编码输出的码字长度是N 66×ZcBG1或N 52×ZcBG2。这个输出是完整的系统比特加校验比特还没有做速率匹配。3.3 nrRateMatchLDPC速率匹配函数速率匹配函数的输入是编码后的码字、BG类型、期望的输出比特数也就是这个码块实际能分配到的资源比特数以及RV值。输出就是经过比特选择和交织后的待传输序列。RV值有4个选择0、1、2、3。不同RV值决定了从循环缓冲区的哪个位置开始读取比特。在HARQ重传中第一次传输用RV0重传时按顺序使用后续RV。BG1和BG2的RV起始位置不同这个函数内部已经处理好了不用自己计算。比较容易被忽略的是nrRateMatchLDPC输出长度不一定是为码块分配的比特数——如果码块有填充比特速率匹配时会跳过填充位置所以实际输出比特数可能大于分配的资源比特数不对实际输出比特数就等于传入的Nref参数但里面的内容有微妙之处。这个细节我建议你自己跑一下rateMatchOutput nrRateMatchLDPC(encoded, bg, Nref, rv)对比一下输入输出长度能更好地理解填充比特在速率匹配中的处理方式。3.4 完整的编码循环从TB到码字把一个传输块完整处理成待调制比特序列的代码逻辑大致是% 1. TB级CRC tbCrc nrCRCEncode(tbBits, 24A); % 2. 码块分割 [cbs, cbsInfo] nrCodeBlockSegmentLDPC(tbCrc, bgn, TargetCodeRate, r); % 3. 逐码块编码 modBits []; for i 1:cbsInfo.NumCodeBlocks cw nrLDPCEncode(cbs{i}, bgn, cbsInfo.Zc{i}); % 4. 速率匹配 rmBits nrRateMatchLDPC(cw, bgn, Nref, rv); % 5. 加扰伪代码示意 scrambled nrPuschScramble(rmBits, ...); % 6. 调制 sym nrSymbolModulate(scrambled, QPSK); modBits [modBits; sym]; end这个循环在功能上等价于nrDLSCH的step方法但拆开写的好处是你可以随时插入调试代码检查每步的输出是否正确。我自己在开发过程中就经常在第三步和第四步之间停下来做中间结果校验。3.5 nrLDPCDecode解码函数工具箱里的nrLDPCDecode配合nrRateRecoverLDPC一起使用接收端做逆操作。nrRateRecoverLDPC把接收到的软比特恢复成完整的码字长度包括填充位置填充的默认LLR值然后nrLDPCDecode进行迭代解码。解码函数最常用的参数是MaxLDPCIterationCount——迭代次数上限。增大迭代次数可以略微改善性能但会线性增加仿真时间。在实际仿真中我通常先设成8到10次看整体性能趋势需要精确的最终性能曲线时再调大到20次左右。需要注意解码结果是软比特LLR要转为硬判决比特需要做符号判断。4. 实战中最容易踩的坑速率匹配长度、填充比特和RV选择4.1 速率匹配输出长度怎么定很多新手在这里犯的第一个错误就是以为速率匹配输出长度等于编码后码字长度的一半或者某个固定比例。实际上速率匹配输出长度完全取决于这个码块分配到的物理资源比特数它和调制阶数、分配的物理资源块数量、符号数、层数都有关系。在nrDLSCH封装里这个长度是根据调度参数自动算出来的。手动实现时你需要先算出总的有效资源比特数再按比例分给各个码块。这里有个原则——前面的码块可以多分一个比特后面的码块少分一个比特。这个规则在标准里有明确公式简单说就是码块之间资源尽量平均、但允许最多相差1比特。4.2 填充比特在速率匹配时被忽略BG1的K是22×ZcBG2的K是10×Zc但码块数据大概率不等于K。比如一个码块实际数据是4000比特对应的Zc选出来是256BG1的K就是5632比特要填充1632个填充比特。填充比特参与编码但速率匹配时会跳过这些填充位置。接收端知道哪些位置是填充比特——就是把那些位置的LLR设成一个很大的值表示已知为0。如果接收端不知道填充比特的数量把填充位置的LLR按普通数据来解解码性能会严重劣化。nrRateRecoverLDPC函数返回的中会标注填充位置实际使用时要特别注意把这部分信息传给解码器。我自己就遇到过解码性能怎么调都上不去最后发现是速率恢复时没有给填充位置赋对应的先验信息。4.3 RV顺序与HARQ的配合在HARQ重传场景下RV的选择顺序有讲究。通常第一次传输用RV0因为它包含了系统比特和足够的校验比特可以在信道条件好时一次解出。后续重传依次使用RV2、RV3、RV1。为什么不是按0、1、2、3的顺序因为RV1和RV2在循环缓冲区中的起始位置不同按0、2、3、1的顺序可以最大化冗余信息的增量。在MATLAB工具箱里每次重传只需要改变传给nrRateMatchLDPC的RV值即可。但要注意如果是模拟系统级调度你需要自己维护每次传输的RV序号工具箱不会自动帮你做这个状态管理。4.4 解码端LLR的缩放问题nrLDPCDecode的输入是软比特LLR。如果你从仿真链路里拿到的是接收符号要先做解调得到LLR再做速率恢复。一个非常容易踩的坑是LLR的幅度没有归一化。不同的调制阶数、不同的噪声方差估计误差会让LLR的绝对值相差很大而LDPC解码对LLR幅度是敏感的。解决方法是做LLR归一化可以用噪声方差对LLR做缩放也可以直接用nrSymbolDemodulate的输出并设置NoiseVar参数。工具箱里nrSymbolDemodulate支持传入噪声方差这样输出的LLR就是归一化后的。这个细节在低信噪比、高阶调制如256QAM的场景下影响特别明显。4.5 BG选择不一致导致解码失败编码端用BG1编码解码端如果默认选了BG2结果会完全解不出来。这个错误在手动实现时很容易发生尤其当你的链路里同时支持多种传输块长度时。nrCodeBlockSegmentLDPC的bgn参数默认是2这意味着如果你不加指定它默认按BG2做分割和填充。例如输入的传输块比较大超过BG2支持的最大码块尺寸函数会自动切换到BG1所以一般情况下自动选择是没问题的。但如果你在编码端显式指定了BG1解码端的nrRateRecoverLDPC和nrLDPCDecode必须也指定BG1这依赖你正确传递信号参数。建议在仿真代码里用同一个配置变量控制收发两端的bgn避免写死。5. 链路验证方法从BLER曲线到吞吐量分析5.1 搭建最小闭环验证链路无论你最终要实现什么功能第一步永远是搭一个最小闭环随机生成传输块 → 编码 → 调制 → 加AWGN噪声 → 解调 → 解码 → 对比误码。这个闭环可以只用上述几个工具箱函数在几十行代码内完成。验证的几个关键指标无误码加噪后没有任何比特错误编解码一致性编码器输出经过解码器之后能还原原始传输块填充比特位置确认速率恢复后填充比特位置正确闭环跑通后再逐步往上叠加信道模型、MIMO、HARQ等模块。5.2 按照3GPP参数设置仿真环境做链路级仿真性能评估时建议尽量按3GPP定义的标准测试参数走。比如码率、调制方式、信道模型、载波频率、子载波间隔等。MATLAB工具箱里带了nrSimulateDLPerfectChannelEstimate这种参考案例但代码比较长初学者直接跑容易头晕。我建议是先用最简单的AWGN做全链路的性能基线确认BLER曲线的趋势正确再加入衰落信道。一个参考的仿真流程snrVec -2:0.5:6; blerResults zeros(size(snrVec)); for snrIdx 1:length(snrVec) numBlkErr 0; numBlkTotal 0; while numBlkErr 20 numBlkTotal 500 % 生成TB、编码、调制、过信道、解调、解码、CRC校验 % 若CRC校验失败numBlkErr加1 % numBlkTotal加1 end blerResults(snrIdx) numBlkErr / numBlkTotal; end注意BLER曲线统计时要以传输块的CRC校验结果为准而不是码块级CRC。因为有些场景下码块正确但传输块错误这时仍然算一次传输块错误。5.3 吞吐量和HARQ的仿真注意点如果你要做HARQ系统的吞吐量仿真重点不再是单次传输的BLER而是多次重传合并后的有效吞吐量。MATLAB工具箱里模拟接收端可以保留每次重传的接收LLR然后做合并解码。HARQ合并有两种方式Chase合并每次发送相同的比特和增量冗余IR每次发送不同的冗余比特。5G NR默认支持IR合并也就是每次重传用不同RV。仿真的时候要注意每次重传后LLR合并需要做符号级对齐速率恢复前要把多个版本的LLR按比特位置叠加填充位置的LLR始终设为高值实现细节建议参照工具箱参考案例不要自己造轮子这个领域牵扯到的边界情况比想象的多。6. 性能优化的实用经验迭代次数、LLR精度与仿真加速6.1 解码迭代次数的取舍LDPC解码的迭代次数越多性能越好但仿真耗时也越大。我在实际仿真中发现对于大多数场景迭代16次和迭代20次的BLER差异小于0.1dB但仿真时间差约20%。如果你只是做功能验证或算法对比8次足够做标准性能评估建议12到16次这是性能和速度的平衡点。工具箱里nrLDPCDecode默认迭代次数是6次这个值偏少。对于1/3码率以下的场景6次迭代性能损失可能超过0.5dB。建议根据码率调整码率越低需要的迭代次数越多。6.2 LLR量化精度对性能的影响硬件实现中LLR通常用有限比特位宽表示如4比特或6比特但软件仿真默认用浮点。如果你要模拟硬件行为可以对LLR做量化。量化对LDPC性能的影响在高阶调制时更明显因为高阶调制的LLR动态范围更大。比如256QAM下如果只用4比特量化LLRBLER性能可能损失0.3-0.5dB。但6比特量化损失就很小了基本可以忽略。如果你的目标是评估硬件方案建议在链路里加上量化模型否则到硬件验证阶段会吃惊。6.3 并行仿真与批量处理LDPC仿真的一个优势是可以并行——多个信噪比点、多个传输块之间完全独立。MATLAB的并行计算工具箱Parallel Computing Toolbox可以把循环里的多次传输块处理改成parfor在物理多核机器上可以线性加速。需要注意parfor中不能用带状态的对象需要把每个迭代完全独立的计算封装成函数。我通常会把传输块处理写成一个独立函数输入是随机种子、SNR、配置结构输出是CRC校验结果。然后在外层做parfor调用这样可以兼顾代码简洁和仿真效率。另外有一个小建议在仿真时随机数流可以并行生成但需要注意各并行任务间不要产生相同的随机序列。用RandStream配合子流分配保证每个任务使用不同的随机种子是避免并行却重复的关键。6.4 浮点转定点的快速验证方法在从算法仿真走向硬件实现之前通常需要做定点仿真。MATLAB的Fixed-Point Designer工具箱可以和5G Toolbox配合但直接对所有函数做定点化会很痛苦。我的建议是先只对解调和LLR部分做定点模型看性能损失如果这个损失可以接受再考虑LDPC解码器内部的定点化。这是因为解调器和LLR计算的动态范围特性最容易影响性能而且实现上也是独立模块。LDPC解码内部的消息传递值范围相对稳定硬件实现通常比解调器还简单。从接口往外层推进每次只改一个模块的定点模型这样调试起来效率最高。7. 仿真实战的技巧如何排查解不出来的问题7.1 二分法定位编解码链路问题如果你搭了一个闭环链路发现解码后CRC校验一直失败第一反应不应该是怀疑LDPC解码器而是先用二分法定位问题在哪一段。我自己的排查顺序通常是先检查编码前的CRC把原始传输块的CRC校验结果打出来确认CRC编码本身正确。再检查编码后速率匹配输出的长度是否和调制/资源映射匹配长度不匹配通常会直接报错或解调结果异常。解调后检查LLR的均值符号是否和发送比特一致——如果在高SNR下LLR符号还经常判断错说明解调或速率恢复出错。最后检查解码器的输入维度是否和nrLDPCDecode要求一致特别看是否有非法的NaN或Inf。这个流程能覆盖90%以上的解不出来问题。不要一上来就调迭代次数或者怀疑信道模型。7.2 用仿真日志记录关键中间变量在实际开发中调试信息的重要性不亚于代码本身。建议在每个模块边界打日志记录输入输出长度、CRC是否通过、填充比特数量、RV编号。把这些工作做到位之后排错基本就是看日志找哪一步不对的问题。这里分享一个我用的日志模板[TB] TB length 10000, CRC pass yes [CBS] NumCodeBlocks 3, BG 1 [CBS #1] K 4096, filler bits 96, Zc 128 [ENC #1] output length 8448 [RM #1] Nref 7000, RV 0, output length 7000 [MOD] QPSK, num symbols 3500通过这些信息你可以很快定位到长度不匹配、填充错误、RV设置异常等问题。7.3 对比工具箱参考实现的输出如果你在实现过程中不确定某一个模块的输出是否正确最直接的方法是调用nrDLSCH系统对象做相同参数的完整流程然后对比你自己的分步实现的中间结果。nrDLSCH是经过验证的参考实现它的分步处理结果可以作为ground truth。当然这个对比只用于调试仿真性能评估时还是要用自己拆开的实现。对比时要注意比特序的问题——5G里CSI码字-层映射、加扰、调制过程中存在比特顺序的重新排列容易造成看起来相同、实际上顺序反了的情况。对比之前先在发送端每个模块打点确认每个模块的输出完全一致再往下走。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑