ARTICLE DETAIL

资讯详情

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

非二进制LDPC码的EXIT分析:解码阈值与度分布设计

非二进制LDPC码的EXIT分析:解码阈值与度分布设计 简介面向通信与编码理论研究者及高年级学生这份压缩包提供了一套完整的非二进制LDPC码EXIT特性分析MATLAB代码。非二进制LDPC码在GF(2^m)域上运行相比二进制版本具有更优性能而EXIT图是评估迭代解码潜力的关键手段。包内覆盖非二进制互信息函数计算、校验矩阵构造、消息传递/贝叶斯解码、EXIT图绘制及不同信噪比与迭代次数下的性能仿真闭环可直接修改参数运行便于复现论文结果或开展编码设计实验。资源共34个文件其中30个m脚本为算法主体2个mat数据文件提供互信息与校验节点/变量节点多项式查找表2个txt文档为使用说明。整个压缩包仅362KB体量轻、模块划分清晰。目前已有227人学习下载适合需要深入理解非二进制LDPC码迭代解码过程、或从事5G/6G及存储信道编码研究的入门与进阶用户。1. 非二进制LDPC的EXIT分析解码阈值怎么被两条曲线算出来做非二进制LDPC码设计的前几周我一直在跟二进制工具较劲把校验矩阵换到GF(4)上之后EXIT图怎么画都不对两条曲线永远差一截。后来才意识到问题不在译码器而在互信息的度量方式——非二进制系统里消息不是单个LLR而是一组符号概率EXIT曲线必须重新定义。这份MATLAB包就是干这件事的它用经验估计加多项式逼近两条路线把变量节点VND和校验节点CND的外信息曲线算出来两条曲线的交点直接给出迭代译码阈值之后还能用线性规划反推度分布。适合正在设计NB-LDPC码、需要快速验证某个度分布能不能收敛或者要给论文补非二进制EXIT图的人。码率、域阶数、节点度都能调跑通main.m就能出图这在同类工具里算很省事的。2. GF(2^m)域运算与LLR映射加减乘除、概率卷积和符号对数化非二进制LDPC和二进制最大的区别是所有校验方程都在GF(q)上做。二进制里校验节点就是一个异或门翻到GF(4)、GF(8)之后校验矩阵里的非零元素都是域元素消息更新也变成域上的运算。所以拆这套代码的第一步是先把GFqAdd、GFqMul、GFqConv这三个基础函数看明白。2.1 GFqAdd与GFqMul先确认你算的是域运算不是整数运算我见过不止一个新手把GF(4)乘法当整数乘法写结果整条EXIT曲线都是错的说不出原因。域上加法和二进制比特串的按位异或等价乘法则要靠本原元和对数表。包里GFqAdd、GFqMul就是干这个的核心逻辑如下。% GFqAdd.m —— GF(2^m) 加法对二进制表示按位异或 function c GFqAdd(a, b) % a, b 是 [0, 2^m-1] 的整数二进制位即 GF(2^m) 元素 c bitxor(a, b); end加法这一步没有玄学bitxor就结束了。注意入参是整数0到q-1不是比特向量用bitxor刚好落在域上。传进来之前先确认a, b 2^m越界了运算结果没有意义。% GFqMul.m —— 用对数-指数表做 GF(2^m) 乘法 function c GFqMul(a, b, expTab, logTab) % expTab(k) alpha^(k-1)logTab(alpha^(k-1)) k-1 % alpha 是本原元expTab/logTab 在初始化脚本里预先生成 if a 0 || b 0 c 0; % 0 没有对数必须单独处理 return; end k mod(logTab(a) logTab(b), length(expTab)); c expTab(k 1); end乘法用查表logTab(a)logTab(b)对应指数相加模q-1是因为非零元构成循环群alpha^(q-1)1。两个表在初始化时生成一次之后所有节点更新都能复用。注意MATLAB索引从1开始所以取expTab(k1)。除法不用单独写函数乘上逆元就行逆元也能用同一张表查。2.2 GFqConv校验节点更新里最核心的概率卷积GFqConv这个名字容易误读成域上的多项式卷积实际上在EXIT计算里它的用途是概率域卷积两条独立的域元素消息它们的和域加法意义下的分布。校验节点做更新时dc-1条输入消息的概率分布要两两卷积得到输出消息。直白点说这就是非二进制版本的校验约束传播。% GFqConv.m —— GF(q) 上的概率卷积Z X Y域加法的分布 function pz GFqConv(px, py, q) % px, py: 长度为 q 的概率向量分别对应 X、Y 的符号分布 % pz: Z X Y 的分布X、Y 独立 pz zeros(1, q); for x 0 : q - 1 for y 0 : q - 1 z bitxor(x, y); % 域加法等价于按位异或 pz(z 1) pz(z 1) px(x 1) * py(y 1); end end end逻辑上就是穷举所有符号对把概率乘起来累加到和的符号上。复杂度是O(q²)GF(4)、GF(8)时完全够用真跑到GF(64)以上就得换加群上的快速卷积Walsh-Hadamard变换但包里默认域阶数不会那么大。调用前确认px、py各自求和为1否则卷积结果会走偏EXIT曲线在低SNR段会莫名下坠。2.3 LLR与unLLR符号概率和对数域之间的双向切换消息在变量节点和校验节点之间来回传但两边的坐标系不一样。变量节点更新时消息用LLR相加校验节点做卷积时又必须回到概率域。所以包里的LLR.m和unLLR.m负责来回切换这也是非二进制实现比二进制麻烦的核心点。% LLR.m —— q 维概率向量转 (q-1) 维 LLR 向量参考符号取 0 function L LLR(p) q length(p); L zeros(1, q - 1); for a 1 : q - 1 L(a) log(p(a 1) / p(1)); % 参考符号 0 对应 p(1) end end % unLLR.m —— LLR 向量还原成完整 q 维概率用于校验节点卷积 function p unLLR(L) q length(L) 1; p0 1 / (1 sum(exp(L))); % 由归一化条件反解 P(0) p zeros(1, q); p(1) p0; for a 1 : q - 1 p(a 1) p0 * exp(L(a)); end endLLR的参考符号固定取0向量长度是q-1而不是q因为归一化条件把最后一个自由度消掉了。unLLR是逆变换p0由归一化条件解出来再逐个指数还原。注意这两个函数在网络里被反复调用每次迭代VND进CND之前都要走一遍unLLR出来再走LLR。参考符号一旦在初始化时改过后面所有J_Empirical的估计都要跟着改否则后面算出来的互信息系统性偏低。3. EXIT曲线的三根支柱J函数、经验估计、VND与CND两条曲线拿到域运算之后下一步是理解EXIT曲线本身。非二进制EXIT图里横轴纵轴都是互信息单位是比特/符号取值范围0到log2(q)。包里的J函数族、经验估计函数和两条节点曲线分别对应理论互信息、实测互信息、整条EXIT曲线怎么拼三件事。3.1 J函数族辨析Calc_J、Calc_J_R和带Minus后缀的差异二进制EXIT图里有个经典的J函数把高斯分布的方差映射成互信息。非二进制沿用这个概念但消息从一维LLR变成q-1维向量理论J函数不再有闭式解所以包里有Calc_J、Calc_J_R和一堆带Minus后缀的变体。命名规律是这样的函数含义使用场景Calc_J.m通用J函数LLR向量到互信息的映射变量节点输出的理论估计Calc_J_R.m信道侧J函数由信道SNR换算信道互信息初始化阶段的信道消息Calc_J_Minus.m扣除先验后的外信息互信息VND外信息曲线Calc_J_R_Minus.m信道相关的扣除先验版本CND曲线初始化参考带Minus后缀的版本做的是同一件事EXIT图画的是外信息不是后验信息。每次迭代里消息的互信息包含一部分来自自身先验的贡献外信息要把这部分扣掉否则曲线会整体偏高交点位置失真。这组函数常见做法是用经验数据拟合多项式系数把系数存到JPols.mat里运行时直接代入求值省得每次都跑蒙特卡洛。3.2 经验互信息怎么算GaussianSamples、InitialSamples与J_Empirical理论J函数只能覆盖部分场景真正要出可发表的图还是得靠经验估计。包里的GaussianSamples.m在给定方差下生成高斯LLR采样InitialSamples.m生成信道初始消息的采样J_Empirical.m则做最后一步从采样估计互信息。% J_Empirical.m —— 用采样估计 LLR 向量与发送符号之间的互信息 function I J_Empirical(L, X) % L: N x (q-1) 的 LLR 采样矩阵X: N x 1 的发送符号范围 0..q-1 q size(L, 2) 1; N size(L, 1); logLik zeros(N, q); logLik(:, 2:end) L; % 参考符号 0 对应的 LLR 恒为 0 logLik logLik - max(logLik, [], 2); % 每行减最大值防 exp 溢出 p exp(logLik); p p ./ sum(p, 2); idx sub2ind([N, q], (1:N), X(:) 1); pX p(idx); I mean(log2(q .* pX)); % 均匀先验下的经验互信息 end核心是mean(log2(q .* pX))发送符号均匀分布时互信息等于log2(q)减去条件熵而条件熵的采样估计就是每个样本被正确识别概率的对数平均。减去行最大值那一步是数值稳定处理q大一点或者SNR高了之后exp(L)会爆减掉最大值再指数结果不变但不会溢出。采样数N建议不低于1e5低于这个数曲线在低SNR段抖动非常明显。3.3 VND与CND的经验曲线Calc_VND_Empirical和Calc_CND_Empirical的算法骨架两条曲线的算法方向正好相反。变量节点外信息在LLR域做加法各条输入消息的LLR直接相加所以Calc_VND_Empirical的核心是生成采样、按度相加、再估计输出互信息。校验节点则要回到概率域做卷积Calc_CND_Empirical会重得多。% Calc_CND_Empirical.m —— 校验节点外信息的经验估计简化骨架 function Ie Calc_CND_Empirical(IaVals, dc, q, numSamples) % IaVals: 输入互信息扫描点dc: 校验节点度q: 域阶数 Ie zeros(size(IaVals)); for i 1 : length(IaVals) sigma J_inv(IaVals(i)); % 互信息反推高斯方差 Ls GaussianSamples(numSamples, dc - 1, sigma); % dc-1 条输入消息 Xs randi([0, q - 1], numSamples, 1); % 发送符号均匀分布 Le zeros(numSamples, q - 1); for n 1 : numSamples p unLLR(Ls(n, 1, :)); % 先回到概率域 for j 2 : dc - 1 p GFqConv(p, unLLR(Ls(n, j, :)), q); % 逐条卷积 end Le(n, :) LLR(p); % 再转回 LLR 域 end Ie(i) J_Empirical(Le, Xs); end enddc-1条输入消息是因为算外信息要扣掉目标边自己那条。先unLLR回概率域做dc-2次卷积再LLR转出去估计互信息这套流程在包里的实际实现完全一致只是采样组织方式更省内存。复杂度是O(numSamples * dc * q^2)所以跑GF(16)、dc8以上会明显变慢这也是为什么包里要做Compute_CND_Approximation多项式逼近——逼近版本把整条曲线秒算出来经验版本留作最终验证。4. 从main.m跑通整套流程参数设置、调用链与结果落盘函数看明白了就该把整包跑起来。按我的习惯先看main.m怎么调再回头抠细节最后才信结果。4.1 main.m的调用链从参数到两条曲线的完整路径包内main.m的调用链大致如下顺序是固定的参数初始化 → 加载J函数多项式表 → 扫描SNR算VND曲线 → 扫描SNR算CND曲线 → 画图 → 存结果。下面是从主脚本里整理出来的核心段落。% main.m —— 非二进制 EXIT 主流程按包内脚本整理 clear; clc; % ---- 域参数与码参数 ---- q 4; % 域阶数 GF(2^m)q 2^m m log2(q); dv 3; % 变量节点平均度 dc 6; % 校验节点平均度 rate 1 - dv / dc; % 设计码率 % ---- 扫描的 SNR 区间与采样配置 ---- EbN0dB 0 : 0.2 : 5; snr EbN0dB 10 * log10(rate) 10 * log10(m); % Es/N0 换算 numSamples 1e5; Ia linspace(0, 0.999, 20); % ---- 先加载多项式表避免重复蒙特卡洛 ---- [JRPols, CNDPols] Load_J_Pols(JPols.mat); % ---- 逐点计算两条曲线 ---- for k 1 : length(snr) Ie_vnd(k, :) Calc_VND_Empirical(dv, q, snr(k), Ia, numSamples); Ie_cnd(k, :) Calc_CND_Empirical(dc, q, Ia, numSamples); end % ---- EXIT 图CND 曲线横轴翻转后与 VND 画在一起 ---- plot(Ia, Ie_vnd(end, :), b-, Ia, 1 - Ie_cnd(end, :), r--);snr那行是重点非二进制符号携带m个比特Es/N0和Eb/N0之间要乘码率再乘m漏了m整条曲线在横轴上平移交点阈值全错。CND曲线画图时横轴取1 - Ia这是EXIT图的约定因为校验节点的先验来自变量节点的外信息方向相反。Ia扫到0.999而不是1是为了避开J_inv(1)发散。4.2 参数怎么设q、度分布、SNR区间和采样点数的选择参数设置直接影响结果可信度我一般按下面这套经验值起步再根据具体码型调。参数建议起步值说明q4 或 8先跑小域验证流程确认无误再上16dv / dc3 / 6对应码率0.5EXIT图最容易看出交点EbN0dB0 ~ 5 dB步进0.2步进太大交点精度不够numSamples1e5低于5e4曲线毛刺明显Ia 扫描点20 ~ 30点太少曲线不平滑LP设计时要用50以上域阶数q和m要同步改q8对应m3snr换算里的m写错了会直接偏4.7 dB。度分布dv/dc决定码率EXIT设计的目标就是在给定码率下找能让两条曲线不相交的度分布参数区这两行是后面第6章LP设计的输入。采样点数翻倍能把曲线磨平但CND经验估计的时间也翻倍先跑一遍确认流程没问题再调大做正式结果。4.3 结果落盘与复用JPols.mat和CND_and_JR_Pols.mat的存取经验估计慢所以包里有Store_J_Pols.m、Store_CND_and_JR_Data_To_File.m两个存表函数把拟合好的多项式系数写到.mat文件里下次启动直接加载。Load_J_Pols.m和Load_CND_and_JR_Data_From_File.m负责读回来。% 存表第一次跑完经验曲线后把系数写盘 Store_J_Pols(JPols.mat, JRPols); Store_CND_and_JR_Data_To_File(CND_and_JR_Pols.mat, CNDPols, JRPols); % 读表后面所有实验直接从文件加载 [JRPols, CNDPols] Load_J_Pols(JPols.mat); [CNDPols, JRPols] Load_CND_and_JR_Data_From_File(CND_and_JR_Pols.mat);这里有个惯用原则经验曲线只在参数没变的时候可信。一旦改了q、dv/dc或SNR范围必须重新跑经验估计再存表否则旧系数会给你一条看似光滑但完全错误的曲线。包里的Readme.txt对两个.mat文件的格式有说明第一次用之前先读一遍。另外压缩包里的www.pudn.com.txt只是来源站的说明页跟技术流程无关可以忽略。5. 避坑指南非二进制EXIT仿真里最常见的五个翻车现场这套代码我前前后后跑了一个多月踩过的坑基本集中在下面五个地方。每条都是血泪经验按现象 → 原因 → 解决写方便你遇到问题时直接对号入座。5.1 现象CND曲线不光滑全是毛刺这是个高频翻车点。Calc_CND_Empirical里每个互信息扫描点要做numSamples次dc-1维卷积本地机器上开了并行才勉强能跑。毛刺的根源是采样数不够——特别是dc较大时校验约束命中小概率符号的次数很少经验分布尾部全是噪声。原因numSamples低于5e4或者q比较大但采样数没跟着涨。解决把numSamples提到2e5以上急着出图就用Compute_CND_Approximation.m的解析逼近版经验版本留到最终验证。我再补一句毛刺也可能是Ia扫描点不均匀导致的改成linspace(0, 0.999, 30)会比随机散布稳定得多。5.2 现象EXIT曲线在高SNR端封顶在0.8左右上不去曲线不收敛到log2(q)停在中间某个值。最先怀疑的不应该是算法而是你自己的数据管线。原因最常见的是LLR.m里参考符号没统一。一处用符号0做参考另一处默认用符号q-1两段代码算出来的互信息差出固定一块。解决全局搜一遍LLR(调用点确认所有参考符号都是0unLLR的还原逻辑跟LLR的参考符号必须成对。另一个可能原因是发送符号采样不均匀randi出来的分布偏了经验互信息被低估。5.3 现象GF(16)及以上结果明显偏离仿真域阶数一高EXIT预测的解码阈值和实际BP译码仿真对不上差值能到0.5 dB以上。原因高斯近似在高阶域上失效。二进制和高斯假设在小域上还算凑合q到16、32之后LLR向量的真实分布跟高斯差太远理论J函数和Compute_J_Approximation的多项式拟合都救不回来。解决q 16时强制走J_Empirical和Calc_VND_Empirical的经验路线放弃解析逼近论文图里同时标注理论曲线和经验曲线审稿人最吃这套。5.4 现象线性规划设计出的度分布码率对不上用LambdaLinProgDesign跑出来的度分布代回码率公式发现和目标码率差出一截。原因码率公式用边视角度分布算rate 1 - dv / dc里dv (sum(lambda_d / d))^{-1}。很多人直接把lambda_d代进平均度公式少除了一个d码率自然错。解决先验算sum(lambda_d)是否为1再用dv 1 / sum(lambda_d ./ d)算平均度最后核对rate。这个坑我踩了一整天最后是拿一个已知的规则码度分布做单元测试才定位到的。5.5 现象fzero求交点失败两条线怎么看都不相交两条曲线明明该相交Myfzero报错或者交点落在(0,0)原点。原因两条曲线是离散采样后线性插值的如果Ia网格没包住交点所在区间插值出来就找不到零点。另外CND曲线横轴翻转后不再单调fzero在非单调区间上会乱跑。解决先在粗网格上扫描Ie_vnd - (1 - Ie_cnd)的符号变化找到交点所在区间再用Myfzero在这个小区间上精确定位。Myfzero的容差我一般设置到1e-6太小了在高SNR段收敛不动。6. 进阶用线性规划反推度分布并用EXIT交点验证设计跑通两条曲线只是第一步包里的LambdaLinProgDesign.m和RhoLinProgDesign.m才是设计工具。6.1 把EXIT收敛条件写成LP约束给定校验节点度分布和目标码率LambdaLinProgDesign用线性规划反推变量节点度分布。核心思路是把两条曲线不相交写成一组线性不等式对每个互信息x变量节点的输出曲线必须压在翻转CND曲线之上。这一步的关键在于每个度d对应的VND输出互信息J(sqrt((d-1)*Jinv(x)^2 sigma_ch^2))只跟x和d有关可以预先算成系数矩阵约束就变成对lambda的线性不等式。% LambdaLinProgDesign.m 的约束构造核心简化 Dmax 15; % 最大变量节点度 x linspace(0.05, 0.95, 100); % 互信息采样网格 sigmaCh 0.8; % 由目标 SNR 换算 % 预计算每个度、每个网格点的 VND 输出互信息 for i 1 : length(x) for d 2 : Dmax vnd(i, d - 1) J(sqrt((d - 1) * (Jinv(x(i)))^2 sigmaCh^2)); end end % LP目标是最小化平均变量节点度等价于最大化码率 f -1 ./ (2 : Dmax); % 目标系数取负linprog 求最小 Aeq ones(1, Dmax - 1); % sum(lambda) 1 beq 1; lb zeros(1, Dmax - 1); % 收敛约束vnd * lambda 1 - CND(1 - x)移项成 A*lambda b A -vnd; b -(1 - cndFlip(x)); % cndFlip 由 Calc_CND_Empirical 得到 lambda linprog(f, A, b, Aeq, beq, lb, []);注意cndFlip(x)要单独调一次Calc_CND_Empirical别在约束里重复跑蒙特卡洛否则慢到怀疑人生。d从2起扫是因为度1的变量节点没有校验约束能力一般直接禁止。跑完lambda之后用包里的CompleteLambdas.m把缺失的度补全并归一化得到可直接用的度分布。RhoLinProgDesign的道理一样只是把约束和变量对调两者交替迭代几次就能收敛到一组互相匹配的度分布。6.2 用BP译码验证EXIT预测的阈值设计完度分布最后一步是验证。我的验证流程固定两步先在EXIT图上读交点SNR再跑一遍完整的非二进制BP译码看瀑布区位置。两者差在0.2 dB以内就算合格。包里Expected_log.m是辅助验证的好工具它统计每轮迭代消息的期望对数似然增长如果译码在EXIT预测的阈值上方0.1 dB就开始收敛说明设计没问题如果在阈值下方还能收敛说明EXIT曲线本身偏悲观了回去查CND经验采样的点数。这套流程走完你对这个度分布为什么能行就有了闭环证据。从那以后我每次换q或改度分布都强制先确认三件事参考符号统一、采样点数不低于1e5、CND缓存文件版本对得上再谈设计结果。希望帮到你。本文还有配套的精品资源点击获取
返回列表