简介:这份资源面向通信工程、信号处理方向的学习者与开发者,聚焦5G NR标准下LDPC码的MATLAB实现,帮助读者理解并复现从校验矩阵构造到编解码的完整链路。压缩包共9个文件,以7个m脚本和2个mat数据文件为主,脚本承担基图选择、提升尺寸计算、信息列处理、打孔与仿真等核心逻辑,mat文件则保存协议对应的校验矩阵数据,整体约20KB,体量轻便、便于快速上手。内容围绕3GPP规范中的编码参数、H矩阵生成方法以及基于消息传递的迭代解码展开,读者可据此搭建可运行的仿真流程,观察不同码率与码族下的纠错表现,并在此基础上调整迭代次数或优化算法以适应实时性需求。目前已有1361人学习下载,适合希望深入掌握5G LDPC编码细节、需要可复用MATLAB参考实现的中高级读者。
1. 5G LDPC 码的 MATLAB 实现:从标准到可跑通的链路级仿真
做 5G 物理层的人迟早会撞上 LDPC。基站侧要评估吞吐,终端侧要验证译码时延,做算法的要对比不同基图(Base Graph)在短码下的纠错能力——这些场景都绕不开一个能跑、能改、能对齐标准的 MATLAB 实现。标题里的 “5G LDPC 标准下 LDPC 码的 MATLAB 实现”,说的就是把 3GPP 38.212 里那套准循环 LDPC(QC-LDPC)编码链路,用 MATLAB 从基图、提升因子、循环移位系数一路搭到编码输出,再配上译码做误码率验证。它解决的不是“LDPC 是什么”,而是“标准里那张表怎么变成能跑的矩阵、提升因子怎么选、编码结果怎么和协议对齐”。适合已经懂通信原理、手上有 MATLAB、需要快速搭链路级仿真或做算法对比的工程师,新手照着步骤也能把最小链路跑起来。
2. 5G LDPC 的基图与提升因子:标准表怎么读、矩阵怎么生成
2.1 两张基图 BG1 与 BG2 的适用边界
3GPP 给 5G LDPC 定义了两张基图:BG1 和 BG2。BG1 的码率偏高、信息列多,适合大块数据传输;BG2 码率低、信息列少,适合控制信息和短包。选错基图,后面提升因子和码率匹配全乱。常见做法是按传输块大小和码率来判:码率 R > 0.25 且信息比特数较大时用 BG1,否则用 BG2。实际工程里更稳的判据是看 38.212 里的表格,按 A(信息比特数)和 R 查表确定 BG,不要凭感觉。
BG1 是 46 列、BG2 是 42 列,前 22 列(BG2 是前 10 列)对应信息位,后面是校验位。每张基图里非零元素的位置和循环移位值 V 都写在标准表里。MATLAB 实现的第一步,就是把这些表变成稀疏矩阵的索引和值。
2.2 提升因子 Zc 的集合与选取规则
提升因子 Zc 决定基图每个元素展开成多大的循环置换矩阵。5G 的 Zc 集合是分组的,每组有一个系数 a 和一组偏移,Zc = a × 2^j,j 取 0 到 7。标准里列了 8 组共 51 个合法 Zc 值。选 Zc 的逻辑是:先根据码块长度和基图列数算出需要的 Zc 范围,再在合法集合里选最接近且不小于需求的值。选完 Zc 后,循环移位值要按 Zc 取模,这一步很容易翻车——标准表里的 V 是针对最大 Zc 给的,实际用的时候要做 V mod Zc。
% 5G LDPC 提升因子集合(按标准分组,这里给一组示例) % a 为组系数,j 范围 0~7,Zc = a * 2^j zc_groups = [2, 3, 5, 7, 9, 11, 13, 15]; zc_set = []; for a = zc_groups for j = 0:7 zc_set(end+1) = a * 2^j; end end zc_set = unique(zc_set); zc_set = zc_set(zc_set <= 384); % 标准上限 % 根据目标码块长度选 Zc K_target = 4000; % 目标信息比特数 bg_cols_info = 22; % BG1 信息列数 Zc_need = ceil(K_target / bg_cols_info); Zc = min(zc_set(zc_set >= Zc_need)); fprintf('选定 Zc = %d\n', Zc);这段代码先构造合法 Zc 集合,再按信息比特数反推最小可用 Zc。参数说明:zc_groups是标准里的组系数,j上限 7 对应每组 8 个值,Zc_need是理论最小值,实际选zc_set里第一个不小于它的值。注意标准里 Zc 最大到 384,超过这个范围的码块要走码块分割,不能硬选。
2.3 从基图到奇偶校验矩阵 H 的生成
有了 BG 和 Zc,下一步是把基图的非零元素展开成 H 矩阵。H 的维度是 (mb × Zc) × (nb × Zc),其中 mb 是基图行数,nb 是列数。每个非零元素 (i, j) 对应一个 Zc × Zc 的循环置换矩阵,移位量是 V(i,j) mod Zc。MATLAB 里不要直接生成稠密 H,用稀疏索引存,否则 Zc 一大内存就爆。
% 以 BG1 为例,假设已从标准表读入非零位置 row_idx, col_idx, shift_val % row_idx, col_idx 是基图里的行列索引(从 1 开始) % shift_val 是对应的循环移位值 Zc = 384; mb = 46; nb = 68; % BG1 展开后的基图行列数(含校验) H_rows = []; H_cols = []; H_vals = []; for k = 1:length(row_idx) i = row_idx(k); j = col_idx(k); v = mod(shift_val(k), Zc); for z = 1:Zc r = (i-1)*Zc + z; c = (j-1)*Zc + mod(z - 1 + v, Zc) + 1; H_rows(end+1) = r; H_cols(end+1) = c; H_vals(end+1) = 1; end end H = sparse(H_rows, H_cols, H_vals, mb*Zc, nb*Zc);逻辑说明:外层遍历基图非零元素,内层把每个元素展开成 Zc 个 1,列索引按mod(z-1+v, Zc)+1做循环移位。参数v必须先对 Zc 取模,这是标准要求和实际实现最容易漏的一步。生成的 H 用sparse存,后续编码用。如果 Zc 取 384、BG1 全展开,H 是 17664 × 26112 的稀疏矩阵,稠密存储会直接吃光内存。
3. 编码实现:从信息比特到码字的可复现步骤
3.1 5G LDPC 编码的核心:双对角结构与快速编码
5G LDPC 的校验矩阵有个特殊结构:前几列校验位构成双对角(dual-diagonal)形式,这让编码可以用递推而不是高斯消元。标准里的编码流程是先把信息比特填进前 K 列,然后按双对角结构逐行递推校验位。具体来说,H 可以分成 [A B C D E] 几块,其中 B 是双对角块,编码时先算中间变量,再回代求校验位。这个结构的好处是编码复杂度线性,不用求逆。
实现时不要自己推公式,按 38.212 的编码步骤来:先做信息比特的循环移位累加,再逐块解出校验位。MATLAB 里可以用稀疏矩阵乘法加速,但要注意索引对齐——基图列索引和实际比特位置的映射错一位,整个码字就废了。
3.2 最小可跑编码脚本与参数说明
下面给一个简化但结构完整的编码框架,假设 H 已经按上一章生成,并且已经按双对角结构重排。
function codeword = ldpc_encode_5g(info_bits, H, Zc, K) % info_bits: 信息比特列向量,长度 K % H: 稀疏校验矩阵,已按编码结构排列 % Zc: 提升因子 % K: 信息比特数 % 返回 codeword: 系统码码字,前 K 位是信息位 N = size(H, 2); M = size(H, 1); codeword = zeros(N, 1); codeword(1:K) = info_bits; % 分离双对角部分:假设 H = [H_info | H_parity] % H_parity 前 M 列是双对角结构 H_info = H(:, 1:K); H_parity = H(:, K+1:K+M); % 计算中间向量 s = mod(H_info * info_bits, 2); % 双对角递推求校验位 p = zeros(M, 1); % 第一行直接解 p(1) = s(1); for i = 2:M p(i) = mod(s(i) + p(i-1), 2); end codeword(K+1:K+M) = p; end逻辑说明:先把信息位放进码字前段,然后算H_info * info_bits得到中间向量s,再按双对角结构从第一行开始递推校验位。参数K是信息比特数,M是校验比特数,N = K + M。注意这里假设 H_parity 是标准双对角形式,实际标准里还有循环移位的细节,需要按 Zc 展开后处理。这个脚本跑通后,可以用mod(H * codeword, 2)验证是否全零,这是最直接的正确性检查。
3.3 码率匹配与打孔:别让码字长度对不上
5G LDPC 编码后还要做码率匹配,包括打孔、重复和交织。常见做法是按冗余版本(RV)从循环缓冲里取比特。打孔规则是:前 2 × Zc 个系统比特要打掉(标准规定),然后按 RV 参数从缓冲里顺序取。这一步出错的表现是译码端永远不收敛,因为收到的比特和编码端对不上。实现时把循环缓冲写清楚,打孔位置用索引数组标记,不要用逻辑判断硬删。
4. 译码与误码率验证:置信传播怎么在 MATLAB 里落地
4.1 对数域置信传播(LLR-BP)的最小实现
译码用对数域置信传播最稳,数值上比概率域好处理。核心是校验节点更新和变量节点更新交替进行。5G LDPC 的 H 是稀疏的,用稀疏矩阵存邻接关系,更新时只遍历非零元素。最小实现里,校验节点更新用 tanh 规则或 min-sum 近似。min-sum 复杂度低,性能损失约 0.2~0.5 dB,工程上常用。
function [decoded, iter] = ldpc_decode_min_sum(llr, H, max_iter) % llr: 接收到的对数似然比,长度 N % H: 稀疏校验矩阵 % max_iter: 最大迭代次数 [M, N] = size(H); [rows, cols] = find(H); % 初始化变量节点消息 Lq = zeros(length(rows), 1); for k = 1:length(rows) Lq(k) = llr(cols(k)); end decoded = zeros(N, 1); for iter = 1:max_iter % 校验节点更新(min-sum) Lr = zeros(length(rows), 1); for i = 1:M idx = find(rows == i); if isempty(idx), continue; end vals = Lq(idx); signs = prod(sign(vals)); abs_vals = abs(vals); [~, min_pos] = min(abs_vals); for k = idx' if k == idx(min_pos) sorted_abs = sort(abs_vals); min_val = sorted_abs(2); else min_val = sorted_abs(1); end Lr(k) = signs * sign(Lq(k)) * min_val; end end % 变量节点更新 for k = 1:length(rows) Lq(k) = llr(cols(k)) + sum(Lr(rows == rows(k))) - Lr(k); end % 硬判决 for n = 1:N total = llr(n) + sum(Lr(cols == n)); decoded(n) = total < 0; end % 校验是否满足 if mod(H * decoded, 2) == 0 break; end end end逻辑说明:Lq是变量节点到校验节点的消息,Lr是校验节点回传的消息。min-sum 里每个校验节点找最小和次小绝对值,符号用乘积。变量节点更新时把自身先验和所有相连校验节点的消息相加,减去当前边。硬判决后做校验,全零就提前退出。参数max_iter一般设 20~50,设太大时延高,设太小高码率下容易失败。
4.2 误码率曲线怎么跑才有说服力
跑 BER 曲线时,信噪比范围要覆盖瀑布区。5G LDPC 在 BG2、低码率下,Eb/N0 从 0 dB 到 4 dB 通常能看到明显下降。每个点至少统计 100 个错误比特,否则曲线抖动大。MATLAB 里用comm.LDPCDecoder可以对照,但自己实现的版本要验证:同一组数据,自己编码自己译码,BER 应该和工具箱接近。如果差很多,先查打孔和码率匹配,再查 H 矩阵的循环移位。
4.3 用已知码字做回归测试
最可靠的验证方法是构造已知信息比特,编码得到码字,加噪声后译码,看能否恢复。更严格的是直接检查H * codeword mod 2是否全零。每次改完基图或 Zc,先跑这个检查,通过了再跑 BER。这一步能挡掉大部分索引错位和取模错误。
5. 避坑与排查:5G LDPC MATLAB 实现里最容易翻车的 5 个点
5.1 现象:编码后校验全零检查不过,H 乘码字结果非零
原因:循环移位值没有对 Zc 取模,或者基图行列索引从 0 开始和 MATLAB 从 1 开始混用。标准表里的索引有的从 0 计,直接拿来用会整体偏移一位。
解决:统一索引基准,所有从标准表读入的索引先加 1。循环移位值在展开前做mod(V, Zc)。检查时打印前几个非零元素的移位量,和标准表对照。
5.2 现象:译码不收敛,BER 曲线在高信噪比下仍然很高
原因:打孔位置不对。5G LDPC 规定前 2 × Zc 个系统比特要打掉,如果漏打或多打,译码端先验信息就错了。
解决:在码率匹配模块里显式标记打孔索引,编码端和译码端用同一套索引。打孔后的 LLR 置零,不要删元素,保持长度一致。
5.3 现象:Zc 选大了内存爆,选小了码率对不上
原因:Zc 选取没有按码块长度和基图列数反推,或者忽略了 Zc 合法集合的限制。
解决:先算Zc_need = ceil(K / Kb),其中 Kb 是基图信息列数,再在合法集合里选最小满足值。不要用任意整数,标准只认那 51 个值。
5.4 现象:min-sum 译码性能比预期差 1 dB 以上
原因:归一化系数没加。min-sum 的近似会高估校验节点消息,加一个 0.75~0.8 的归一化因子能补回来。
解决:在校验节点更新后乘归一化系数,或者用 offset min-sum,减去一个固定偏移。参数按码率和迭代次数调,一般 0.75 起步。
5.5 现象:BG1 和 BG2 切换后结果全乱
原因:两张基图的列数、信息列数、双对角结构位置都不同,代码里硬编码了 BG1 的维度。
解决:把基图参数做成配置结构体,BG 切换时整体替换。信息列数、校验列数、双对角起始位置都从配置读,不要写死在函数里。
6. 进阶技巧:用 MATLAB 做 5G LDPC 的快速原型与参数扫描
6.1 把编码译码封装成可配置对象
写到后面你会发现,每次改 Zc 或 BG 都要动一堆脚本。用 MATLAB 的类或结构体把参数封起来,编码和译码只依赖配置对象。这样扫参数时只改一个字段,不用碰核心代码。我一般会定义一个ldpc5g_config结构体,包含bg、Zc、K、N、max_iter、normalize_factor,所有函数从这个结构体取参数。
6.2 参数扫描的实用表格
下面这张表是我扫参数时常用的范围,按场景调:
| 参数 | 典型范围 | 说明 |
|---|---|---|
| Zc | 按码块长度反推 | 必须在合法集合内 |
| max_iter | 20~50 | 高码率取大,低时延取小 |
| normalize_factor | 0.7~0.85 | min-sum 归一化,0.75 起步 |
| Eb/N0 范围 | 0~5 dB | 覆盖瀑布区 |
| 每点错误数 | ≥100 | 保证曲线平滑 |
扫的时候先固定 Zc 和 BG,扫 Eb/N0 看 BER;再固定 Eb/N0,扫 max_iter 看收敛速度。两步分开做,不要一次全扫,否则结果没法归因。
6.3 用 profile 找瓶颈
MATLAB 的profile on能直接告诉你时间花在哪。5G LDPC 译码里,校验节点更新的循环通常是热点。如果发现find调用太频繁,把邻接关系预存成索引数组,别在迭代里反复查。我自己的习惯是:先跑通,再 profile,只优化占时间超过 30% 的那一段,其他不动。
6.4 一个我踩过的坑
早期实现时我直接把标准表的移位值拿来用,没做mod Zc,结果 Zc 小的时候编码全错,Zc 大的时候偶尔对。查了两天才发现是取模漏了。从那以后,凡是标准表里的值进代码,第一件事就是确认它的定义域和取模规则。这个习惯帮我省了很多后悔药。希望帮到你。
本文还有配套的精品资源,点击获取