
简介本资源是面向通信工程专业学生、5G协议研究者及MATLAB开发者的技术实践包聚焦5G NR标准下LDPC码的完整仿真实现解决从理论参数理解到可运行代码落地的关键断点。压缩包共9个文件7个.m脚本2个.mat校验矩阵数据总大小仅20KB轻量但结构完整包含3GPP基图选择、提升尺寸计算、H矩阵生成、 puncturing处理及端到端仿真主函数等核心模块覆盖编码、信道加噪、BP迭代解码全流程。已有1360人学习下载体现了其在教学演示与算法验证场景中的实用价值。用户可直接运行sim_ldpc_v1.m复现5G LDPC性能曲线借助get_pcm.m和get_3gpp_base_graph.m深入理解3GPP Release 15定义的两类协议基图构造逻辑并通过.mat文件快速加载标准化校验矩阵显著降低协议级LDPC仿真的入门门槛与调试成本。 5G和LDPC这两个词放在一起几乎就是现代移动通信物理层的代名词。做通信算法的人多半都绕不开MATLAB里的LDPC仿真——从最初在论文里看到校验矩阵构造方法到真正把5G NR标准里的BG1/BG2基图跑通这中间的坑比想象中多得多。我花了不少时间把5G标准协议下的LDPC编码译码链路在MATLAB里完整实现了一遍从基矩阵生成、扩展成校验矩阵、编码、译码到整条仿真链路评估现在把这些过程整理出来。这篇文章不是协议文档的复述而是我踩过坑之后梳理出的一套可以直接落地的实现思路适合刚开始接触5G LDPC、想在MATLAB里跑通一个完整物理层编码仿真的同学也适合那些已经在用通信工具箱、但想搞清楚底层到底发生了什么的人。1. 5G为什么把LDPC扶上位数据信道编码的选型逻辑1.1 从Turbo到LDPC一次迟来的替换4G时代的Turbo码统治了十几年到了5G NR标准制定时数据信道最终没有继续用Turbo而是选择LDPC作为PDSCH和PUSCH的信道编码方案。很多人以为这只是因为LDPC性能更好实际上更关键的原因是——LDPC的并行译码结构天然适配高吞吐需求。Turbo码的迭代译码本质上是串行的软输入软输出交织即便用并行MAP译码器扩展性也受限而LDPC的校验矩阵天生带并行度译码器可以用大量处理单元同时更新变量节点和校验节点。5G eMBB场景的峰值速率目标是20Gbps这个吞吐量对译码器的并行度和时钟频率要求极高。LDPC的准循环结构QC-LDPC让硬件实现可以按块进行并行更新这是Turbo很难做到的。另外LDPC的码率灵活性更好通过打孔和缩短可以很平滑地适配不同码率不像Turbo码那样需要大量交织器设计。1.2 5G NR里LDPC的职责边界LDPC在5G NR中并不是唯一的信道编码。物理层控制信道PDCCH、PUCCH用的是Polar码而数据信道PDSCH/PUSCH才用LDPC。这个分工在R15版本里就定死了。做MATLAB仿真时先别急着上来就写编解码函数想清楚你仿真的场景是数据信道还是控制信道很关键——这决定了你该用哪套协议参数。我见过不少人在PUCCH仿真里拿LDPC去凑结果误码率曲线怎么调都下不来大概率是编码方案选错了。5G LDPC的协议定义在3GPP TS 38.212第5.3.2节涉及基图选择、提升因子Zc的确定、位选择、交织等步骤。MATLAB通信工具箱里提供的nrLDPCEncode和nrLDPCDecode函数严格遵循这套流程但前提是你得先正确调用nrDLSCH或自己完成传输块到码块的切分。2. 协议里的关键参数基图、提升尺寸与速率匹配2.1 BG1/BG2的适用场景与矩阵结构5G标准定义了两套基图BG1和BG2。BG1面向大传输块和高码率场景支持的最大码块长度是8448比特码率上限接近8/9BG2面向小传输块和低码率场景最大码块长度是3840比特码率下限到1/5。选择规则在TS 38.212里有明确表格当传输块大小A大于292比特或者码率R大于1/4时选BG1否则选BG2。这个条件在MATLAB里判断就两行但非常容易忽略——选错基图直接导致编码结果对不上标准。从矩阵结构看BG1的基矩阵大小是46行68列其中前22列是信息列BG2是42行52列前10列是信息列。基矩阵里的每个元素有两种含义-1代表这个位置没有边连接非负整数代表循环移位值。所谓的QC-LDPC就是每个非-1元素被替换成一个Zc乘以Zc的单位阵的循环移位矩阵-1被替换成全零矩阵这样整个H矩阵就由Zc大小的循环子矩阵块拼接而成。2.2 提升因子Zc的确定过程Zc的取值并不是任意的5G标准限定了51个合法值形式为Zc a × 2^j其中a属于{2,3,5,7,9,11,13,15}j从0到7。选取的原则是用当前码块长度K除以BG的信息列数BG1是22BG2是10向上取整得到K然后从Z值表里选一个大于等于这个比值的最小Zc。更准确地说是先计算Kb对于BG1固定Kb22对BG2如果K大于640则Kb10否则需要根据K的取值查表确定Kb可能是6到9之间的值然后Zc取满足Kb×Zc≥K的最小合法值最终码块填充为KKb×Zc比特。这个步骤在MATLAB里直接用nrLDPCEncode会自动处理但如果自建H矩阵Zc的选取是第一步且决定后续所有矩阵尺寸。我建议写一个独立的函数把51个Z值存成一个常量数组然后用循环查找别用二分查找之类的花活——这个查找频率不算高简单直接最不容易出错。2.3 系统位打孔与速率匹配的工程含义5G LDPC自带一个有意思的设计编码完成后前2×Zc个系统比特会被打孔不直接送入信道。这听起来反直觉——明明是重要的系统位为什么不发原因是这2列在基图中参与核心校验节点连接打孔之后译码端可以把它们当作未知变量通过校验关系进行恢复等价于提供了一种隐式的速率匹配让高码率场景下的性能不会因为直接删掉校验位而崩塌。速率匹配本身在TS 38.212里定义了一套完整的比特选择、交织、循环缓冲区生成流程。MATLAB里对应的是nrRateMatchLDPC和nrRateRecoverLDPC。自己做完整链路仿真时一定要注意如果直接编码然后过AWGN不做速率匹配仿真出来的BLER曲线和标准场景完全对不上。因为标准流程里速率匹配输出的码率是实际的传输码率R而不是基图本身设计的码率。3. MATLAB环境准备与校验矩阵的生成3.1 用5G Toolbox还是自建矩阵两种路线我都走过先说结论想快速出结果验证算法直接用通信工具箱想深入理解协议细节或者做自定义改进比如换一种基矩阵、改译码调度就得自己生成H矩阵。用工具箱的好处是省心。nrLDPCEncode输入码块二进制位和基图编号、Zc值直接输出编码后的比特。nrLDPCDecode输入LLR通过名称-值对设定迭代次数和译码算法返回译码后的硬判决值。但工具箱是个黑盒一旦性能不达标你很难判断是编码的问题还是译码设置的问题。我自己是把两条路都打通了先用工具箱跑通基线再自建矩阵复现结果两者对得上之后才有底气去改算法细节。3.2 从基矩阵扩展完整H矩阵的实现套路5G基图的完整数据可以从MATLAB的通信工具箱里提取函数名是ldpcQuasiCyclicMatrix旧版本叫ldpcQuasiCyclicMatrix输入BG编号返回基矩阵。也可以从3GPP的Excel表格里手动维护一份但那个容易出错不推荐。核心扩展逻辑如下HG ldpcQuasiCyclicMatrix(bgNumber); % 返回基矩阵 Zc 确定好的提升因子; H zeros(size(HG,1)*Zc, size(HG,2)*Zc); % 预分配后面转稀疏 for r 1:size(HG,1) for c 1:size(HG,2) shift HG(r,c); if shift 0 % 生成Zc×Zc的单位阵循环右移shift位 block circshift(eye(Zc), shift, 2); H((r-1)Zc1:rZc, (c-1)Zc1:cZc) block; end end end H sparse(H); % 必须转稀疏这里有三个容易翻车的地方。第一MATLAB索引从1开始而协议里的移位值是从0开始计数的circshift的第二个参数需要小心核对。第二循环移位方向是右移还是左移不同资料里可能不一样以TS 38.212表5.3.2-2和ldpcQuasiCyclicMatrix输出的一致为准。第三H矩阵即使在中等Zc下也很大BG1在Zc384时矩阵维度是17664行乘以26112列非稀疏表示直接内存溢出——我机器上跑过一次内存16GB直接到顶所以稀疏存储不是可选项是必选项。3.3 大矩阵的稀疏存储与内存控制生成H矩阵时的预分配方式也值得说两句。用zeros预分配一个17664×26112的double矩阵瞬间就是3.7GB内存占用量。更好的做法是直接构建稀疏矩阵的三元组形式行号、列号、值最后统一调用sparse。MATLAB的sparse对这种分块循环矩阵支持得不错而且后续译码时的稀疏矩阵乘法效率很高。如果机器内存吃紧还有一个省钱的办法不显式生成完整H矩阵而是维护一份基矩阵加Zc译码时按块索引去访问子矩阵。这种方案对内存友好但需要把译码器里的消息传递逻辑从矩阵运算改成基于块索引的循环更新。代码复杂度会上升但对大Zc仿真非常管用。我后来做64路并行仿真时就是靠这个方式把内存峰值压下来的。4. 编码器实现基于QC结构的快速编码4.1 借助nrLDPCEncode快速起步工具箱的编码入口很简洁coded nrLDPCEncode(cw, bgNumber, Zc);这里的cw是信息比特向量长度K注意是填充后的长度bgNumber是1或2Zc是前面算好的提升因子。输出长度是BG的列数乘以Zc即BG1是68×ZcBG2是52×Zc。这个长度对应的是完整的LDPC码字长度还没做速率匹配。值得留意的是nrLDPCEncode要求的输入是K比特也就是填充之后的长度。如果块长K本身不是Kb×Zc需要先补零到K编码完再在速率匹配阶段处理。这个细节我最初没注意直接把K长度的比特丢进去报错还以为是工具箱版本问题。4.2 自实现编码器的核心思路自己写编码器时最直观的思路是把H矩阵高斯消元成系统形式[P|I]然后用生成矩阵G[I|P^T]直接做矩阵乘。但这个思路在5G LDPC场景下有个问题H矩阵非常大高斯消元过程会破坏准循环结构消出的P矩阵不再具备分块循环特性存储和计算开销都水涨船高。工程上更实际的做法是利用QC-LDPC的块结构做高效编码。基本思路是把系统位分成块利用校验矩阵中双对角结构BG1和BG2的后一部分校验位矩阵都是双对角形式分层递推求解校验位。这个方法的计算复杂度是O(n)级别远优于通用矩阵乘的O(n^2)。实现上需要把基矩阵按列分块成信息部分A和校验部分BB里又分核心校验位和扩展校验位然后逐块递推。这部分实现我强烈建议先画一个基矩阵的非零分布图再动手搞清楚了哪些列是系统列、哪些是核心校验列、哪些是扩展校验列代码写起来心里就有底。BG1和BG2的列结构其实很有规律前Kb列是系统位紧接着两列是核心校验再后面是扩展校验。5. 译码器实现从BP到min-sum的工程折中5.1 置信传播译码的数学雏形LDPC译码的核心是置信传播Belief PropagationBP也叫和积算法Sum-Product Algorithm。在Tanner图上信息在变量节点和校验节点之间来回传递。变量节点收集来自信道和各校验节点的信息更新后发给校验节点校验节点则基于外部信息的校验约束更新后发回变量节点。log域BP的校验节点更新公式为L(r_ji) 2 * atanh( ∏ tanh(L(q_ij)/2) )这个公式看起来简洁但在MATLAB里直接用会面临两个问题一是atanh的参数精度问题当tanh乘积趋近1时数值不稳定二是大量求tanh运算的开销非常大。性能仿真里这个更新在每轮迭代中要执行成千上万次纯BP跑一次中等规模的仿真慢得让人怀疑人生。5.2 归一化最小和译码的MATLAB实现工程上几乎都不会用纯BP而是用最小和Min-Sum及其改进版本。核心思想是把校验节点更新里的tanh/atanh替换成符号乘加最小值L(r_ji) α * (∏ sign(L(q_ij))) * min|L(q_ij)|其中α是归一化因子典型值在0.7到0.8之间。这个近似保留了BP最核心的最不可靠消息主导最小LLR逻辑同时把指数运算换成了比较运算在MATLAB里用min函数一行的量速度提升数倍性能损失通常不到0.1dB。我的译码器骨架是这样写的function [decbits, iters] ldpc_min_sum_decode(LLR_in, H, Zc, max_iter, alpha) [m, n] size(H); % 预处理校验矩阵的邻接列表 [row_idx, col_idx] find(H); % 初始化变量节点到校验节点的消息为信道LLR M_vc LLR_in; for iter 1:max_iter % 校验节点更新 (用邻接列表做分组归约) for r 1:m idx find(col_idx r); messages M_vc(idx); signs prod(sign(messages)); [min1, minpos] min(abs(messages)); % 计算每个出边的外信息 ... end % 变量节点更新 for v 1:n idx find(row_idx v); M_vc(idx) LLR_in(v) sum(M_cv(all_except_idx)); end % 后验硬判决与校验 ... end end这个朴素的实现能跑通但速度很一般。原因在于每次迭代都用find去扫描邻接这非常浪费。应提前把邻接关系缓存成cell数组每个变量节点一个列表、每个校验节点一个列表更新时直接遍历cell。我实测这个改动带来的提速比是最明显的能到5倍以上。5.3 迭代停止条件与LLR输入的处理迭代停止条件的设置直接关系到平均迭代次数也就直接关系到吞吐。一般做法是每轮迭代后做一次硬判决如果H × hard_decision 0模2提前终止。这个判据在误码率曲线瀑布区能显著降低平均迭代次数。LLR输入的一个隐蔽细节是打孔位和填充位的处理。打孔的系统位没有对应的信道观测量输入LLR应该置0表示等概率。填充位在编码前是0在译码端不是简单置0而是要把它们的先验信息设为一个很大的正值告诉译码器这个位确定是0。如果填充位也按0处理相当于给译码器提供了错误的先验信息BLER会凭空高出一截。这个坑我在自建矩阵译码时踩得很深后来逐一排查LLR设置才发现。6. 仿真链路搭建与性能验证6.1 完整链路示例与参数表我搭的仿真链路是信息比特生成 → 码块切分与填充 → LDPC编码 → 速率匹配 → QPSK调制 → AWGN信道 → 软解调LLR计算 → 速率恢复 → LDPC译码 → 填充分离与误块率统计。仿真参数如下参数取值基图BG1信息块长度K1408比特Zc64码率速率匹配后1/2调制方式QPSK信道AWGN译码算法归一化最小和α0.75最大迭代次数10仿真样本数每SNR点至少2000个码块这个参数组合在MATLAB里单机跑速度还能接受。如果想看BLER的瀑布区SNR范围可以设置在0dB到3dB区间步进0.25dB或0.5dB。6.2 结果分析性能阈值与迭代次数观测按上述参数归一化最小和在1/2码率、QPSK下的性能仿真BLER 1e-2对应的Eb/N0大约在1.6到1.8dB左右相比未编码QPSK有一个很明显的编码增益。和香农极限的差距大概在1dB内。如果换用更大的Zc和更长的码块差距还能进一步缩小。另一个值得观察的指标是平均迭代次数。在低SNR时大部分码块会耗尽10次迭代才能收敛进入高SNR区绝大多数码块在2到4次迭代内就满足了校验约束并提前终止。这种SNR越高、时延越低的特性是LDPC提前终止策略带来的直接红利在系统级仿真里做时延预算时很有价值。我建议仿真时除了画BER/BLER曲线也把平均迭代次数打印出来这个数据在给领导或同学汇报时特别能说明问题。MATLAB里直接在译码函数里累计每轮迭代次数即可代价几乎为零。7. 实践中的踩坑记录与调试链路7.1 矩阵下标偏移与扩展规则不对齐这是自建H矩阵时最容易踩的坑。5G标准表格里的移位值是0-basedMATLAB的索引是1-based这两个偏移量差一位效果就是整个校验矩阵的所有子块都错位了一个位置译码器完全无法收敛。更迷惑的是编码端如果也用了同样的错位逻辑编出来的码字居然还能过校验表面上看起来一切正常只有和标准编码器对拍才暴露问题。我现在的做法是每次生成H矩阵后先用nrLDPCEncode的编码结果和自己实现的编码器输出对比一次不一致就直接报警。这个自检步骤应该固化到仿真脚本里而不是每次手工检查。7.2 BLER/SNR性能不达标时的排查链路我把我踩过的性能问题归纳成一条排查链路按照出现频率排序先确认LLR输入没接反。QPSK软解调中实部虚部对应关系弄反是最常见的低级错误症状是性能曲线完全不对。再确认填充位LLR设对了。把填充位当普通位设成0LLR性能掉大约0.3dB不仔细看曲线甚至不容易察觉。检查速率匹配与恢复是否对称。速率匹配端做了比特选择恢复端就得放回对应的LLR位置放错了等于把信息打乱。检查最大迭代次数。迭代次数设太少比如3次瀑布区会右移明显设到10到20次左右性能趋于饱和没必要盲目加大。如果以上都没问题再考虑归一化因子α的影响。α在0.7到0.8之间有一个性能平台偏差太多会有0.1到0.2dB的劣化。7.3 一个值得优化的细节分层译码最后分享一个我后来做的优化思路。前面说的译码器每次迭代是先更新所有校验节点再更新所有变量节点这叫泛洪调度flooding。它的收敛速度比较慢需要更多迭代次数。5G实际硬件实现和MATLAB工具箱默认算法都支持分层调度layered scheduling也就是按校验矩阵的行块逐层更新每更新完一层立刻用新消息更新相关变量节点信息在层与层之间流动更快同样的迭代次数下能多收敛出0.2到0.3dB的增益或者说达到同样BLER所需的迭代次数能减少将近一半。在MATLAB里实现分层译码本质上就是把H矩阵按Zc行块切分成多个子层按照基矩阵的行顺序逐层处理。这个改动听起来不大但涉及消息索引的全面重写我前后调试了两周才稳定下来。作为过来人的建议是先在泛洪调度上把BLER曲线调对再动分层调优不要一上来就追求最优调度调试难度会指数级上升。说了这么多核心的东西就是这几件事想清楚BG1/BG2的适用边界搞定Zc和矩阵扩展编码可以用工具箱快速起步但一定要有自实现对照译码直接上归一化最小和最后用BLER曲线验证一切。这套链路跑通之后后面再接MIMO检测、信道估计、HARQ重传都是在同一个框架上增加模块不会再被编码这块卡住。本文还有配套的精品资源点击获取