简介:面向5G-NR物理层编码研究的MATLAB仿真资源,围绕LDPC编译码误码率仿真展开,译码算法采用OMS最小和偏置算法,码率设定为0.5。资源针对通信工程、电子信息和移动通信方向的师生及算法工程师,可帮助理解5G-NR标准中LDPC编译码流程,并快速搭建仿真链路评估误码率性能。
包体共14个文件,以MATLAB的m脚本和函数为主,辅以3个mat数据文件用于保存仿真中间结果或对比数据,另有1个mp4教程视频直观演示运行方法。整个压缩包仅8.86MB,轻量易用。目前已有269人学习/下载。
通过这套资源,读者既能获得可运行的LDPC编译码程序,又能结合教程视频理清OMS偏置算法的实现要点,可以直接运行主脚本复现不同信噪比下的误码率曲线,适合作为课题入门、课程设计或算法对比的参考资料。 做5G物理层算法仿真这几年,我经常遇到有人吐槽:LDPC编码调用一下函数库就完事,但一到译码就抓瞎,尤其想自己实现OMS(Offset Min-Sum,偏置最小和)译码器的时候,网上资料不是抄3GPP规范就是甩一个公式然后没了下文,最后自己做出来的误码率曲线怎么都对不上预期。这篇就用MATLAB把5G-NR-LDPC编译码误码率仿真完整走一遍,码率固定0.5,译码算法采用OMS,从编码结构、速率匹配、QPSK软解调、迭代译码到实测结果全部讲透,还配教学级代码片段。适合正在做通信系统课程设计、5G物理层算法评估或者刚接触LDPC内部机理的同学,也适合想搞懂OMS实现细节再往硬件方向转的工程师。
1. 5G-NR为什么选中LDPC:一次信道编码的换代
先交代一个背景。5G-NR的物理层数据信道PDSCH/PUSCH,在R15版本定稿时做了一个重要决定:信道编码从4G LTE的Turbo码全面切换成LDPC码。当时3GPP内部争论了很久,最终LDPC胜出的核心原因就一个字:快。5G的eMBB场景要求峰值吞吐率达到20Gbps级别,这是LTE完全不可比的,信道译码器不能在接收端变成瓶颈。
1.1 高吞吐场景下的Turbo码瓶颈
Turbo码的译码使用BCJR/MAP类算法,每次迭代都需要做前向、后向递推,而且这种递推是串行依赖的,一个状态的计算结果会直接影响下一个状态,天然不利于大规模并行。芯片上做高吞吐Turbo译码器,要堆很多硬件资源才能勉强达到几十Gbps。
LDPC完全不一样。它的校验矩阵是稀疏矩阵,译码过程被拆成许多相互独立的校验节点和变量节点更新,各个节点之间没有强时序依赖。这意味着在FPGA或ASIC上可以展开成大规模的并行计算结构,几十Gbps甚至上百Gbps的译码吞吐在工程上是可实现的。另外5G还在控制信道PDCCH上选了Polar码,数据信道用LDPC,这是标准里两套信道编码体系并存的原因。
1.2 这篇仿真要解决的具体问题
很多教材讲LDPC只停留在"稀疏校验矩阵+BP迭代译码"这个层面,但实际到了5G-NR,还要面对基矩阵选择、提升因子扩展、速率匹配、打孔位置LLR怎么处理等一系列工程问题。尤其是OMS这种偏置最小和译码算法,教材里基本不会展开,可它恰恰是硬件实现里非常讨喜的一个方案。
这篇仿真就是想解决三个问题:一是把5G-NR LDPC的码结构从基矩阵到实际校验矩阵彻底讲明白;二是给出OMS译码器可以直接照着写的核心逻辑;三是完整跑出一条BER曲线,让读者知道一个可用的编译码链路应该长什么样。仿真参数我选了标准里典型的BG1基矩阵、码率0.5,调制先用QPSK,因为它软解调最简单,可以把全部注意力集中在译码器本身上。
2. 码率0.5的码字结构:基矩阵、提升因子与速率匹配
很多人第一次看5G-NR LDPC标准会懵,因为标准里根本不给你一个现成的大尺寸H矩阵,而是给了一张很小的基矩阵表。这背后的设计思想是准循环LDPC,也就是QC-LDPC。
2.1 QC-LDPC:用基矩阵描述超大校验矩阵
NR里定义了两张基矩阵,称为BG1和BG2。
BG1尺寸是46行×68列,其中信息列数kb=22,适用于较大传输块和中高码率场景。BG2尺寸是42行×52列,其中信息列数kb=10,适用于较小传输块和低码率场景。
基矩阵里的每个元素不直接是0或1,而是代表了Zc×Zc的块矩阵:-1表示全零矩阵,0表示单位阵,正整数表示单位阵循环移位对应位数。这里的Zc叫做提升因子,5G标准里Zc不是随便取的,它必须来自标准规定的提升值集合。实际校验矩阵H的维度就是(M×Zc)行、(N×Zc)列。
信息比特长度K与基矩阵信息列数kb和提升因子Zc直接相关:
- K = kb × Zc
- 母码码长 N = nb × Zc,其中nb是基矩阵总列数
举个例子,我用BG1、Zc=96,那么一次编码处理的信息比特K=22×96=2112比特,母码码长N=68×96=6528比特。注意这个Zc=96在标准提升值集合里是合法的,所以这个配置很干净。
2.2 从母码率到0.5:速率匹配在做什么
BG1的母码率大约22/68≈0.32,BG2的母码率大约10/52≈0.19。也就是说,直接编码出来的母码码字码率低于0.5,要做速率匹配,通过打孔和比特选择把实际发送的码字码率提高到0.5。
5G-NR的速率匹配有一套标准规则,包含系统位打孔、校验位打孔和循环缓冲区比特选择。对于纯算法仿真,为了聚焦OMS译码器本身,我做一个合理的教学简化:保留全部系统比特,只在校验比特里做均匀打孔,把发送比特数E控制为2K。这样实际码率就是:
- R = K / E = 2112 / 4224 = 0.5
这个简化虽然和标准细节有差异,但BER曲线趋势和译码器行为是一致的。你在处理打孔位置时要特别注意一点:没有发送的比特,接收端没有信道观测值,对应LLR必须置0,表示完全不确定。如果这一步漏了,误码率曲线会变得很奇怪。
仿真参数可以先用这样一张表确定下来:
| 参数 | 取值 | 说明 |
|---|---|---|
| 基矩阵 | BG1 | 46×68,kb=22 |
| 提升因子Zc | 96 | 96在标准提升值集合内 |
| 信息比特数K | 2112 | 22×96 |
| 母码码长N | 6528 | 68×96 |
| 发送比特数E | 4224 | 2×K,码率0.5 |
| 调制方式 | QPSK | 每符号2比特 |
| 最大迭代次数 | 10 | 迭代次数后面会讲怎么调 |
| OMS偏置β | 0.5 | 典型值,后面会讲影响 |
3. OMS译码器原理:从BP到偏置最小和的工程取舍
LDPC译码本质上是在因子图上做消息传递,每个变量节点、每个校验节点来回交换软信息,直到整个码字满足所有校验方程。完整版的BP译码性能最好,但代价非常高。
3.1 BP译码和它难以落地的tanh运算
BP译码里,校验节点向变量节点传递消息时用到的公式是:
- r_ji = 2 * atanh(∏ tanh(q_ij / 2))
这个公式里有tanh、atanh,还有连乘。浮点计算一次两次没感觉,但在每一轮迭代里,每个校验节点每条边都要算一次,硬件实现要消耗大量的面积和功耗。对5G这种追求超高吞吐的系统来说,直接在芯片里铺BP所需的tanh运算单元,成本高得离谱。
3.2 Min-Sum、NMS和OMS:三条近似路线
既然tanh运算贵,工程上就想办法近似。Min-Sum算法的出发点是一个观察:当LLR绝对值变大时,tanh值很快趋近±1,一串数相乘的结果,符号由负数的奇偶个数决定,幅值则主要由绝对值最小的那个数主导。于是校验节点更新可以简化成:
- r_ji = ∏ sign(q_ij) × min|q_ij|
这个近似把tanh、atanh全省了,代价是Min-Sum会比BP差大概0.3到1dB,原因在于它系统性高估了校验消息的幅值。
为了补偿这个高估,出现两条改进路线:
- NMS(归一化最小和):乘一个小于1的缩放因子α,通常取0.75到0.8
- OMS(偏置最小和):减去一个偏置β,通常取0.5左右
OMS的校验节点更新公式为:
- r_ji = ∏ sign(q_ij) × max(min|q_ij| - β, 0)
三种方案放在一起对比,结论很清晰:
| 算法 | 核心操作 | 性能 | 硬件代价 |
|---|---|---|---|
| BP | tanh/atanh、乘法 | 最好 | 最高 |
| NMS | 乘法缩放 | 接近BP | OMS之上多了乘法器 |
| OMS | 减法、取绝对值、比较 | 接近NMS/BP | 最低 |
标题里说的"OMS最小和偏置算法",全称就是Offset Min-Sum,翻译过来是偏置最小和。Min-Sum是基础框架,OMS在这个框架上加了偏置补偿。它最吸引人的地方在于整个校验更新过程只需要比较、减法、取符号,一个乘法器都用不到,这对FPGA和ASIC实现来说是巨大的优势。
3.3 迭代译码的整体流程
OMS译码器逐轮迭代的流程,和BP是一致的:
- 初始化:把所有变量节点发给校验节点的消息v2c初始化为信道LLR
- 校验节点更新:对每个校验节点,收集相连变量节点的消息,计算符号积、最小值和次小值,然后按OMS公式生成c2v消息
- 变量节点更新:每个变量节点把信道LLR和所有c2v相加得到后验LLR,然后减去对应边的c2v得到新v2c
- 硬判决:根据后验LLR的符号判断比特值
- 早停校验:如果当前硬判决满足所有校验方程,说明收敛到了合法码字,提前退出迭代
这里有个关键点:如果开启了早停,平均迭代次数会明显下降,但BER曲线不变。仿真时可以先用固定迭代次数比如10次,确认性能稳定后再加早停。
4. MATLAB仿真链路搭建:编码、调制与LLR解调
链路结构不复杂:随机比特生成、LDPC编码、QPSK调制、AWGN信道、QPSK软解调得到LLR、OMS译码、误码统计。真正决定仿真能否跑通、曲线是否合理的是参数对齐和细节处理。
4.1 仿真参数和链路结构
我在MATLAB里把仿真参数集中放在一个脚本开头,方便反复改。K=2112这个块长不算小,如果直接用教学版OMS译码器做全仿真,速度会很慢。我的建议是:第一遍先用小配置跑通,比如BG2、Zc=24,这样K=240、N=1248,教学版代码几分钟能出一轮结果;确认逻辑无误后再上BG1、Zc=96的大配置。
LDPC编码有两种实现路径。有5G Toolbox环境的话,可以调用工具箱里的LDPC编码函数,具体接口以对应版本文档为准,它内部已经处理好了基矩阵扩展和速率匹配。没有工具箱也不影响,自己按QC-LDPC扩展规则把H矩阵构造出来,然后利用5G基矩阵近似下三角的结构特点做递推编码。这里不展开完整编码函数,因为篇幅太长,但H矩阵构造必须要写对:基矩阵里-1对应Zc×Zc全零矩阵,非负整数对应单位阵循环移位后的矩阵,这个步骤错了后面全错。
4.2 QPSK软解调与Eb/N0换算
QPSK可以看成两路独立的BPSK,I路和Q路各传一个比特。我用0/1比特到星座点的映射为:00对应(+1,+1),01对应(+1,-1),10对应(-1,+1),11对应(-1,-1)。收到带噪符号后,I路和Q路的LLR可以分别计算:
- LLR = 2 × y / noise_var
其中noise_var是每维度噪声方差,实际代码里信号功率归一化后,可以简单用noise_var = 10^(-snr_db / 10)来计算,前提是所有发送符号功率归一为1。
Eb/N0和信噪比SNR的换算关系是:
- SNR_dB = EbN0_dB + 10×log10(R) + 10×log10(m)
这里R是码率0.5,m是每符号比特数,QPSK时m=2。所以当EbN0=2dB时,SNR大约是2 + (-3.01) + 3.01 = 2dB。这个换算经常有人算错,一错把所有曲线都平移了,排查起来很痛苦。
5. OMS译码器核心实现与仿真主循环
这一段给出OMS译码器的教学版代码。教学版的意思是逻辑清晰、方便和理论公式对照,效率不是最优。真正要跑大参数,我建议在这个基础上把邻居关系预存成cell数组、向量化校验节点更新,或者改写成MEX。
5.1 教学版OMS译码函数
function [hat_bits, iter_used] = oms_decode(ch_llr, H, max_iter, beta) % OMS译码器 教学版 % ch_llr : 1xN 信道对数似然比, 长度等于母码码长N % H : MxN 稀疏校验矩阵 % max_iter: 最大迭代次数 % beta : OMS偏置量 [M, N] = size(H); v2c = zeros(M, N); % 变量节点发给校验节点的消息矩阵 c2v = zeros(M, N); % 校验节点发给变量节点的消息矩阵 [row_idx, col_idx] = find(H); % 初始化: v2c 等于信道LLR for k = 1:length(row_idx) v2c(row_idx(k), col_idx(k)) = ch_llr(col_idx(k)); end for it = 1:max_iter % ---- 校验节点更新 ---- for m = 1:M nbr = col_idx(row_idx == m); % 相邻变量节点 msg = v2c(m, nbr); sgn = prod(sign(msg)); % 符号积 [amp, order] = sort(abs(msg)); % 按幅值排序 min1 = amp(1); min2 = amp(2); for k = 1:length(nbr) n = nbr(k); if abs(msg(k)) == min1 min_ex = min2; % 排除自身后的最小值 else min_ex = min1; end c2v(m, n) = sgn * sign(msg(k)) * max(min_ex - beta, 0); end end % ---- 变量节点更新 ---- for n = 1:N nbr = row_idx(col_idx == n); % 相邻校验节点 post = ch_llr(n) + sum(c2v(nbr, n)); for k = 1:length(nbr) m = nbr(k); v2c(m, n) = post - c2v(m, n); end end % ---- 硬判决与早停 ---- post_all = ch_llr + sum(c2v, 1); hat_bits = double(post_all >= 0); % 0/1比特映射 if mod(H * hat_bits(:), 2) == 0 iter_used = it; return; end end iter_used = max_iter; end这段代码里最值得揣摩的是校验节点更新中最小值和次小值的处理。对每个校验节点,我只需要找到所有输入消息里幅值最小的两个,然后给每条边回消息时,排除自己那个输入,用剩下输入里的最小值减去β,再和0比较。这个逻辑是OMS实现里最容易写错的地方,很多人直接用全体最小值而没有排除当前边,导致性能严重下降。
5.2 主仿真循环与误码统计
主循环的框架可以这样组织,我直接说明关键步骤而不是贴完整代码,避免掩盖重点:
对每个EbN0点,先把信号功率归一化为1,计算对应noise_var;然后进入帧循环。在每一帧里,生成K个随机比特,做LDPC编码得到N比特码字,经过速率匹配选出E个发送比特,QPSK调制后过AWGN信道,软解调得到E个LLR。注意这E个LLR只是发送位置的,译码器需要N个LLR,所以要把打孔位置的LLR补成0,还原出完整的LLR序列。之后调用oms_decode,得到估计比特后,只比较信息位部分,统计错误比特数和传输总比特数。
统计终止条件我习惯这样写:每个SNR点至少累计到100个错误比特,同时最多跑200帧。如果某点跑到200帧错误比特还不够100个,就接受当前结果并继续下一个SNR点。这样做的好处是低误码率区域曲线不抖动,每个点都有足够统计量支撑。
6. 结果分析、参数影响与踩坑清单
链路搭完,仿真跑通后,出来的BER曲线会非常直观地反映OMS译码器的行为。这里说两个我一定要强调的结果规律。
6.1 误码率曲线与OMS/MS/BP对比
用BG1、Zc=96、K=2112、码率0.5、QPSK这套配置,BER曲线在EbN0约2dB附近可以进入1e-4以下。OMS译码器在低信噪比区域和BP的差距很小,在高信噪比区域比Min-Sum大约好0.3dB左右,比BP差大约0.1到0.2dB。这个性能差距换来的是硬件实现复杂度大幅降低,非常划算。
如果同时实现MS译码器做对比,你会在曲线上看到一个很明显的特点:MS在瀑布区下降得慢,OMS的瀑布区更陡。因为OMS通过减去偏置,实际上纠正了Min-Sum过度自信的问题,让校验节点传递出去的软信息更接近BP的统计特性。
6.2 偏置β与迭代次数怎么调
β是OMS里最重要的超参数。拿0.5码率、QPSK、10次迭代这个配置来看:
- β=0.3:补偿不足,性能接近MS
- β=0.5:性能最好
- β=0.7:补偿过头,增益下降,高信噪比下可能出现平台期
偏置的取值和信噪比范围有关系,通常取0.5附近不会出错。如果你做的是定点仿真,β需要和量化精度一起调,因为定点截断本身就会损失部分软信息精度。
迭代次数方面,5次迭代BER还明显偏高,10次基本够用,20次和10次的曲线几乎重合。这说明OMS在这个配置下大约10轮迭代就能收敛。配合早停机制后,平均迭代次数通常不到最大迭代次数的一半,这是硬件设计里非常关心的功耗指标。
6.3 容易踩的坑
最后把我在调试中踩过的坑集中列出来,每一条都能让仿真结果从"看起来离谱"变成"符合预期":
- H矩阵扩展方向别搞反。基矩阵里的正整数偏移量代表循环移位方向,扩展代码写错了,整个矩阵就错了,但译码器表面上还在运行,很难一眼发现。
- 打孔位置的LLR必须置0。我见过有人直接用随机小值代替,结果相当于给译码器喂了假信息,FER异常高。
- 早停判断用mod(H * hat_bits, 2) == 0,不要直接判断H * hat_bits == 0。后者在GF(2)上的语义是错的。
- LLR符号和调制映射必须统一。如果调制时1映射到-1,那么LLR大于0应该判为1,算法里是一致的,但写的时候很容易在某个环节多乘一个负号,导致BER永远停在0.5附近。
- 统计帧数要够。每个SNR点至少100个错误比特再停止,否则曲线抖动到没法看。
- 教学版OMS代码跑大K很慢。建议先用BG2小Zc确认逻辑,再切到大参数;有条件就把邻居表预存成cell数组,并把校验节点更新改写成按行向量化操作。
最后分享一点个人经验。我最早做5G-NR LDPC仿真时,总想一步到位跑BG1大块长,结果教学版OMS一帧要算好久,低误码率点根本跑不完。后来我改成小配置把每一条曲线都跑通,理解清楚OMS的β行为和早停机制,再上大参数做精细仿真,效率高了不止一个量级。这篇给的教学版代码虽然慢,但每一步都能对上公式;等你能把校验节点更新里排除自身的最小值逻辑闭着眼睛写出来,OMS译码器在工程实现上的优势你就真正体会到了。
本文还有配套的精品资源,点击获取