ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LDPC编码与BP译码的Matlab仿真链路:从稀疏校验矩阵到误码率曲线

LDPC编码与BP译码的Matlab仿真链路:从稀疏校验矩阵到误码率曲线 简介本资源是一套面向本科及硕士阶段通信工程、信息与信号处理方向学习者的LDPC码原理验证与仿真实践材料聚焦低密度奇偶校验码的编码、迭代解码如置信传播BP算法及校验过程的Matlab实现。资源基于MATLAB 2019a开发代码结构清晰、注释完整涵盖参数配置、 Tanner图构建、校验矩阵生成、比特翻转与消息传递等核心环节可直接运行并支持关键参数调整与性能曲线绘制如BER vs. SNR。压缩包共含若干.m主函数与脚本文件以Matlab源码为主辅以必要说明逻辑整体大小为2.3MB便于快速部署与教学演示。目前已有162人下载学习适用于课程设计、毕业设计前期验证、编码理论实验课拓展及科研入门参考帮助学习者深入理解LDPC码的数学机理与工程实现细节。 做通信仿真绕不开LDPC。这几年不管是5G NR、WiFi 6还是DVB-S2LDPC编码都是物理层前向纠错的主力方案我自己在Matlab里把编码、解码和校验整条链路跑通之后才真正理解了稀疏校验矩阵的分量。这篇就按我实际操作的顺序把LDPC的Matlab仿真链路、核心代码和踩过的坑一次讲清楚。先说清楚这篇适合谁通信方向的学生、做物理层算法仿真的工程师、以及想复现LDPC编解码流程但不想只调现成函数的人。如果你只是想拿仿真跑个误码率交作业我给的脚本可以直接用如果你想搞清楚H矩阵为什么是稀疏的、译码器里面的迭代到底在算什么那你会更喜欢前两章的内容。1. LDPC到底在干什么从信道到校验的整体视角1.1 编码在通信链路中的位置我见过不少朋友一上来就在Matlab里找LDPC函数这很容易陷入“用工具但不懂链路”的状态。先退一步看看整个数字通信系统信源经过信源编码压缩冗余经过信道编码加入可控冗余然后调制、上变频、进信道、接收端做镜像处理最后信道译码还原信息。LDPC就是信道编码这一步。信道编码的本质是在发送序列里刻意添加校验关系让接收端能够根据这些关系判断数据有没有被信道破坏并且在错误不严重时直接纠正回来。你可以把它理解成寄快递时填的保价单——多写几份信息万一路上盒子磕碰了收件人还能根据单据推测出原样。LDPC的任务就是把这个“保价单”设计得既高效又便宜。在Matlab仿真里我们通常用AWGN信道做基准测试也就是给信号叠加高斯白噪声。此时LDPC编码前后的区别非常直观没有信道编码时误码率随信噪比恶化得很快加了LDPC之后曲线会出现一个明显的“瀑布区”信噪比稍微提升一点误码率就急剧下降。这也是LDPC最吸引人的特性——逼近香农限的同时译码复杂度可控。1.2 稀疏校验矩阵LDPC名字的来源LDPC的全称是Low Density Parity Check Code低密度奇偶校验码。关键是“低密度”三个字——它的校验矩阵H里1的个数非常少。如果码长是1000典型行重可能只有20左右列重只有3左右。这种极度稀疏的结构是LDPC能被高效译码的根基。为什么稀疏这么重要译码器在工作时要沿校验矩阵的行和列来回传递“消息”。如果H矩阵是稠密的每个校验节点要处理成千上万个变量节点迭代一次的计算量就爆炸了。而稀疏矩阵保证了每个节点只和少数几个节点相连整个图就变成了一张稀疏的Tanner图消息传递的复杂度就降下来了。另一个对LDPC至关重要的概念是“环”。在Tanner图里消息从变量节点出发经过校验节点再回到变量节点如果路径太短会形成所谓的“小环”导致消息传递时相关性过强影响译码性能。所以构造H矩阵时我们不仅要让它稀疏还要尽量避免4环。后面代码里我会专门处理这个问题。1.3 在Matlab里仿真LDPC的两条路线Matlab里做LDPC仿真现在有两条完全不同的路线。第一条是直接用Communications Toolbox里的comm.LDPCEncoder和comm.LDPCDecoder对象加上dvbs2ldpc这类标准H矩阵生成函数。这条路线省时省力适合验证系统级性能比如看某个信噪比下的误码率或者做链路级联调。缺点是封装太厚想观察内部迭代过程、想改算法细节就比较吃力。第二条路线是自己手动实现编码和译码。编码端自己构造H矩阵、生成G矩阵译码端自己实现比特翻转BF算法或置信传播BP算法。这条路线的代码量明显更大但你能看到每一个变量在每次迭代中的变化真正理解LDPC的工作原理。我自己的建议是最终要交付系统级仿真走第一条路线要写论文、要理解算法本质、要面试讲原理走第二条路线。这篇博文会把两条路线都覆盖先用标准函数跑通全链路再手动实现一个最小可用的BP译码器最后把校验环节做扎实。2. 编码端实操校验矩阵H的构造与编码实现2.1 用Matlab内建函数拿标准H矩阵我们先走最快路径。Matlab的Communications Toolbox提供了dvbs2ldpc函数可以直接生成DVB-S2标准里的LDPC校验矩阵。调用方式非常简洁% 码率1/2码长64800的DVB-S2 LDPC码 H dvbs2ldpc(1/2); % 查看矩阵尺寸 disp(size(H));这个H是稀疏矩阵格式存储的尺寸是32400×64800非零元素占比非常低。之后可以直接传给编码器和译码器% 创建LDPC编码器和译码器 ldpcEnc comm.LDPCEncoder(H); ldpcDec comm.LDPCDecoder(H, ... IterationTerminationCondition, Parity check satisfied, ... MaximumIterationCount, 50); % 生成随机信息比特 infoLen size(H, 2) - size(H, 1); % 信息位长度 data randi([0 1], infoLen, 1); % 编码 codeword ldpcEnc(data); % 噪声信道和硬判决解码简单示例 rxBits codeword; noisePos randperm(length(codeword), 5); % 随便翻转5个bit模拟错误 rxBits(noisePos) 1 - rxBits(noisePos); decoded ldpcDec(double(rxBits)); % 校验 syndrome mod(H * double(codeword), 2); disp([编码后校验和: , num2str(sum(syndrome))]);dvbs2ldpc还有个好处是生成的H矩阵已经是准循环LDPCQC-LDPC结构这种结构非常利于硬件实现。如果你想做5G相关的仿真Matlab R2020b以上版本还有ldpcQuasiCyclicMatrix函数可以直接生成5G NR标准定义的BG1和BG2矩阵用法类似% 5G NR BG1码率兼容基矩阵12行 H5g ldpcQuasiCyclicMatrix(1, 12, 22, BaseGraph, 1);标准矩阵的好处是性能有保证不会出现因为H矩阵构造不合理导致的性能地板效应。用它来作为基准线是验证自己手写算法是否正确的第一步。2.2 自己动手构造一个Gallager矩阵如果你要深入理解LDPC手写H矩阵是一道绕不过去的坎。最经典、最基础的构造方式是Gallager在1962年提出的随机构造法。Gallager矩阵的思路分三步先按列重j把列分成j个块每块是一个行重为k的单位阵的列置换。听起来可能有点抽象我直接上代码配合注释就清楚了function H gallagerH(n, j, k) % n: 码长 % j: 列重每列1的个数 % k: 行重每行1的个数 % 条件: j*k n, 且n能被k整除 if mod(n, k) ~ 0 error(n必须能被k整除); end m n * j / k; % 校验行数 H zeros(m, n); % 第一块每行连续k个1 for row 1:m/k H(row, ((row-1)*k1):(row*k)) 1; end % 其余j-1块对第一块做随机列置换 for block 2:j perm randperm(n); H(((block-1)*m/k1):(block*m/k), :) H(1:m/k, perm); end % 随机打乱行顺序让矩阵看起来更均匀 H H(randperm(m), :); end这种构造方法能保证行重和列重严格固定矩阵一定是稀疏的。但它有个隐患——可能出现4环。4环的意思是Tanner图里存在两条长度为4的路径导致两个变量节点和两个校验节点两两相连。工程上一般会加一个去4环的后处理。function H removeCycle4(H) % 检查并消除4环简单实现 [m, n] size(H); maxIter 100; for iter 1:maxIter totalCycle4 0; % 遍历任意两列检查是否存在4环 for col1 1:n-1 for col2 col11:n commonRows sum(H(:, col1) H(:, col2)); if commonRows 2 % 找到4环把其中某个1移动到其他位置 totalCycle4 totalCycle4 1; rowsWithOnes find(H(:, col1) H(:, col2)); % 把col2在rowsWithOnes(1)处的1挪到同一行的0位置 H(rowsWithOnes(1), col2) 0; zeroCols find(H(rowsWithOnes(1), :) 0); % 选一个不与col1形成4环的列 for zc zeroCols if zc ~ col1 sum(H(:, col1) H(:, zc)) 2 H(rowsWithOnes(1), zc) 1; break; end end end end end if totalCycle4 0 break; end end end注意这个去4环实现是纯教学用的效率不高但能跑。实际工程里可以用渐进边增长PEG算法一步到位地构造无短环矩阵那个代码量更大这里不展开。2.3 从H到G把编码流程跑通有了H矩阵编码的基础就有了。LDPC编码本质是通过H矩阵构造生成矩阵G然后做c G * u的线性变换。这里的核心是把H化作系统形式。做法是高斯消元把H化成[Hp | H1]的形式其中Hp是可逆的方阵。然后生成矩阵G可以写成[I | (Hp^(-1) * H1)^T]的形式。主要坑在二进制域加减都是异或高斯消元里每一步都要用mod 2运算。我封装了一个最小实现function G getGeneratorMatrix(H) % 把校验矩阵H转换为系统形式的生成矩阵G % 要求H的列数n 行数m且前m列构成的矩阵可逆 [m, n] size(H); % 高斯消元把H化为 [A | B] 形式A是m×m单位阵或者尽量接近 Htmp mod(H, 2); % 列交换记录用来恢复原始顺序 colOrder 1:n; % 简化列交换的高斯消元 for col 1:m % 找主元 pivotRow find(Htmp(col:m, col), 1); if isempty(pivotRow) % 当前列没有主元尝试和后续列交换 swapped false; for c col1:n if ~isempty(find(Htmp(col:m, c), 1)) Htmp(:, [col c]) Htmp(:, [c col]); colOrder([col c]) colOrder([c col]); swapped true; break; end end if ~swapped error(矩阵不满秩无法直接构造生成矩阵); end pivotRow find(Htmp(col:m, col), 1); end pivotRow pivotRow col - 1; % 交换行 Htmp([col pivotRow], :) Htmp([pivotRow col], :); % 消去其他行的该列 for r 1:m if r ~ col Htmp(r, col) 1 Htmp(r, :) mod(Htmp(r, :) Htmp(col, :), 2); end end end % 此时Htmp [I | P] P Htmp(:, m1:end); % G [I | P^T]但需要恢复列顺序 % 在系统形式下G [I | P]P P^T 在GF(2)中转置 Gsystem [eye(n-m), mod(P, 2)]; % 恢复列顺序 G zeros(n-m, n); % colOrder前m列是主元列后面是自由列恢复时需要反向映射 % 这里简化处理如果原H的前m列恰好可逆则colOrder不会改变 if isequal(colOrder, 1:n) G Gsystem; else % 一般情况把Gsystem的列映射回原始列位置 % 系统形式下第i个信息位对应原始第colOrder(i)列 % 这个映射关系比较绕建议仿真时先检查colOrder是否为单位排列 invOrder(colOrder) 1:n; for i 1:n-m G(:, i) Gsystem(:, invOrder(m i)); end end end这段代码我加了注释但还是要提醒实际使用时如果构建矩阵时保留两列结构不做列交换用dvbs2ldpc生成的矩阵本身就是准循环磷酸二酯的不需要这么麻烦。但自己写一遍这个高斯消元能帮你彻底搞懂“由H推G”的数学过程。编码的时候信息位长度是n - m校验位长度是m编码就是简单的二进制矩阵乘法function codeword ldpcEncode(data, G) % data: 列向量长度 n-m % G: 生成矩阵尺寸 (n-m) × n codeword mod(double(data) * G, 2); codeword codeword.; end3. 解码端与校验核心算法和完整链路3.1 信道模型和接收端准备编码之后仿真要过信道。最常用的是BPSK调制加AWGN信道。BPSK就是把0变成1把1变成-1或者反过来然后叠加高斯噪声。用Matlab几行就能搞定function [rxLLR, rxBits] transmitAWGN(codeword, EbN0dB, codeRate) % codeword: 0/1序列 % EbN0dB: 每比特信噪比单位dB % codeRate: 编码码率 k/n n length(codeword); % BPSK调制0 - 1, 1 - -1 tx 1 - 2 * double(codeword); % 根据EbN0计算噪声方差 EbN0 10^(EbN0dB / 10); N0 1 / codeRate / EbN0; % 考虑码率后的噪声功率 sigma sqrt(N0 / 2); noise sigma * randn(n, 1); rx tx noise; % 硬判决用于对比 rxBits double(rx 0); % 软信息对数似然比 LLR 2*rx / sigma^2 rxLLR 2 * rx / (sigma^2); end这里有个容易搞混的地方EbN0是每信息比特的信噪比不是每编码比特的信噪比。由于LDPC引入了冗余每编码比特实际分的能量更少所以计算噪声方差时要除以码率。如果你想让仿真结果和其他论文对齐务必注意这个细节。3.2 置信传播BP解码的核心思想解码是LDPC最精彩的部分。BP译码的基本思想是每个变量节点从信道拿到初始LLR对数似然比然后和相邻的校验节点来回传递“软信息”经过多轮迭代最终对每个比特做出判决。我第一次看BP算法的时候觉得很抽象后来用一个比喻就通了想象一个班级里每个学生拿到一个原始分数信道LLR但老师规定同组的几个学生的分数必须满足某种校验关系比如加起来是偶数。每个学生先根据自己分数和同组其他人的分数反过来估算自己纠正后的分数然后再和下一组调整。多轮交流之后整个班级的分数逐渐收敛到一个满足所有校验关系的状态。BP译码在Matlab里用对象调用是现成的dec comm.LDPCDecoder(H, IterationTerminationCondition, Parity check satisfied);但如果你想自己实现核心是节点更新两步校验节点更新和变量节点更新。我写了一个简化但完整可跑的BP译码器用的是LLR域的min-sum近似复杂度比标准SPA低性能损失在可接受范围内function decodedBits bpDecode(receivedLLR, H, maxIter) % receivedLLR: 接收LLR向量 % H: 校验矩阵 % maxIter: 最大迭代次数 % 返回硬判决比特 [m, n] size(H); % 建立连接关系 [rowIdx, colIdx] find(H); numEdges length(rowIdx); % 初始化变量节点到校验节点消息 v2c zeros(numEdges, 1); % 初始化信道信息 channelL receivedLLR(:); for iter 1:maxIter % 校验节点更新 c2v zeros(numEdges, 1); % 按照校验节点分组 for check 1:m edgeIndices find(rowIdx check); if isempty(edgeIndices) continue; end % 计算符号乘积和绝对值最小 signs sign(v2c(edgeIndices)); signProduct prod(signs); absVals abs(v2c(edgeIndices)); minVal min(absVals); % 更新每一条边 for idx edgeIndices c2v(idx) signProduct * sign(v2c(idx)) * minVal; end end % 变量节点更新 for var 1:n edgeIndices find(colIdx var); if isempty(edgeIndices) continue; end totalL channelL(var) sum(c2v(edgeIndices)); for idx edgeIndices v2c(idx) totalL - c2v(idx); end end % 判决计算后验LLR posteriorL channelL; for var 1:n edgeIndices find(colIdx var); posteriorL(var) channelL(var) sum(c2v(edgeIndices)); end decodedBits double(posteriorL 0); % 检查校验 syndrome mod(H * decodedBits, 2); if sum(syndrome) 0 break; end end end这段代码的性能其实已经不错了但注意我用的是min-sum近似真正的SPA算法在校验节点更新时要用到双曲正切函数计算量更大性能略好。如果你的课题对性能极限有要求建议在标准BP和min-sum之间都做一次对比。3.3 校验环节的实现校验是LDPC仿真里最容易忽略但又最重要的环节。校验的本质是验证接收端解码出的码字是否满足校验方程syndrome mod(H * decodedBits, 2); if all(syndrome 0) % 校验通过 else % 校验失败 end如果syndrome全零说明译码结果满足所有校验关系。但这里有个关键点syndrome全零只是表示这个码字是合法码字并不代表它一定和发送端一致。如果信道噪声把某个码字变成了另一个合法码字校验同样通过但信息比特已经错了。这是LDPC本身的“不可检错误”任何纠错码都存在只是概率极低。在仿真统计误码率时需要区分两个指标误码率BER和误帧率FER或BLER。BER是比特错误比例FER是帧错误比例。实际统计代码如下% 统计BER和FER numFrameErrors 0; numBitErrors 0; for frame 1:numFrames % ... 发送、接收、译码 ... bitErr sum(decodedBits ~ data); numBitErrors numBitErrors bitErr; if bitErr 0 numFrameErrors numFrameErrors 1; end end ber numBitErrors / (numFrames * infoLen); fer numFrameErrors / numFrames;校验环节还有一层深意在校验通过即停止迭代。这是很多实际系统降低译码延迟的关键手段。LDPC译码器每迭代一次都要消耗时间和功耗如果第5次迭代就满足所有校验方程那后续45次迭代完全没必要。Matlab的comm.LDPCDecoder也默认支持这个机制通过IterationTerminationCondition参数控制。我强烈建议不管是仿真还是实际系统都把这个条件打开不看白不看地省一半以上计算量。4. 完整仿真脚本与结果分析4.1 一版可以直接跑的仿真脚本理论讲完给出一版完整可跑的脚本。这个脚本整合了编码、AWGN信道、BP译码和校验统计可以直接复制到Matlab里运行%% LDPC编码、解码、校验全链路仿真 clear; clc; close all; % 参数设置 n 504; % 码长 j 3; % 列重 k 6; % 行重需要n能被k整除 infoLen n - n*j/k; maxIter 30; % 步骤1构造H矩阵Gallager随机构造 H gallagerH(n, j, k); H removeCycle4(H); % 步骤2获取生成矩阵G G getGeneratorMatrix(H); % 步骤3仿真误码率 EbN0dBList 1:0.5:4; numFrames 100; berList zeros(size(EbN0dBList)); ferList zeros(size(EbN0dBList)); for idx 1:length(EbN0dBList) EbN0dB EbN0dBList(idx); totalBitErr 0; totalFrameErr 0; for frame 1:numFrames % 随机信息比特 data randi([0 1], infoLen, 1); % 编码 codeword ldpcEncode(data, G); % 过信道得到LLR rxLLR transmitAWGN(codeword, EbN0dB, infoLen/n); % BP译码 decodedBits bpDecode(rxLLR, H, maxIter); % 校验 syndrome mod(H * decodedBits, 2); if sum(syndrome) ~ 0 % 译码失败校验不通过 totalFrameErr totalFrameErr 1; totalBitErr totalBitErr sum(decodedBits ~ data); else % 校验通过但可能仍有比特错误极小概率 totalBitErr totalBitErr sum(decodedBits ~ data); if sum(decodedBits ~ data) 0 totalFrameErr totalFrameErr 1; end end end berList(idx) totalBitErr / (numFrames * infoLen); ferList(idx) totalFrameErr / numFrames; fprintf(EbN0 %.1f dB, BER %.2e, FER %.2e\n, ... EbN0dB, berList(idx), ferList(idx)); end % 画图 figure; semilogy(EbN0dBList, berList, -o, LineWidth, 1.5); hold on; semilogy(EbN0dBList, ferList, -s, LineWidth, 1.5); grid on; xlabel(Eb/N0 (dB)); ylabel(误码率); legend(BER, FER);这个小脚本的问题在于码长短、迭代次数少、帧数少所以曲线抖动会比较大。跑完后你会看到BER曲线整体有下降趋势但还不够平滑。实际做论文时建议把帧数加到1000帧以上并且每个信噪比点跑多个随机种子取平均。4.2 误码率曲线怎么看LDPC的BER曲线通常有三个区域靠近左上角是错误平层区域这个区域误码率下降缓慢主要受短环和最小距离限制中间段的陡降区域就是“瀑布区”这是LDPC表现最好的地方信噪比增加一点点误码率就掉几个数量级右下角是错误地板区域如果H矩阵构造不好或者译码算法有缺陷曲线会在这里“卡住”怎么增加信噪比都不再下降。我见过很多人拿到曲线后只看一个信噪比点这是不对的。正确做法是先画出整条曲线观察瀑布区位置是否与理论预期一致。比如码长504、码率0.5的LDPC码在BPSK-AWGN信道下瀑布区中心大概在2到3dB之间。如果曲线在5dB以上才下降说明H矩阵构造或者译码实现有问题。4.3 迭代次数、帧长、量化位宽怎么选这几个参数之间是互相牵制的。迭代次数太少译码器还没收敛就停了性能差迭代次数太多仿真时间长而且超过一定次数后性能不再提升。我的经验是先用几个典型的信噪比点测试不同迭代次数下的BER找到那个“再多迭代也不明显变好”的拐点。对于码长500左右的码30到50次是常见区间码长60000以上的5G码可能40到80次才够。帧长方面仿真帧数不能太少。BER曲线在低误码率区域需要大量样本才能统计出可靠数字。比如你要测10的负5次方的误码率每个信噪比点至少需要10的6次方个比特样本也就是1000帧乘以每帧约500比特。帧数不足的结果就是曲线在低误码率区域上下乱跳无法判断真实性能。量化位宽主要影响的是定点仿真。浮点仿真不用纠结。如果你要做FPGA原型验证那要把LLR量化为固定位宽常见是4到6比特量化太粗会让BP译码性能明显恶化这个坑我在实际工程里踩过损失了大概0.3到0.5dB的增益。5. 常见问题与排查技巧实录5.1 H矩阵不满秩或编码后校验恒失败这是我自己动手写H矩阵时遇到最多的坑。H矩阵不是随便一个稀疏矩阵都能直接用它必须满足几个条件行数m小于列数n、前m列或者其他任意m列构成的子矩阵可逆、行与行之间线性无关。如果H矩阵不满秩那么生成矩阵G里会出现零行编码出来的码字会有校验位全是0的情况译码时校验大概率失败。快速判断方法用matlab的rank函数看一下GF(2)下的秩。注意Matlab默认的rank是实数域上的要手动在二进制域上计算。排错思路是先检查列重行重是否符合预期再看是否有零行零列最后做高斯消元测试。Gallager随机构造法一般不会满秩但如果n和k的比例选得不合理比如n j*k就可能出现失败。我把Gallager构造函数的参数检查做的严格一些提前报错省得后面猜谜。5.2 解码不收敛或误码率高如果译码器在最大迭代次数内一直无法通过校验通常有三个原因。第一个是H矩阵里有4环甚至更短的环导致消息传递时置信度重复计算。第二个是信道LLR计算错误比如码率没除、噪声方差算错LLR的幅度整体偏大或偏小译码器就很容易误判。第三个是BP译码本身在高信噪比下可能进入“伪收敛”也就是说校验方程明明不满足但消息更新陷入局部稳定状态不再变化。排查这类问题我有个习惯先用硬判决解调看信道误码率是否正常。如果信道误码率本身就高得离谱问题基本在链路模型如果信道误码率正常但译码输出还是错就检查校验矩阵和译码实现。一个非常实用的调试技巧是打印每次迭代后的syndrome重量。正常情况下syndrome的非零个数应该随着迭代逐渐减少最后变为0。如果它不降反升或者一直震荡说明有环在作怪。5.3 仿真速度太慢LDPC译码的复杂度主要在迭代过程Matlab的循环效率又是出了名的低。我刚开始手写BP译码器时码长1000的码跑100帧就要好几分钟根本没法扫整条曲线。后来做了三处优化速度提升非常明显第一个优化是矩阵向量化。把节点更新从for循环改成矩阵运算。比如变量节点更新可以用sparse矩阵的稀疏乘法一次完成所有边的更新不需要逐节点循环。第二个优化是减少求模运算给H矩阵和所有中间变量用logical类型避免不必要的double类型转换。第三个优化是使用parfor并行跑不同信噪比点多核CPU能直接缩放加速比。另外如果你只是要最终结果不追求自己实现算法直接用comm.LDPCDecoder对象是最快的方式。它底层是C代码编译的MEX文件速度比自己写的Matlab循环快一个数量级以上。5.4 常见问题速查表下面的表格是我总结的LDPC仿真排错速查表遇到问题时可以直接对照问题现象可能原因排查手段编码后校验和不为0生成矩阵G构造错误或H与G不满足正交关系检查G是否满足 mod(H*G.,2) 全零译码器永不收敛校验矩阵存在4环检查Tanner图环分布用PEG算法重新构造高信噪比下BER不再下降错误地板效应受短环或最小距离限制增大码长改进H矩阵构造使用标准矩阵低信噪比下BER比理论差很多LLR计算错误核对EbN0换算核查噪声方差公式两种译码器结果不一致算法实现有差异先用无噪声信道测试再逐步加噪声仿真曲线抖动剧烈帧数太少增加帧数使用多个随机种子取平均实际操作中最浪费时间的问题往往是“莫名奇妙的1比特错误”。这种问题大概率出在mod运算上。Matlab的mod对负数、小数都会给出非预期结果而LDPC全是0/1运算建议所有二进制运算先统一double后再mod或者用bitand和bitshift配合逻辑运算避免浮点误差混入。最后再说两句踩过的坑做LDPC仿真这段时间我最大的体会是这个项目难的不是算法本身而是链路里任何一个环节的小数点错误都会让最终结果彻底失效。比如EbN0和EsN0的换算少除了一个码率曲线整体偏移0.5dB又比如H矩阵和G矩阵的正交性验证很多教材上没写但实际编码前必须做一次不然错都不知道错在哪。所以我建议每个入门的朋友拿到任何一套LDPC代码先跑一次无噪声的完整链路。如果无噪声时都不能做到编码、译码、校验完全一致那后面加噪声得到的任何数字都没有意义。这个习惯帮我避开了至少五次方向性错误你值得拥有。最后再分享一个小技巧仿真结果落地之后可以顺手把不同迭代次数下的收敛曲线画出来横轴是迭代次数纵轴是syndrome重量。这条曲线能直观展示译码器“从乱到稳”的过程写报告或者做PPT的时候特别好用而且能帮你快速判断当前码的收敛速度是否合理。本文还有配套的精品资源点击获取
返回列表