QC-LDPC MATLAB仿真资源包:从矩阵构造到BER性能评估
简介:QC-LDPC.zip 是一份面向通信与量子纠错初学者的 MATLAB 仿真学习资源,围绕准循环低密度奇偶校验码(QC-LDPC)的编码实现展开,适合希望理解 LDPC 码构造原理、动手复现编码流程的高校学生与科研入门者。压缩包共 3 个文件,以 2 个 m 脚本和 1 个 asv 自动备份文件为主,整体仅 4KB,轻量易读。其中脚本涵盖校验矩阵构建与编码主流程,注释较为详细,可帮助读者直观理解码字生成、生成矩阵线性运算、信道噪声模拟以及基于消息传递算法的迭代解码等关键环节,并支持通过修改参数观察不同设置下的纠错性能变化。目前已有 1253 人学习下载,适合作为掌握 QC-LDPC 编码技术、为后续量子通信研究打基础的入门实践材料。
1. 从一次链路仿真翻车说起:QC-LDPC.zip 到底是什么
去年帮一个做卫星通信的团队排查链路误码率异常,对方信誓旦旦说 LDPC 编码增益没问题,结果我把他们的校验矩阵打印出来一看——准循环块大小和扩展因子对不上,译码器迭代 50 次都不收敛。这种坑在 LDPC 项目里太常见了,而QC-LDPC.zip这个资源包,恰好就是冲着这类问题来的。它是一套基于 MATLAB 的准循环 LDPC 码完整实现,覆盖校验矩阵构造、编码、BP 译码以及 BER 性能评估。QC-LDPC(Quasi-Cyclic LDPC)的核心价值在于:用循环移位单位阵拼出稀疏校验矩阵,既保留随机 LDPC 的纠错能力,又能用移位寄存器硬件高效实现,5G NR 数据信道用的就是这类码。这个包适合通信算法工程师、做信道编码课程设计的研究生,以及需要快速验证 LDPC 方案可行性的系统仿真人员。如果你正在找一份能跑通、能改参数、能对照标准看性能的 MATLAB LDPC 源码,这份资源值得拆开细看。
2. QC-LDPC 的矩阵构造与编码逻辑:为什么不能随便填 1
2.1 准循环结构的数学本质
QC-LDPC 的校验矩阵 H 不是随机撒 1,而是由若干个大小为 Z×Z 的循环置换矩阵(CPM)和全零矩阵拼成。每个 CPM 由单位阵循环右移若干位得到,移位量记作 shift value。整个 H 可以写成基矩阵(base graph)加扩展因子 Z 的形式:基矩阵里每个元素要么是 -1(对应全零块),要么是一个非负整数(对应移位量)。这种结构的好处是,编码时只需要存储基矩阵和移位表,硬件上用桶形移位器就能完成矩阵向量乘法,省掉大量寄存器。
资源包里的构造脚本一般会提供两种基矩阵来源:一种是协议规定的标准基图(比如 5G NR 的 BG1/BG2),另一种是 PEG(Progressive Edge Growth)或 Gallager 方法生成的随机基矩阵再做准循环化。我一般建议先跑标准基图,确认编译码链路通了,再换自定义基矩阵做对比。
2.2 编码实现:从生成矩阵到累加编码
LDPC 编码的朴素做法是先对 H 做高斯消元得到生成矩阵 G,然后 c = m·G。但 H 的维度动辄几千,直接求 G 计算量大且破坏稀疏性。工程上常用的是基于近似下三角(ALT)的累加编码,把 H 重排成特定形式后分块回代。
资源包里通常会有encodeLDPC.m或类似函数,核心逻辑是先把 H 分成信息位和校验位两块,利用校验方程 H·c^T = 0 解出校验位。下面是我根据常见实现整理的一段编码调用示例:
% 参数设置 Z = 384; % 扩展因子,5G NR 常用 384 bg = 1; % 基图编号,BG1 适合高码率 K = 22 * Z; % 信息位长度 N = 68 * Z; % 码字长度(BG1 满配) R = K / N; % 码率约 0.32~0.89 可调 % 构造基矩阵和移位表(资源包内函数) [H, shifts] = buildQCBaseMatrix(bg, Z); % 生成随机信息比特 msg = randi([0 1], K, 1); % 编码:利用校验方程回代求校验位 codeword = qcLDPCEncode(msg, H, shifts, Z); % 验证校验方程 if any(mod(H * codeword, 2) ~= 0) error('编码结果不满足校验方程,检查移位表'); end这段代码的关键参数是Z和bg。Z决定码块粒度,越大码长越长、纠错越强,但译码延迟也上去;bg选 1 还是 2 取决于码率和信息位长度,BG1 支持更大信息块。shifts是移位量表,必须和H的块结构严格对应,否则校验方程必挂。资源包里如果提供了buildQCBaseMatrix,直接调用即可;如果没有,需要自己按协议表格填移位值。
2.3 译码:置信传播与迭代停止条件
译码端资源包一般给的是对数域 BP(Belief Propagation)或最小和(Min-Sum)算法。BP 译码在 Tanner 图上反复传递变量节点和校验节点的对数似然比,直到校验方程全部满足或达到最大迭代次数。最小和是 BP 的近似,把 tanh 运算换成符号乘积加最小值,硬件友好但损失约 0.2~0.5 dB 增益。
调用译码函数时,除了接收软信息,还要设maxIter和alpha(最小和的归一化因子)。maxIter设太小会提前退出导致误码,设太大浪费算力。我一般先设 50 次,观察平均迭代次数分布再调。
% 通过 AWGN 信道 snr_dB = 2.5; rxSig = awgn(1 - 2*codeword, snr_dB, 'measured'); % 计算初始 LLR llr = 2 * rxSig / (10^(snr_dB/10)); % 最小和译码 maxIter = 50; alpha = 0.75; % 归一化因子,经验值 0.7~0.8 [decoded, iterUsed] = qcLDPCDecode(llr, H, shifts, Z, maxIter, alpha); % 统计误码 bitErr = sum(decoded ~= msg);alpha这个参数是血泪经验:不归一化的最小和译码性能会明显劣化,0.75 左右是多数场景的甜点值。iterUsed能帮你判断译码器是否工作在收敛边缘——如果大量码字用满 50 次迭代,说明 SNR 太低或矩阵构造有问题。
3. 在 MATLAB 里跑通 BER 曲线:参数怎么设、图怎么看
3.1 仿真脚本的组织方式
资源包通常带一个main_BER.m或simQC_LDPC.m作为入口。跑之前先确认三件事:MATLAB 版本(R2018b 以上对通信工具箱支持较好)、是否依赖 Communications Toolbox(awgn函数需要)、以及并行计算工具箱是否可用(跑 BER 曲线很吃时间,parfor能快数倍)。
一个典型的 BER 仿真循环结构如下:
snrVec = 0:0.5:4; % SNR 扫描范围 numFrames = 200; % 每 SNR 点仿真帧数 ber = zeros(size(snrVec)); for si = 1:length(snrVec) errCnt = 0; bitCnt = 0; parfor fi = 1:numFrames msg = randi([0 1], K, 1); cw = qcLDPCEncode(msg, H, shifts, Z); rx = awgn(1 - 2*cw, snrVec(si), 'measured'); llr = 2 * rx / (10^(snrVec(si)/10)); dec = qcLDPCDecode(llr, H, shifts, Z, 50, 0.75); errCnt = errCnt + sum(dec ~= msg); bitCnt = bitCnt + K; end ber(si) = errCnt / bitCnt; fprintf('SNR = %.1f dB, BER = %.3e\n', snrVec(si), ber(si)); end semilogy(snrVec, ber, 'o-'); grid on; xlabel('SNR (dB)'); ylabel('BER');numFrames的设置有个取舍:低 SNR 段误码多,200 帧足够统计;高 SNR 段误码率可能到 1e-6 以下,200 帧可能一个错都没有,曲线会断。常见做法是设一个最小错误数阈值(比如 100 个错误比特就停),而不是固定帧数。
3.2 参数扫描与性能对比
想验证不同码率或不同译码算法的差异,可以把Z、bg、maxIter、alpha做成外层循环。资源包如果带了多种译码实现(BP、最小和、偏移最小和),可以画在同一张图上对比。我一般会关注三个指标:BER=1e-5 时的 SNR 门限、平均迭代次数、以及误帧率(FER)。FER 在短码场景比 BER 更能反映系统表现。
提示:跑对比曲线时固定随机种子(
rng(42)),否则每次结果抖动会让你误以为参数改动有效。
4. 避坑与排查:QC-LDPC 仿真里最容易翻车的五件事
4.1 校验方程不满足,编码输出全是错
现象:编码后H * codeword模 2 不为零,译码器输入合法码字却输出乱码。原因:移位表shifts和基矩阵H的块索引错位,或者Z与移位量的取值范围不匹配(移位量应在 0 到 Z-1 之间)。解决:打印H的分块结构,逐块核对移位量;用单位阵测试单个 CPM 的循环移位方向(左移还是右移,不同资料定义相反)。
4.2 译码不收敛,迭代次数总是跑满
现象:iterUsed恒等于maxIter,BER 曲线在高 SNR 段出现错误平底(error floor)。原因:Tanner 图中存在短环(girth 太小),消息在环上反复震荡;或者最小和算法未归一化。解决:检查基矩阵的环分布,PEG 构造时设最小 girth 为 6;最小和译码加alpha=0.75归一化;必要时换偏移最小和(OMS)。
4.3 AWGN 信噪比定义搞混,曲线整体偏移
现象:BER 曲线比文献值差 3 dB 或好得不真实。原因:awgn函数的'measured'模式和手动计算 LLR 时的噪声方差口径不一致;或者 SNR 按符号算还是按比特算没统一。解决:统一用 Es/N0 或 Eb/N0,在代码里显式写出换算关系;LLR 计算中的噪声方差要与awgn实际加入的功率匹配。
4.4 大 Z 值导致内存溢出
现象:Z=384时H矩阵维度到几万,full(H)直接爆内存。原因:把稀疏校验矩阵转成了稠密矩阵做运算。解决:全程用稀疏存储(sparse),矩阵向量乘法用H * c而非full(H) * c;译码器内部用索引表代替完整矩阵。
4.5 并行仿真结果不可复现
现象:parfor跑出来的 BER 和for不一致。原因:parfor内随机数流独立,每个 worker 的种子不同。解决:在parfor内用RandStream按帧索引设种子,保证每次运行结果一致。
5. 进阶技巧:用外部信息比与 EXIT 图判断译码器极限
跑通 BER 曲线只是第一步。真正要判断一个 QC-LDPC 码的性能边界,我习惯做两件事:看外部信息传递图(EXIT chart)和统计译码器收敛时的平均互信息。EXIT 图能直观显示变量节点和校验节点的信息交换是否匹配,如果两条曲线在低 SNR 就相交,说明译码门限高,码本身设计有问题。
具体操作上,可以在译码迭代过程中记录每次迭代的变量节点外部 LLR 方差,换算成互信息。资源包如果没带这个功能,自己加一个钩子函数即可:
% 在译码循环内记录外部信息 miVar = zeros(maxIter, 1); for it = 1:maxIter [llrOut, llrExt] = qcLDPCDecodeOneIter(llr, H, shifts, Z, alpha); miVar(it) = computeMutualInfo(llrExt, msg); if all(mod(H * (llrOut > 0), 2) == 0) break; end end plot(1:it, miVar(1:it), '-o');computeMutualInfo用直方图法或高斯近似都行,关键是看互信息是否随迭代单调上升并趋于 1。如果曲线在中途走平,说明译码器陷入局部最优,需要调整基矩阵的度分布。
另一个实用技巧是给译码器加早停策略:连续两次迭代的硬判决结果不变就退出,能省 30% 以上平均迭代次数,对硬件实现尤其重要。我一般会在maxIter之外再加一个earlyStop标志,默认开启。
从那以后我每次拿到新的 LDPC 码,都强制先跑一遍校验方程验证和 EXIT 图,确认矩阵构造没问题再上 BER 仿真,省得后面调半天才发现是移位表填错了。希望帮到你。
本文还有配套的精品资源,点击获取