QC-LDPC码MATLAB源码详解:从H矩阵构造到译码仿真 简介基于MATLAB实现的准循环低密度奇偶校验QC-LDPC码源代码面向通信工程、信息理论及相关方向的研究人员、在校学生与算法工程师用于快速完成纠错编码算法的仿真验证与性能对比。该码型在保留LDPC码接近香农极限能力的同时凭借准循环结构显著降低编码复杂度并有效规避4环问题提升译码稳定性。资源包为zip格式包含1个m文件大小仅2KB代码体积精炼便于直接阅读与二次修改。目前已有386人学习下载可作为QC-LDPC码学习与项目开发的基础参考。该m文件针对QC-LDPC码的核心设计流程编写涵盖带约束的校验矩阵构造、避免4环的循环移位设计、快速编码的矩阵运算实现以及基于置信传播的译码思路与误码性能评估方法能够帮助使用者理解准循环结构如何提升编码效率同时为调整码率、码长等参数和移植到其他仿真平台提供出发点。1. 能把QC-LDPC码跑通这套MATLAB源码值得你花一下午拆一遍做通信物理层仿真的工程师对QC-LDPC码应该都不陌生。5G NR、DVB-S2、WiFi 802.11n/ac这些标准里QC-LDPC码几乎是无处不在的信道编码方案。但真正自己动手把QC-LDPC码从构造校验矩阵、编码到译码全过程在MATLAB里跑通的人可能没那么多——因为这中间有太多细节容易让人翻车。我最初接触QC-LDPC码时最大感触就是全网能找到的代码不少但要么只给了基矩阵不教你怎么扩展成完整的H矩阵要么译码部分写得稀碎、BP译码和最小和译码混在一起完全跑不出预期的误码率曲线。这份QC-LDPC码的MATLAB源码包解决的就是这个痛点。它不是单纯丢给你几个孤零零的函数而是一套从H矩阵构造、编码到译码仿真验证的完整链路。你拿到之后可以在自己的仿真工程里直接调用它的QC-LDPC编解码函数或者把它当作学习模板搞清楚QC-LDPC码的准循环特性到底是怎么通过循环移位矩阵实现的。如果你正在做LDPC码相关的课程设计、毕业设计、或者实际项目中需要选定信道编码方案做性能验证这份源码都会是一个很合适的起点。它的代码风格是教科书和工程实践之间的那种状态既有清晰的变量命名和关键注释也有可以直接改参数跑仿真出图的完整脚本对新手友好对熟手来说也省了从零搭链路的时间。2. QC-LDPC码的核心机制为什么基矩阵加循环移位就够了2.1 准循环结构到底省了什么理解QC-LDPC码关键要抓住「准循环」这三个字。LDPC码的校验矩阵H如果随便设计那么编码复杂度会随着码长的增加而急剧上升因为生成矩阵往往不再具备稀疏性编码过程相当于在做稠密矩阵乘法工程实现根本扛不住。QC-LDPC码的思路是把一个大的校验矩阵H切成若干个子块每个子块要么是零矩阵要么是单位矩阵的循环移位版本。这样一来整个校验矩阵就由一个小得多的基矩阵也叫基础矩阵或原型矩阵通过扩展因子lifting factor扩展得到。基矩阵中的每个元素告诉你这个位置是零矩阵还是某种循环移位矩阵。如果有多个子块是循环移位的单位阵那么整个H矩阵就具有分块循环的特性译码时可以利用这种结构做部分并行处理硬件实现时存储和路径选择都简化了不少。这套MATLAB源码里核心的构造函数就是围绕这个逻辑展开的先把基矩阵定义出来再根据你指定的扩展因子Z生成完整的H矩阵。实际仿真时你会发现通过修改基矩阵中的移位值可以灵活调整校验矩阵的围长girth和度分布从而影响误码率性能。比如基矩阵中某个位置的值是5表示单位阵循环右移5位如果填的是-1表示这是一个Z×Z的全零矩阵。这个约定在很多标准中都是通用的源码里也遵循了这个规则。2.2 基矩阵、扩展因子与码率的关系QC-LDPC码的码率直接由基矩阵的尺寸决定。假设基矩阵是mb×nb的扩展因子是Z那么完整的校验矩阵H就是mbZ×nbZ的。如果基矩阵是满秩的码率就是(nb-mb)/nb。5G NR标准中基矩阵有两个版本分别对应不同的码率范围但本质上都是同一个扩展逻辑。这套MATLAB源码里带的基矩阵例子支持多种码率配置你可以直接修改基矩阵的行数和列数来适配自己的需求。我在实际使用中一般会先在源码的参数配置区设置好Z值比如Z64或Z128然后观察H矩阵的稀疏度以及行列重的分布。这里有个很实用的经验扩展因子Z越大H矩阵尺寸越大但单位信息的编码增益不一定线性提升因为围长和环的分布也会变化。所以做仿真对比时不要只调Z还要对照看误码率曲线的交叉点。另一个容易忽略的点是编码模式。QC-LDPC码的编码可以利用H矩阵的准循环结构做高效的校验位计算而无需显式生成稠密生成矩阵。这套源码在编码实现上采用了类似RU算法的思路把H矩阵分块成A、B、C、D、E子矩阵利用B矩阵的特殊结构比如双对角结构做递推求解校验位。这样可以避开大矩阵求逆的复杂度。源码里的编码函数对这块逻辑做了完整实现你不需要再去翻标准的附录推导一遍。提示如果你拿到源码后想换自己的基矩阵注意保持移位值在0到Z-1范围内否则构造出来的H矩阵会出现非法的循环移位译码表现会很反常。3. 把源码跑起来从H矩阵构造到完整的仿真链路3.1 H矩阵构造函数的使用与参数配置代码包里最核心的就是QC_LDPC_H_construction这个MATLAB函数它负责把基矩阵和扩展因子转换成完整的校验矩阵。它的调用方式很直观% 定义基矩阵:每一行代表一个子块的行位置,每一列代表子块偏移 % 这里以某标准中的基矩阵为例, -1表示全零块, 非负整数表示循环右移位数 base_matrix [ 0 1 2 -1 3 -1; -1 0 1 2 -1 4; 2 -1 0 1 4 -1 ]; Z 32; % 扩展因子, 即每个子块的维度 H QC_LDPC_H_construction(base_matrix, Z); % 返回的H是mb*Z行, nb*Z列的稀疏逻辑矩阵这段代码的逻辑并不复杂。函数内部会先读取基矩阵的行数和列数然后用一个循环遍历每个元素。对每个非负整数p它会生成一个Z×Z的单位阵再对单位阵按行循环右移p位填入H矩阵对应的子块区域。如果元素是-1就直接填充Z×Z的全零矩阵。参数说明base_matrix中的移位值p取值范围是0到Z-1。如果p超过了Z-1循环移位会产生混叠相当于回到了某个非预期的移位状态这会破坏校验矩阵的代数结构。Z的取值一般建议和调制阶数、码长匹配比如码长等于nb×Z时Z的选择要让码长符合实际传输块的大小。跑完这段代码你可以用nnz(H)看一下非零元素个数再算一下H矩阵的列重和行重确认它确实是一个稀疏矩阵。如果非零元素比例太高说明基矩阵设计得不好后续译码收敛会非常慢。3.2 编码实现利用校验矩阵直接算校验位编码函数是这套源码里分量最重的部分。由于QC-LDPC码的H矩阵不是系统形式的直接用H矩阵编码需要先做高斯消元但这会破坏稀疏性和准循环结构。源码的实现思路是利用H矩阵的分块结构把校验位逐段计算出来。function codeword QC_LDPC_encode(info_bits, H, Z) % info_bits: 待编码的信息比特向量, 长度等于 code_length - parity_length % H: 完整的校验矩阵, 由QC_LDPC_H_construction生成 % Z: 扩展因子, 用于分块索引 [mb, nb] size(H) / Z; % 基矩阵的行数和列数 % 按基矩阵块大小对H进行分块索引 % 对H进行LU-style分块, 利用B矩阵的双对角结构求解校验位 % 核心代码省略中间步骤... % 常见做法是先把H重排成[A B; C D]的形式 % 然后计算phi D - C*(A\B), 校验位p1 phi \ (C*(A\info)) end实际编码流程中我习惯先把信息比特填充到码字的前半部分然后解方程得到校验位。这里有一个数值上的坑MATLAB中用反斜杠运算符解稀疏矩阵方程时如果矩阵是奇异的会静默给出错误的结果或者警告。所以源码中如果涉及到矩阵求逆或除法操作建议在调用编码函数之前先检查H矩阵的秩是否为满秩。如果你只是做算法验证而不追求工程级的编码速度也可以直接把H矩阵做高斯消元得到生成矩阵G然后用G乘以信息比特得到码字。但这种方法只适合码长比较短的情况码长超过几千比特时生成矩阵会变得非常稠密内存和运算量都吃不消。3.3 译码链路BP译码与最小和译码的完整实现这套源码里包含了两套译码器置信传播BP译码器和最小和Min-Sum译码器。BP译码的精度更高但运算量大而且需要对信道对数似然比LLR做校准最小和译码是BP的近似用最小值操作代替了双曲正切函数的计算硬件实现友好得多。这里的实现两者都有你可以直接对比它们的误码率差异。function decoded_bits QC_LDPC_BP_decode(recv_llr, H, max_iter) % recv_llr: 接收符号的对数似然比, 长度等于码长 % H: 校验矩阵 % max_iter: 最大迭代次数, 一般取5~50 [rows, cols] size(H); % 初始化变量节点消息为信道LLR % 迭代循环 for iter 1:max_iter % 校验节点更新: 计算每个校验节点发给变量节点的外信息 % 变量节点更新: 累加所有相邻校验节点的消息加上信道LLR % 判决: 如果所有校验方程满足或达到最大迭代次数则退出 end % 输出硬判决结果 endBP译码里最需要校准的参数是信道LLR的缩放因子。如果你用的是BPSK调制加高斯白噪声信道LLR的标准计算公式是2r/σ²其中r是接收符号σ²是噪声方差。但很多人在仿真时直接用接收信号本身当作LLR也不做幅度归一化导致译码性能大幅下降。这套源码的demo脚本里给出了正确的LLR计算方式。要注意的是BP译码的复杂度是O(n·dv)其中dv是变量节点的最大度。对于高列重的码迭代一次的时间开销会比较大。我做仿真时一般先跑20次迭代看趋势如果误码率没有明显下降再考虑增加到50次。这里有一个建议如果你想要误码率曲线稳定每个信噪比点上至少要统计100个错误比特如果仿真量太大至少也要统计30个错误帧否则误码率曲线的抖动会很厉害没法判断性能优劣。4. 仿真参数设置与性能调优从demo脚本到可复现误码率曲线4.1 完整仿真脚本框架源码包里提供了一个完整的仿真脚本从随机信息比特生成、编码、BPSK调制、加噪、译码到误码率统计都有。我建议你先把这个脚本完整跑一遍确认能出图再去改参数做自己的实验。脚本的核心框架如下clear; close all; % 系统参数 Z 32; base_matrix [...]; % 载入你的基矩阵 H QC_LDPC_H_construction(base_matrix, Z); [mb, nb] size(base_matrix); code_len nb * Z; % 码字长度 info_len (nb - mb) * Z; % 信息位长度 rate info_len / code_len; % 码率 % 仿真参数 EbN0_dB 0:0.5:4; max_iter 20; num_frame 2000; for idx 1:length(EbN0_dB) % 计算噪声方差: BPSK调制下 Es/N0 Eb/N0 * rate EsN0_dB EbN0_dB(idx) 10*log10(rate); noise_var 10^(-EsN0_dB/10); ber_sum 0; frame_err 0; for frame 1:num_frame info_bits randi([0 1], 1, info_len); codeword QC_LDPC_encode(info_bits, H, Z); % BPSK调制: 0映射为1, 1映射为-1 tx_signal 1 - 2 * codeword; % 加性高斯白噪声 rx_signal tx_signal sqrt(noise_var) * randn(size(tx_signal)); % 计算LLR llr 2 * rx_signal / noise_var; % 译码 decoded_bits QC_LDPC_BP_decode(llr, H, max_iter); % 统计错误 bit_err sum(decoded_bits(1:info_len) ~ info_bits); ber_sum ber_sum bit_err; end ber(idx) ber_sum / (info_len * num_frame); end % 画图 semilogy(EbN0_dB, ber, b-o); grid on;这段脚本中最关键的是EsN0和EbN0之间的换算。很多第一次做LDPC仿真的人会把这两个概念混在一起直接用EbN0去算噪声方差导致结果横坐标偏移了10*log10(rate)个dB。码率越低这个偏移越明显。这里加了一句换算其实是在弥补这个常见错误。帧数和迭代次数也是性能瓶颈所在。如果你用BP译码且迭代次数设置得太大每个信噪比点跑2000帧可能要几个小时。我一般的做法是先用500帧粗略看一下各信噪比点的误码率量级如果在某个信噪比下100帧里一帧都没错就可以把这个点标记为「无误码区域」不用继续加大帧数。然后针对误码率在1e-3到1e-5之间的中间区域把帧数加到5000以上这样绘制的曲线才平滑、可信。4.2 不同译码算法的对比与选择同一个H矩阵下BP译码和最小和译码的性能差异通常在0.2~0.5dB之间。BP译码更接近最大似然译码的性能但实现时要特别注意数值稳定性。当迭代次数增大时BP译码的更新公式中可能会出现概率值下溢导致译码器输出NaN。源码里在BP译码实现中加了数值保护机制即在每个变量节点更新后检查对数似然比绝对值是否超过一个阈值比如20或30。如果超过就截断到该阈值防止数值爆炸。最小和译码的实现相对简单性能和BP的差距可以通过乘一个归一化因子来弥补。常见的做法是在最小和更新公式中乘一个0.8左右的缩放因子这个因子可以针对不同的基矩阵做微调。我在对比仿真时发现归一化最小和Normalized Min-Sum的性能可以做到比BP差不到0.3dB而单次迭代的耗时差不多只有BP的三分之一到一半。如果你的项目强调吞吐或实时性最小和是更优的选择如果是课程设计需要展示理论性能上限就用BP。源码的demo里两种译码器是分开的两个文件你可以通过切换译码函数让同一套仿真脚本跑出两条对比曲线。这个对比图放在报告里是很加分的因为它说明你对算法本身的复杂度-性能权衡有清晰的认识。提示如果你在跑仿真时发现误码率曲线在高信噪比下出现平台error floor不要急着加迭代次数绝大多数情况是H矩阵的围长不够存在较多的长度为4的环导致错误传播无法消除。这时应该回头检查基矩阵的移位值设计。5. 避坑指南QC-LDPC码仿真中的高频翻车点5.1 现象译码曲线完全乱跳或直接报错很多人在第一次跑QC-LDPC仿真时会遇到一个现象在低信噪比下误码率甚至高于不编码的情况或者译码器直接报「Matrix is close to singular or badly scaled」的警告。原因H矩阵构造问题大概率是基矩阵中存在重复的移位值导致H矩阵的行之间存在线性相关使得H矩阵不满秩。这样一来编码时求解校验位就会出现数值奇异。另外基矩阵中移位值的范围超过Z-1也会导致子块循环移位重叠。解决先检查基矩阵的每一行、每一列对应位置的移位值是否都在0到Z-1之间且同一行内是否有两个子块的移位值差值为0即两个列块完全相同。如果有需要手动调整基矩阵的第几个移位值比如加1再对Z取模。习惯上我会用MATLAB的rank函数检查构造出的H是否满秩rank(full(H))如果秩不是mb*Z说明基矩阵设计不合格。5.2 现象BP译码迭代不收敛输出全零另一个常见问题是译码器迭代了50次硬判决结果依然是全零或者完全随机的乱码误码率在0.5左右徘徊。原因LLR计算错误。最常见的情况是BPSK映射时使用了0映射为0、1映射为1的信号没有转换成双极性信号0映射为11映射为-1。接收端直接套用2r/σ²的公式LLR的符号完全反了译码器当然收敛不到正确码字。解决把调制映射统一成双极性即tx_signal 1 - 2*codeword。另外检查一下是否有信噪比单位混用如果仿真脚本中噪声方差是按EsN0计算的不要用EbN0去套公式。遇到这种问题时可以在编码之后对码字先做一次校验计算mod(H * codeword, 2)是否全零如果不是全零说明编码环节已经出错译码器再怎么迭代也没有意义。5.3 现象误码率曲线高信噪比下有error floor这个现象比较隐蔽。有时候低中信噪比区域曲线下降正常但当信噪比超过某个值后误码率不再下降出现了一个明显的平台。原因H矩阵的短环问题。QC-LDPC码的基矩阵在扩展时若某个移位值的组合形成了长度为4的环即基矩阵中存在一个2×2子块且移位值满足特定等式关系那么在完整的Tanner图中就存在环。短环会极大削弱迭代译码的性能尤其在较高信噪比下错误的迭代消息会在环中循环增强。解决重新设计基矩阵的移位值优先选去除长度为4的环的移位值组合。可以编写一个小脚本遍历Z的取值检测每种组合下是否满足消除4环的条件。源码包中如果附带了girth检测函数可以用它来辅助筛选。5.4 现象仿真速度极慢一个信噪比点跑一小时最后这个现象更像是个工程效率问题而非错误。不少同学在demo框架下直接加了长的码长比如Z256nb20以上然后把帧数设成5000加上BP译码的50次迭代运行时间指数级上升。原因没有合理利用QC-LDPC码的准循环特性。逐比特更新消息是一般LDPC的通用做法但QC-LDPC的校验矩阵有分块结构可以成块地更新消息MATLAB中用矩阵运算替代for循环可以大幅加速。解决用tic和toc测量单帧译码时间如果超过0.1秒就要考虑优化。另外在调试阶段先把迭代次数降到5帧数降到100确认整个流程正确后再逐步增加仿真规模。做参数扫描时优先并行运行不同信噪比点而不要串行跑完一条曲线再跑另一条。6. 进阶验证拿这套源码生成标准码并对比理论界当你已经把源码跑通、误码率曲线稳定之后可以考虑做一个更有说服力的验证构造一个接近标准的QC-LDPC码和相同码长码率下的随机LDPC码或已知的好码做性能对比并且画上香农极限线作为参考。这样做的价值在于你能直观地看到QC-LDPC码的结构化设计带来的性能损失有多少以及迭代译码离最大后验概率译码还有多远。我一般会这样做把源码中的基矩阵换成标准里某一个已知的好基矩阵扩展因子设为64码率0.5然后跑出它的误码率曲线。再找一段同样是0.5码率、码长相同的随机LDPC码用稀疏随机矩阵构造用同一套BP译码跑一遍。把两条曲线画在同一张图上再加上对应码率下BPSK的香农极限。如果一切正常QC-LDPC码的性能应该在随机码附近或略差0.1~0.2dB但编译码的复杂度明显更低。这里有一个具体技巧可以用于验证译码算法实现的正确性取基矩阵为单位阵循环移位的特例即每个子块都是循环移位阵没有-1此时QC-LDPC码退化为一个重复码加交织的级联码结构我们可以精确计算出它的译码门限。如果仿真的误码率曲线在这个门限附近显现出快速下降的趋势说明你实现的BP译码逻辑没有重大缺陷。我最初拿到这套源码时就是先这么做了一次校验确认无误以后才放心在它基础上做新的基矩阵搜索实验。从那以后我每次拿到任何LDPC相关的代码都会强制先跑一遍香农极限对比和特例校验这两个环节再决定是否在项目中复用它。这套源码至少能帮你省掉最磨人的底链搭建阶段。希望这份拆解帮到你。本文还有配套的精品资源点击获取