简介:本资源是一份面向电子信息工程、计算机及数学专业本科生的极化码译码算法实践材料,聚焦高斯信道下CA-SCL(级联列表连续消除)译码的MATLAB实现,解决课程设计、期末大作业与毕业设计中极化码仿真实验与性能评估的实际需求。压缩包共17个文件,含11个核心MATLAB脚本(如polarEnc.m、polarDec.m、likelihood_rate.m等,覆盖编码、AWGN信道建模、CRC校验、路径度量与列表管理)、2张结果图(png)、2个MATLAB图形文件(fig)及1个C++加速模块(likelihood_rate_c.cpp)与对应MEX编译文件,整体仅60KB,轻量易部署。已有80人学习下载,代码采用参数化设计,变量命名规范、注释详尽,支持MATLAB 2014a至2024a多版本直接运行,并附带可开箱即用的案例数据,便于快速验证不同列表长度、码长与SNR下的译码性能。读者可完整掌握CA-SCL算法的树搜索逻辑、路径保留机制与度量更新策略,获得可调试、可拓展、可教学的工程级参考实现。
1. 极化码在高斯信道下用 CA-SCL 译码,不是调个函数就能跑通的仿真任务
很多人下载到极化码在高斯信道下CA——SCL译码算法 matlab代码.rar后直接解压、运行main.m,结果报错Undefined function 'polarDecodeSCL'或SNR not defined,甚至误以为是 MATLAB 版本问题——其实根本原因在于:CA-SCL 不是 MATLAB 通信工具箱内置函数,它是一套需手动实现的、带循环冗余校验(CRC)辅助的串行抵消列表译码流程,其性能高度依赖于码长 N、列表大小 L、CRC 长度和高斯信道建模方式的协同配置。这类仿真面向的是通信系统设计者、研究生课程作业或 5G NR 物理层协议验证场景,目标不是“跑起来”,而是复现论文中 Eb/N0 与 BLER 的典型拐点曲线(如 10⁻³ 级误块率对应 SNR=2.8 dB)。你若刚接触极化码,会发现传统 SCL 已难满足 5G 控制信道对低时延高可靠的要求;而 CA-SCL 通过 CRC 提前剪枝,在同等列表尺寸下可将译码复杂度降低 30%~50%,这是它被选为 5G eMBB 控制信道标准译码方案的核心依据。本文不讲抽象定义,只聚焦如何用原生 MATLAB(R2018b 及以上)从零搭建可验证、可调参、可 debug 的 CA-SCL 流程。
2. 极化码构造与 CA-SCL 译码器的四层结构拆解:为什么必须手写核心模块
CA-SCL(CRC-Aided Successive Cancellation List)不是黑盒,它由四个逻辑耦合紧密的子系统构成:极化码构造(Polar Construction)、CRC 编码嵌入、SCL 树搜索 + CRC 辅助剪枝、高斯信道建模与软信息生成。MATLAB 通信工具箱虽提供polarDesign和polarEncode,但不支持将 CRC 多项式直接注入冻结比特位置,也不提供带 CRC 校验的 SCL 节点淘汰逻辑——这意味着你无法仅靠comm.PolarDecoder实现 CA-SCL。常见误区是把 CRC 当作后处理:先跑完 SCL 得到 L 个候选码字,再逐个校验 CRC。这会导致冗余计算,且无法利用 CRC 在树搜索中途淘汰无效路径,失去“辅助”意义。正确做法是:在每一级比特判决时,对当前所有活跃路径同步计算 CRC 校验位,并在扩展子节点前剔除 CRC 失败路径。下面分步说明各模块的 MATLAB 实现要点。
2.1 极化码构造:用 Bhattacharyya 参数法确定冻结比特,而非查表
极化码性能取决于冻结比特(frozen bits)位置选择。MATLABpolarDesign默认使用 Gaussian Approximation(GA),但 GA 在短码长(N≤1024)下误差较大。更可靠的做法是基于 Bhattacharyya 参数(Z)迭代计算信道极化程度:
function frozenBits = polarDesignBhattacharyya(N, K, SNR) % N: 码长 (必须为2的幂), K: 信息比特数 % SNR: 信噪比(线性值,非dB),用于计算初始信道可靠性 sigma2 = 1 / (10^(SNR/10)); % 高斯信道噪声方差 Z0 = exp(-1/(2*sigma2)); % BPSK调制下初始Bhattacharyya参数 % 递归计算所有N个信道的Z_i(使用快速算法,避免2^N复杂度) Z = zeros(1, N); Z(1) = Z0; for n = 1:log2(N) len = 2^n; for i = 1:len/2 Z(i) = 2 * Z(i) - Z(i)^2; % Z^{(i)}_n = Z^{(2i-1)}_{n-1} ⊞ Z^{(2i)}_{n-1} Z(len/2+i) = Z(i)^2; % Z^{(i)}_n = Z^{(2i-1)}_{n-1} ⊛ Z^{(2i)}_{n-1} end end % 选取K个最小Z值对应位置作为信息比特,其余为冻结比特 [~, idx] = sort(Z); infoBits = sort(idx(1:K)); frozenBits = setdiff(1:N, infoBits); end注意:此函数返回的是1-based 索引的冻结比特位置向量(如
[1 2 4 5]),后续编码时需将这些位置置为 0。关键参数SNR必须与信道仿真段一致,否则冻结比特选择失准将导致整个译码性能坍塌。若用 GA 法(polarDesign(N,K,'Path','GA','SNR',SNR)),需确认 MATLAB 版本 ≥ R2020a,且 GA 在低 SNR 下易误判弱信道。
2.2 CRC 辅助嵌入:将 CRC 校验位插入信息比特末尾,再映射到极化码位置
CA-SCL 的“辅助”本质是让 CRC 校验参与路径筛选。标准做法是:先对 K-bit 信息比特u_info计算r-bit CRC(常用 CRC-11 或 CRC-6),拼接成(K+r)-bit 序列,再从中选出 K 个位置放入信息比特集,剩余 r 位作为“隐式冻结比特”参与 SCL 搜索。MATLAB 无内置 CRC 多项式快速计算,需手动实现:
function crcBits = calcCRC(data, poly, init, xorOut) % poly: CRC多项式系数向量,如CRC-11为[1 0 0 0 0 0 0 0 0 1 1](x^11+x^2+1) % data: 二进制行向量,长度任意 len = length(data); reg = init; % 移位寄存器初始值 for i = 1:len bitIn = xor(data(i), reg(end)); % 异或输入比特与最高位 reg = [bitIn, reg(1:end-1)]; % 移位 if bitIn reg = xor(reg, [poly, zeros(1, length(reg)-length(poly))]); end end crcBits = xor(reg, xorOut); % 输出异或 end % 示例:为K=64信息比特添加CRC-11 K = 64; r = 11; polyCRC11 = [1 0 0 0 0 0 0 0 0 1 1]; % x^11 + x^2 + 1 u_info = randi([0,1], 1, K); % 原始信息比特 crc = calcCRC(u_info, polyCRC11, zeros(1,11), zeros(1,11)); u_ca = [u_info, crc]; % (K+r)比特序列2.2.1 关键映射:将 u_ca 分配到极化码信息比特位置
设infoPos为polarDesignBhattacharyya返回的信息比特位置(长度 K+r),则需将u_ca的第 i 位填入u(infoPos(i))。注意:infoPos是升序排列的索引,u_ca前 K 位是原始信息,后 r 位是 CRC,因此:
u = zeros(1, N); % 初始化全零码字 u(infoPos(1:K)) = u_info; % 前K位放信息 u(infoPos(K+1:end)) = crc; % 后r位放CRC(这些位置在传统SCL中本应是冻结位) % frozenBits 中需排除 infoPos 所有元素,仅保留真正冻结位 trueFrozen = setdiff(frozenBits, infoPos);提示:此步骤确保 CRC 比特参与 SCL 树搜索,而非事后校验。若跳过此步,CA-SCL 退化为普通 SCL,性能损失可达 0.5 dB。
3. CA-SCL 核心译码器实现:列表管理、路径度量更新与 CRC 剪枝三步闭环
CA-SCL 的核心是维护一个大小为 L 的路径列表,每个路径包含:当前已判决比特序列u_hat、对应的 LLR 向量llr、路径度量PM(通常为累积 log-likelihood)、以及是否通过 CRC 校验的标志。译码过程按比特位置i=1:N顺序进行,对每个活跃路径执行“分裂-度量更新-CRC 剪枝”三步操作。MATLAB 中需用结构体数组高效管理路径,避免动态扩容损耗。
3.1 初始化路径列表与 LLR 生成
高斯信道下,接收信号y = x + n,其中x = 1-2*u(BPSK 映射),n ~ N(0, σ²)。LLR 计算公式为LLR_i = 4*y_i / σ²(假设等功率分配):
sigma2 = 1 / (10^(SNR/10)); llr = 4 * y ./ sigma2; % y为N维接收向量 paths(1).u_hat = []; % 初始空路径 paths(1).llr = llr; paths(1).PM = 0; paths(1).crcValid = false; L = 32; % 列表大小,典型值8/16/32/643.2 主循环:逐比特扩展与剪枝
对每个比特位置i(从 1 到 N),遍历当前所有路径,根据i是否在infoPos中决定是否分裂:
for i = 1:N newPaths = []; for p = 1:length(paths) if ismember(i, infoPos) % 信息比特位:必须分裂为0和1两个子路径 % 计算u_i=0和u_i=1的LLR更新(使用SC递归公式) [llr0, llr1] = updateLLR(paths(p).llr, i, N); % 路径度量更新:PM = PM + log(P(y|u_hat)) pm0 = paths(p).PM + logProb(llr0, 0); % u_i=0的似然 pm1 = paths(p).PM + logProb(llr1, 1); % u_i=1的似然 % 创建新路径 newPath0.u_hat = [paths(p).u_hat, 0]; newPath0.llr = llr0; newPath0.PM = pm0; newPath0.crcValid = false; newPath1.u_hat = [paths(p).u_hat, 1]; newPath1.llr = llr1; newPath1.PM = pm1; newPath1.crcValid = false; newPaths = [newPaths, newPath0, newPath1]; else % 冻结比特位:只保留u_i=0路径(或按冻结值) % 更新LLR,不增加路径数 newLLR = updateLLR(paths(p).llr, i, N); paths(p).llr = newLLR; paths(p).u_hat = [paths(p).u_hat, 0]; % 冻结位固定为0 end end % 若有新路径(即信息比特分裂),执行剪枝 if ~isempty(newPaths) % 按PM降序排序,保留前L个 [~, idx] = sort([newPaths.PM], 'descend'); paths = newPaths(idx(1:min(L, end))); % 对每个路径,若u_hat长度等于infoPos长度,执行CRC校验 for j = 1:length(paths) if length(paths(j).u_hat) == length(infoPos) % 提取infoPos对应位置的比特,组成u_ca_est u_ca_est = zeros(1, length(infoPos)); for k = 1:length(infoPos) pos = infoPos(k); if pos <= length(paths(j).u_hat) u_ca_est(k) = paths(j).u_hat(pos); end end % 计算估计CRC并与u_ca_est后r位比对 estCrc = calcCRC(u_ca_est(1:K), polyCRC11, zeros(1,11), zeros(1,11)); paths(j).crcValid = isequal(estCrc, u_ca_est(K+1:end)); if ~paths(j).crcValid paths(j).PM = -inf; % 使该路径在后续排序中被淘汰 end end end % 再次按PM排序并剪枝 [~, idx] = sort([paths.PM], 'descend'); paths = paths(idx(1:min(L, end))); end end3.2.1 LLR 更新函数:实现极化码的递归信道合成
updateLLR是极化码译码的数学核心,需按i的二进制表示确定递归层级:
function [llr0, llr1] = updateLLR(llr, i, N) % llr: 当前LLR向量(长度N) % i: 当前比特位置(1-based) % N: 码长 % 返回u_i=0和u_i=1对应的LLR更新值 % 此处简化:实际需按i的二进制位进行奇偶分解,此处用MATLAB内置polarLLR % (注:R2021b+提供polarLLR,但为兼容性,我们手写基础版) % 获取i的二进制表示(log2(N)位) bits = dec2bin(i-1, log2(N))-'0'; len = length(bits); % 从最高位开始递归分解 tempLLR = llr; for level = 1:len halfLen = N / (2^level); for seg = 0:(2^level-1) start = seg*halfLen*2 + 1; mid = start + halfLen - 1; endIdx = start + halfLen*2 - 1; if endIdx > N, break; end % 奇偶合成:llr_odd = f(llr_left, llr_right), llr_even = g(llr_left, llr_right) llrLeft = tempLLR(start:mid); llrRight = tempLLR(mid+1:endIdx); % f(x,y) = sign(x)*sign(y)*min(|x|,|y|) (approx) llrOdd = sign(llrLeft).*sign(llrRight).*min(abs(llrLeft), abs(llrRight)); % g(x,y) = x + y llrEven = llrLeft + llrRight; % 根据bits(level)决定输出哪一组 if bits(level) == 0 tempLLR(start:endIdx) = [llrEven, llrOdd]; else tempLLR(start:endIdx) = [llrOdd, llrEven]; end end end llr0 = tempLLR(i); llr1 = -tempLLR(i); % 对称信道下,u_i=1的LLR为负 end关键参数说明:
L(列表大小)是性能与复杂度的权衡点。L=8 适合教学演示;L=32 可逼近香农限 0.1 dB;L=64 在 N=1024 时内存占用超 2GB。polyCRC11必须与编码端完全一致,否则 CRC 剪枝失效。logProb函数需实现log(P(y|u)),高斯信道下为-0.5*(y - (1-2*u))^2/sigma2。
4. 高斯信道建模与 BLER 仿真:控制变量法跑出可信曲线
CA-SCL 的价值最终体现在 BLER(Block Error Rate)随 SNR 的变化曲线上。单纯跑一次SNR=2得到BLER=0.12毫无意义,必须采用蒙特卡洛方法,在每个 SNR 点上积累足够多的块错误统计(建议 ≥ 200 个错误事件),才能绘制平滑曲线。MATLAB 中需严格分离信道模型、编码、译码和统计模块。
4.1 可复现的高斯信道生成与 SNR 控制
避免使用awgn()函数,因其内部 SNR 定义可能与理论公式不一致。手动控制噪声方差更可靠:
function [y, sigma2] = generateGaussianChannel(x, SNR_dB) % x: 发送符号向量(BPSK: +1/-1) % SNR_dB: 信噪比(dB),定义为 Eb/N0,Eb=1(单位能量) SNR_lin = 10^(SNR_dB/10); sigma2 = 1 / SNR_lin; % 因为Eb=1,故N0=sigma2 n = sqrt(sigma2) * randn(size(x)); % 零均值高斯噪声 y = x + n; end % 主仿真循环示例 SNR_vec = 1:0.5:4; % SNR范围,步长0.5dB BLER_vec = zeros(size(SNR_vec)); numTrials = 10000; % 每个SNR点最大试验次数 minErrors = 200; % 目标错误数 for idx = 1:length(SNR_vec) SNR = SNR_vec(idx); errors = 0; totalBlocks = 0; while errors < minErrors && totalBlocks < numTrials totalBlocks = totalBlocks + 1; % 编码 u_info = randi([0,1], 1, K); crc = calcCRC(u_info, polyCRC11, zeros(1,11), zeros(1,11)); u_ca = [u_info, crc]; u = zeros(1, N); u(infoPos(1:K)) = u_info; u(infoPos(K+1:end)) = crc; x = 1 - 2*polarEncode(u, frozenBits, N); % 自定义polarEncode % 信道 [y, ~] = generateGaussianChannel(x, SNR); % 译码 u_hat = caSCLDecode(y, frozenBits, infoPos, polyCRC11, K, N, L); % 比较 if ~isequal(u_hat(infoPos), u_ca) errors = errors + 1; end end BLER_vec(idx) = errors / totalBlocks; end4.2 三组对照实验:验证 CA-SCL 的增益来源
为证明 CA-SCL 有效性,必须与基线对比。以下三组实验缺一不可,且需在同一SNR_vec和numTrials下运行:
| 实验组 | 关键配置 | 预期 BLER 差异 | 作用 |
|---|---|---|---|
| SCL | L=32, 无 CRC 剪枝,u_hat仅按 PM 选最佳路径 | 比 CA-SCL 高 0.3–0.6 dB | 证明 CRC 辅助必要性 |
| CA-SCL | L=32, CRC 在树搜索中实时剪枝 | 基准曲线 | 本文目标实现 |
| CA-SCL-L8 | L=8, 其他同 CA-SCL | 与 SCL-L32 接近,但复杂度低 4× | 证明列表压缩可行性 |
提示:绘图时用
semilogy(SNR_vec, BLER_vec, '-o'),横轴 SNR,纵轴 BLER 对数坐标。若 CA-SCL 曲线未在 SNR=2.8 dB 处穿过 10⁻³,检查frozenBits是否用相同 SNR 构造、polyCRC11是否匹配、infoPos映射是否正确。常见错误是polarEncode函数未正确处理frozenBits,导致发送端与接收端码结构不一致。
5. 调参与排错实战:解决.rar包中 MATLAB 代码的五大高频故障
下载的matlab代码.rar通常包含main.m、polarEncode.m、caSCLDecode.m等文件,但因 MATLAB 版本差异、路径缺失或参数硬编码,90% 的用户首次运行失败。以下是五个最常触发Error using ...的故障点及修复命令,无需修改算法逻辑,仅调整环境与参数。
5.1 故障1:Undefined function 'polarEncode'—— 缺少自定义编码函数
.rar包中常遗漏polarEncode.m,或函数名与调用不一致(如encodePolar.m)。不要试图用comm.PolarEncoder替代,因其不支持 CA 结构。修复方法:
# 在MATLAB命令窗口执行,创建最小可用编码函数 edit polarEncode.m然后粘贴以下标准实现(支持冻结比特和任意 N):
function x = polarEncode(u, frozenBits, N) % u: 信息比特向量(长度K) % frozenBits: 冻结比特位置(1-based索引) % N: 码长(2的幂) u_full = zeros(1, N); u_full(setdiff(1:N, frozenBits)) = u; % 将u填入非冻结位 % 极化码编码:G = F^{\otimes n},F=[1 0;1 1] G = hadamard(N) > 0; % 简化:Walsh-Hadamard矩阵的二值化 x = mod(u_full * G, 2); x = 1 - 2*x; % BPSK映射 end5.2 故障2:Index exceeds matrix dimensions——infoPos长度与u_info不匹配
错误源于polarDesign返回的infoPos长度 ≠ K+r。检查polarDesignBhattacharyya函数中sort(Z)后取idx(1:K)是否应为idx(1:K+r)。修正命令:
% 在polarDesignBhattacharyya.m中,将 infoBits = sort(idx(1:K)); % 改为 infoBits = sort(idx(1:K+r)); % r为CRC长度,需作为输入参数传入5.3 故障3:Out of memory—— 列表大小 L 设置过大
当N=1024, L=64时,单路径u_hat占用 1024×8 字节,64 条路径即 512 KB,但 LLR 向量(1024×8×64=512 KB)和路径结构体开销易超 2 GB。立即执行:
% 在main.m开头添加内存限制 maxL = floor(1e9 / (N * 8 * 2)); % 按1GB内存估算 L = min(L, maxL); % 自动下调L fprintf('Adjusted list size L to %d due to memory limit.\n', L);5.4 故障4:BLER stuck at 0.5—— 高斯信道 SNR 单位混淆
.rar包中常将SNR直接当作10^(SNR/10)使用,导致噪声过大。验证方法:在generateGaussianChannel中加入诊断:
% 在函数内添加 fprintf('SNR_dB=%.1f -> sigma2=%.4f (should be ~0.25 at SNR=6dB)\n', SNR_dB, sigma2);若sigma2远大于 1,说明 SNR 输入是线性值而非 dB,需在调用处改为SNR_dB = 10*log10(SNR_lin)。
5.5 故障5:CRC always fails—— 多项式初始值与异或输出不匹配
calcCRC函数中init和xorOut设为zeros(1,r)是常见错误。标准 CRC-11 初始值为ones(1,11),输出异或为ones(1,11)。修正:
% 将 calcCRC 调用改为 crc = calcCRC(u_info, polyCRC11, ones(1,11), ones(1,11));终极验证技巧:在
caSCLDecode.m最终返回前,添加fprintf('Best path CRC valid: %d\n', paths(1).crcValid);。若始终为0,说明 CRC 计算或映射存在系统性错误;若为1但u_hat错误,则问题在 LLR 更新或路径度量。此时可将u_hat和u_ca打印出来逐位比对,定位第一个差异位——这通常是infoPos映射偏移 1 位所致(MATLAB 索引从 1 开始,而部分代码按 0 开始实现)。
本文还有配套的精品资源,点击获取