5G-NR LDPC译码仿真实战:OMS偏置最小和算法MATLAB实现 📅 发布时间:2026/9/9 1:43:27 👁 浏览次数: 简介MATLAB实现的5G-NR LDPC编译码误码率仿真完整方案适合通信工程专业学生、科研人员以及从事信道编码算法开发的工程师。仿真采用OMS最小和偏置译码算法码率设为0.5覆盖编码、调制、AWGN信道、译码及误码率统计全流程可直接用于5G NR物理层链路级性能评估。压缩包共14个文件包含10个M函数/脚本源码、3个MAT数据文件和1个MP4操作教程整体大小约8.86MB。源码注释清晰、模块化组织便于二次开发MAT文件存放仿真中间变量与结果可快速复现数据视频教程完整演示从参数配置到结果分析的运行过程并解释OMS算法中最小值和偏置项的作用。已有269人学习下载非常适合希望快速建立LDPC仿真平台、研究迭代译码收敛特性或深入理解OMS算法实现细节的读者。 做了小半年的5G-NR-LDPC译码器仿真最近终于把基于OMS偏置最小和算法的误码率平台跑通了。这篇东西不打算写成说明书就把它当成一次实战记录来写——从5G-NR LDPC的编码结构怎么理解到OMS译码算法的数学动机再到一套可直接跑的MATLAB仿真链路设计我都会按自己的思路捋一遍。如果你正准备做LDPC相关的误码率仿真或者想弄清楚偏置最小和算法相比传统BP、最小和算法到底改了什么这篇内容应该能帮你省掉不少查资料的功夫。要提前说明的是我的仿真场景固定为码率0.5调制方式选QPSK起步信道是AWGN。这样设置不是因为别的而是这个配置刚好是5G控制信道和部分业务信道的典型工作点也是验证算法性能最直观的起点。1. 5G-NR LDPC编码方案拆解基矩阵、扩展因子与码率0.5的选型1.1 准循环结构为什么5G标准选择了QC-LDPC5G-NR选用的LDPC码全称是准循环LDPC码QC-LDPC。所谓“准循环”指的是校验矩阵H不是随便散列的稀疏矩阵而是由一个个子块拼接而成每个子块是Z×Z大小的全零矩阵或循环移位单位矩阵。这个Z就是扩展因子也叫lifting size。H矩阵有了这种分块结构带来的第一个好处是编译码器可以用移位寄存器实现不需要存储巨大的稀疏矩阵。第二个好处是同一套基矩阵可以通过改变Z适配不同的码块长度从而覆盖5G从几十比特到几千比特的传输块范围。如果你接触过Wi-Fi的LDPC或者DVB-S2的LDPC会发现它们也是这类思路只是5G在基矩阵设计和速率匹配上做得更细致。从硬件角度看QC-LDPC的校验节点和变量节点更新天然就是并行的每个Z块内的运算互不依赖这对ASIC和FPGA实现极其友好。所以做仿真时我建议把你的H矩阵也按Z块来组织别把整个大矩阵摊平了处理后续无论是性能分析还是硬件映射都会省力很多。1.2 BG1和BG2的选择逻辑与码率0.5的关系5G-NR标准里定义了两套基矩阵BG1和BG2。BG1是46行×68列信息列数为22最大支持的码块长度可达8448比特适合大码块和高码率场景BG2是42行×52列信息列数只有10最大支持3840比特适合小码块和低码率场景。那么码率0.5该选哪个这里有个容易误解的地方。很多人一看0.5低于BG2的2/3最高码率就直接选BG2但如果你的信息位长度比较大比如超过3840比特BG2根本装不下。通常我这么判断信息位长度K在3840以内码率0.5优先考虑BG2如果K很大比如接近8000比特就选BG1然后通过速率匹配把码率降到0.5。不过这样一来校验位里会有不少被打孔丢失的比特译码时对这些位置的LLR要做特殊处理。我的仿真平台用的是简化方案——固定信息位长度在BG2能覆盖的范围K1056码率0.5所以基矩阵选BG2是合理的。选择这个参数组合的另一个原因是在Z96时BG2扩展出的码长大约为4992比特码长适中跑蒙特卡洛仿真时单帧仿真时间不会太长统计误码率的迭代次数也可以控制。1.3 打孔比特与系统比特映射新手最容易翻车的地方5G-NR LDPC编码第一步是确认打孔比特。基矩阵中前两列也就是索引为0和1的列是打孔列实际传输时这两列的比特不发送。这样设计的原因比较底层主要是为了保证任意码率下都能从第3列开始实现系统码结构同时避免低码率时出现校验矩阵的零空间问题。在仿真时最常见的错误是编码后直接把全部比特送调制接收端也给所有比特计算LLR。这样做的直接后果是解码性能异常差甚至比未编码还差。正确做法是——发射端丢掉打孔比特再调制映射接收端译码初始化时把被打孔比特对应的LLR设置为0也就是等概率信息让译码器通过迭代约束关系把这些比特逐渐恢复出来。还有一个小细节5G-NR编码后有一个填充比特的概念如果信息位长度不满足K 22·Z或K 10·Z的倍数关系需要填充哑元比特。哑元比特在译码时已知LLR要设置为一个很大的置信值。这一点很容易被忽略导致误码率地板效应。2. OMS偏置最小和译码算法从BP算法的复杂度痛点说起2.1 为什么必须从置信传播退到最小和置信传播BP译码在理论上是LDPC码的最优迭代译码方式但它的校验节点更新涉及tanh运算和atanh运算每个非零元素都要做一次双曲函数变换。这在浮点仿真里还好但在定点实现或者硬件流水线里开销非常大。更麻烦的是tanh域运算对小信号的数值精度极其敏感量化稍微差一点就可能导致性能雪崩。所以工业界几乎不会直接上纯BP实现而是用最小和Min-SumMS类算法替代。Min-Sum的核心思想很粗暴——把校验节点更新里的tanh/atanh链路的非线性操作直接换成取最小绝对值。这样做的理论依据是多个独立随机变量的联合可靠度主要由最小可靠度决定。从信息论视角看这个近似是有道理的但代价是Min-Sum的输出消息幅度总是大于真实BP的消息幅度也就是“过估计”最终表现为误码率性能相比BP有0.3到0.5dB的损失。为了解决过估计问题有两种常见的修正思路一种是对校验节点消息整体乘一个小于1的归一化因子也就是Normalized Min-SumNMS另一种是减去一个正的偏置常数这就是我们今天的主角OMSOffset Min-Sum。OMS的修正方式更粗暴但好处是硬件实现时只需要做一次减法不需要乘法器这在资源受限的芯片设计里非常讨喜。2.2 OMS的偏置项到底在修正什么OMS的校验节点更新公式很简洁[ L_{i \rightarrow j} \left( \prod_{j \in N(i) \setminus j} \text{sign}(L_{j \rightarrow i}) \right) \times \max\left( \min_{j \in N(i) \setminus j} |L_{j \rightarrow i}| - \beta, 0 \right) ]其中β就是偏置项offset。观察这个式子当最小可靠度绝对值小于等于β时输出被强制截断为0相当于这条边在本次迭代中不传递有效信息当最小可靠度大于β时幅度被减去β。β的取值直接影响译码性能。理论上最优β值取决于码率、码长、信道信噪比以及量化方案并没有闭式解但工程上有一些经验范围。在浮点AWGN信道、码率0.5、QPSK调制下β取0.5到0.75之间通常比较合适。我的仿真里默认β0.5并且在代码里把β设成可变参数方便你跑不同信噪比点的时候观察最优值是否有偏移。实际跑下来我发现一个容易被忽略的现象OMS的截断特性相当于给消息传递引入了一个“死区”在低信噪比区域这个死区反而能起到抑制噪声消息传播的作用所以OMS在低信噪比下的性能有时会比NMS更好。而到了高信噪比区域死区会导致部分本该传递的修正消息被截断可能出现错误平层。做工程选型时要注意这个特性。2.3 OMS和NMS怎么选如果你的目标是硬件实现我强烈建议OMS。理由很简单NMS需要在校验节点输出上乘一个α因子这个乘法在定点实现中需要额外一个乘法器或者移位加法器而且α的量化精度会影响性能OMS只是减法偏置参数β可以直接编码到比较器和加法器逻辑中。如果做纯浮点仿真且追求最佳性能你可以把两者都跑一遍取更优者。不过我自己的实验数据显示在5G-NR BG2、码率0.5、2048码长这个档位上OMS和NMS的差距通常在0.1dB以内考虑到实现成本OMS的性价比更高。3. MATLAB仿真链路完整搭建从校验矩阵到误码率曲线3.1 基矩阵获取与QC-LDPC扩展要复现这套仿真你首先需要BG2的基矩阵。如果你装了5G Toolbox有现成函数nr5gDLSCHDemux这类封装可以间接拿到但更直接的方式是查3GPP TS 38.212表5.3.2-2手动把BG2的42×52矩阵录入MATLAB。这个过程比较繁琐但一次搞定后就可以复用。拿到基矩阵后扩展是标准操作对于基矩阵中的每个元素b(i,j)如果b(i,j) -1则对应一个Z×Z全零块否则构造一个Z×Z单位矩阵每行循环右移b(i,j)位。这里有个MATLAB的小技巧可以直接用circshift(eye(Z), shift, 2)来生成循环移位矩阵然后用cell2mat把所有的块拼接成完整的H矩阵。Z96时H矩阵的尺寸是4032×4992虽然不小但在MATLAB里存储和运算都还扛得住。需要特别提醒的是一定要用logical类型存储H矩阵或者转成稀疏矩阵否则普通double类型的H矩阵做索引运算会消耗大量内存。我一开始没注意仿真跑到一半内存直接爆了。3.2 编码器实现从标准格式到可直接运行的代码5G-NR的编码并不是简单地把信息位乘以生成矩阵更准确的描述是信息位对应基矩阵中的系统列填充比特和CRC比特都在系统列里然后根据校验矩阵的后几列递推校验位。手工实现递推编码最容易出错我建议直接用MATLAB通信工具箱的ldpcEncoderConfig配合ldpcEncode来做或者用nrLDPCEncode5G Toolbox。如果你没有工具箱也可以自己用高斯消元法求生成矩阵核心代码如下function G getGenMatrix(H) [M, N] size(H); % 高斯消元将H化为 [P I] 形式再求G [I P] % 注意列交换记录编码后要还原顺序 end但高斯消元法的问题在于生成的G矩阵是稠密的对4032×4992的码来说G会巨大无比仿真一次编码就需要GB级内存。所以我最终推荐的做法是走标准化的nrLDPCEncode接口或者自己实现基于基矩阵的校验位递推。对BG2来说校验位是可以通过基矩阵的分块结构高效算出来的比求全尺寸G矩阵靠谱得多。另外编码时信息位填充和打孔的顺序要对。我踩过一次把打孔列信息直接当普通比特参与编码结果译码怎么迭代性能都起不来。后来查标准才发现打孔列在编码阶段本身就是要参与运算的只是编码完成后不发送。3.3 QPSK调制与AWGN信道的Eb/N0换算调制部分选QPSK。QPSK映射方式为比特对(0,0)映射到(1j)/√2其余依次按格雷映射。采用格雷映射的好处是相邻符号只有1比特差异解调的LLR近似误差小。信道是AWGN关键是噪声方差的计算。这里有一个高频翻车点误码率曲线的横坐标通常是Eb/N0每比特能量与噪声功率谱密度比但仿真时所有计算都发生在符号域。二者转换公式是[ \frac{E_s}{N_0} \frac{E_b}{N_0} 10\log_{10}(R \times m) ]其中R是编码码率0.5m是每符号比特数QPSK为2。于是Es/N0 Eb/N0 0dB。换句话说在码率0.5、QPSK下Es/N0和Eb/N0数值相等但不要因此觉得这个换算无所谓换成16QAM或者码率3/4换算至少差好几dB。噪声方差可以直接用sigma2 1 / (2 * 10^(EsN0/10))计算前提是信号平均功率归一化为1。注意这里的N0是单边功率谱密度噪声总功率为N0/2对应每个实部的方差所以总噪声方差就是N0/2乘以2等于N0。信号平均功率为1时接收符号的信噪比就是Es/N0。3.4 OMS译码器的MATLAB核心实现这里给出OMS译码函数的关键部分。为了效率我用消息矩阵而不是边列表来组织消息传递。变量节点消息矩阵V2C、校验节点消息矩阵C2V都是与H矩阵同尺寸的矩阵只在H非零位置有意义。function [decBits, iter, success] omsDecoder(llrIn, H, Z, beta, maxIter) [M, N] size(H); c2v zeros(M, N); v2c repmat(llrIn(:), M, 1); v2c(H 0) 0; % 非连接边消息置零 c2v(H 0) 0; % 预先计算H非零位置的索引 [rowIdx, colIdx] find(H); for it 1:maxIter % 变量节点更新: v2c llr sum(c2v) - c2v对应边 for j 1:N connected find(H(:, j)); vSum llrIn(j) sum(c2v(connected, j), 1); for i connected v2c(i, j) vSum - c2v(i, j); end end % 校验节点更新OMS核心 for i 1:M connected find(H(i, :)); for j connected others setdiff(connected, j); if isempty(others) c2v(i, j) 0; else minAbs min(abs(v2c(i, others))); c2v(i, j) prod(sign(v2c(i, others))) * max(minAbs - beta, 0); end end end % 判决 llrTotal llrIn sum(c2v, 1); decBits double(llrTotal 0); if mod(H * decBits(:), 2) 0 success true; iter it; return; end end success false; iter maxIter; end这段代码的效率不算最优因为每个节点更新都用了find循环但对教学和中等码长仿真足够了。如果你要跑大批量蒙特卡洛建议改成行循环内用矩阵操作避免双重for能快出好几倍。另外一个对性能影响极大的点是译码器的调度方式。上面的代码是flooding调度也就是所有变量节点并行更新、所有校验节点并行更新。5G标准里的LDPC更适合分层调度layered scheduling可以加速收敛一半左右的迭代次数。但OMS和分层调度结合时偏置参数β可能要稍微调大一点因为分层更新相当于更频繁地使用新信息消息的过估计程度会有变化。3.5 误码率统计怎么才算可信统计误码率最基本的准则是保证每个信噪比点有足够多的错误事件。我一般设定至少统计到100个错误帧同时设置一个最大帧数上限比如5000帧来防止高信噪比点耗时过长。算法如下每个Eb/N0点循环发送随机信息位编码、调制、加噪、译码。统计错误比特数和总传输比特数两者相除得到该点的误码率BEL。同时统计错误帧数如果错误帧数达到100则提前结束该点的仿真。如果码字是系统码注意统计误码率时应该只统计信息比特的错误不统计校验比特。关于误码率和误信率BLER的关系我想多说一句。有的资料把“误信率”理解为单个比特的错误概率这容易和误码率混淆。在信道编码语境下误信率更常用的是误帧率或误块率也就是一个码字传输完毕后至少存在1比特错误的概率。误码率反映的是平均比特级质量而误帧率直接反映系统是否丢包二者之间有如下关系[ \text{BLER} 1 - (1 - \text{BER})^{K} ]这只是近似实际由于错误比特不是独立均匀分布这个等式并不严格但在粗略估算时好用。跑仿真时两个量都可以统计出来曲线一起画能更全面反映系统性能。4. 仿真结果分析与避坑经验4.1 不同迭代次数的性能差异我跑了8次、16次、32次迭代三个配置。8次迭代时误码率在约1.5dB处出现明显平台期这说明迭代不充分16次迭代性能基本收敛32次迭代相比16次几乎没有增益。所以对BG2、码率0.5这个配置最大迭代次数设置在16左右足够不必盲目加大。如果你的目标是做复杂度对比可以在结果里记录平均迭代次数OMS因为偏置截断的关系平均迭代次数会比MS稍多一点点但每轮迭代的运算量更小整体资源开销依然有优势。4.2 偏置参数β对曲线的影响β0时OMS退化成普通MS性能最差β越大校验节点消息的修正越激进。我实测下来β0.5和β0.75都能正常工作但β0.75在低信噪比段会略微牺牲性能。这背后的逻辑是低信噪比时消息幅度整体偏小β过大容易把大量本应传递的消息截成0导致信息丢失。在量化仿真里β的选择更关键。比如用6比特量化LLR动态范围大约在-4到4之间此时最优β往往在0.25到0.5这个区间。做定点实现时不要直接沿用浮点β值要重新扫描一遍。4.3 仿真中遇到的典型问题与排查方法我在搭建这套链路时遇到不少问题挑几个最典型的说一下。LLR符号反了导致性能完全不可用。这是最常见的低级错误。QPSK软解调得到的LLR习惯上我会定义LLR 0表示比特为0的概率更大。如果你刚好定义反了译码器会把所有判决结果取反误码率约为1。排查方法很简单——先用无编码BPSK或QPSK仿真对比理论误码率LLR正确的话仿真曲线应该紧贴理论曲线。打孔列LLR没有置0导致低信噪比崩溃。这个前面提到过打孔比特在接收端没有对应观测LLR必须初始化为0否则等于给译码器注入错误先验信息。有次我把打孔列LLR随机初始化了结果中低信噪比段的性能差了将近1dB排查了很久才发现问题。基矩阵列置换导致编码译码不对齐。如果你用外部工具获取基矩阵一定要确保编码时用的H矩阵和译码时用的H矩阵完全一致。QC-LDPC的行列置换非常容易藏bug我的建议是在仿真开始前做一个校验随机生成一批信息位编码后无噪声直接译码要求成功解码概率为100%。这一步能过滤掉绝大多数矩阵维度或索引问题。运行时间过长。MATLAB纯循环实现OMS译码在码长4000的情况下一个信噪比点跑5000帧可能要几十分钟。解决办法是利用parfor对Eb/N0点做并行循环在函数内部把符号函数product和min操作向量化或者降低统计错误帧数为50帧先行验证趋势最终结果再用100帧跑一遍。5. 仿真结果怎么进一步扩展跑通OMS之后我建议你下一步在三个方向上做改进。第一个方向是换成NMS算法做对比代码改动非常小把校验节点里的减法改成乘法就行。这样你手上就同时有了两套常用低复杂度算法的性能对比数据写报告或者做软硬件协同设计时会更从容。第二个方向是加量化。MATLAB里可以用quantizer对象模拟定点LLR量化观察OMS在4比特、6比特量化下的性能损失。这一步对硬件实现极其重要很多软件仿真性能优秀的算法在定点化之后会现原形。第三个方向是把AWGN信道换成衰落信道。5G实际场景里瑞利衰落信道更常见你需要把调制、解调和信道估计模块都替换掉误码率的绝对数值会变差但OMS与BP之间的相对性能差距基本保持稳定。你会发现OMS在衰落信道下依然是很稳的选择。在我看来LDPC译码算法的研究核心从来都不是公式推导多精彩而是工程实现多可靠。OMS这个算法之所以能在5G接收机方案中占据一席之地靠的就是“用减法修正了乘法器都省了”这种极致的工程思维。这一点在你自己动手实现之后体会会更深。本文还有配套的精品资源点击获取