尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

BCH-Polar级联:让极化码从理论走向工程的后悔药

BCH-Polar级联:让极化码从理论走向工程的后悔药 简介一套聚焦信道编码核心算法的MATLAB源码包适合通信工程专业学生、编码算法初学者以及需要快速搭建仿真环境的工程师。资源以BCH码、极化码、汉明码、卷积码和循环码为主线覆盖编码、译码、性能评估的完整学习链路帮助读者理解伽罗华域纠错、信道极化、维特比回溯与香农极限等关键概念。包内含48个m脚本文件压缩包大小仅34KB文件均为可直接运行的MATLAB源码既有各编码器主程序也有码率、迭代次数、交织长度对误码性能影响的测试脚本以及维特比译码、香农极限分析等配套模块。通过运行这些代码可以直观对比不同信道编码方案的纠错能力与频谱效率观察极化码在高信噪比下的性能优势也能通过香农极限脚本判断当前编码方式距离理论下限的差距适合用于课程实验、毕业设计或编码技术预研。目前已有373人学习下载源码结构清晰、注释简要便于按需修改参数或扩展新算法模块能够在现有基础上快速生成自定义仿真实验。1. 极化码理论最优工程上却离不开 BCH 和 CRC 这瓶“后悔药”做物理层的人大概都经历过这种场面论文里极化码Polar code的性能曲线贴着香农限仿佛信道编码的终点就是它可真把极化码搬进自己的链路里码长一短、码率一高误码平台就告诉你什么叫理论与现实的落差。问题不在极化码的极化机制而在于极化码本身不负责“修正自己选错的信息比特”——它需要外援。BCH 码和循环冗余校验CRC就是这么被拉回战场的。5G NR 里最终落地的也不是纯极化码而是 CRC 辅助的极化码CA-PolarBCH 作为外码与极化码级联的方案同样活跃在深空通信和部分卫星链路线。这篇笔记就围绕“信道编码里的 BCH-Polar 级联”展开讲清楚极化码为什么需要 BCH 和循环冗余帮忙级联怎么搭、参数怎么定、仿真怎么跑、坑在哪。适合正在做 5G 物理层、卫星数传、或者想在水声/无人机图传链路里试极化码的工程师读目标就一个让你看完能动手搭出自己的级联验证平台。2. 让极化码从理论走上工程CA-Polar 与 BCH 级联到底解决什么2.1 极化码的信道极化为什么短码性能总是差一口气极化码的核心思想是把 N 个独立信道通过递归的“信道合并 信道分裂”操作变成 N 个极化程度不同的比特信道。一部分信道容量趋近 1好信道一部分趋近 0坏信道。发端把信息比特放在好信道上坏信道放冻结比特收发双方约定好的固定值这就是极化码能逼近香农限的根源。但极化有个物理约束极化效应要到位码长必须足够长。N1024 时极化已经比较充分N256 时某些中等可靠信道还处在“半好半坏”的模糊区。工程场景下码长常常被帧结构和时延卡死比如 5G NR 控制信道的极化码码长从 32 到 1024 不等短码场景非常多。短码极化不充分直接后果是即使选最好的 K 个信道放信息比特仍然有部分比特信道错误概率远高于理论预期。这时候信息比特错了极化码译码器SC 或 SCL未必纠得回来。工程上的做法是引入外码。BCH 码作为外码把极化码内码译码后的残余错误再纠正一层CRC 则扮演“选码器”——SCL 译码器在多个候选路径里挑一条最像正确码字的路径靠什么判断靠 CRC 校验。路径列表里只有一条能通过 CRC 校验就选它如果多条通过选路径度量最好的那条。这就是 CA-PolarCRC-Aided Polar名字的由来。所以极化码不是不要外码而是短码工况下必须靠 BCH 或 CRC 这类“后悔药”来兜底。2.2 BCH 码在级联里的定位纠错、检错与早期终止BCH 码是 1960 年提出的循环码家族成员线性分组码出身有严谨的代数学结构。它能做到一件事对于任意正整数 m 和纠错能力 t构造出码长 n2^m-1、能纠正 t 个错误的 BCH 码。工程里常用的缩短 BCH 码是把标准本原 BCH 码的信息位缩短得到的以适应帧长需求。在级联结构里BCH 通常做外码放在极化码之前发端维。极化码译码输出一串比特先经过 BCH 译码器纠正残余错误再交给上层。为什么不用 LDPC 或 Turbo 做外码因为 BCH 的译码延迟极低、实现简单移位寄存器就能搞定编码且纠突发错误之外的随机错误能力与极化码残错分布匹配良好。极化码经过 SC 译码后的错误往往是随机分布的少量比特错误正是 BCH 最擅长处理的场景。BCH 还有一个被低估的作用检错和译码提前终止。级联链路里如果 BCH 译码失败错误个数超出 t说明这一帧已经救不回来接收端可以直接发起重传请求不必等整帧数据全部译完。这在高吞吐链路里能省下可观的无效计算。另一个作用是“避免错误扩散”——BCH 译码失败时能明确上报译码失败而不是像卷积码那样默默输出错误比特。注极化码做内码、BCH 做外码的级联结构本质是把“概率译码”和“代数译码”两种思路叠在一起。前者擅长逼近容量后者擅长清理残余错误。两者各自干自己最擅长的事。2.3 循环冗余在级联里另一个身份CRC 不只是校验热词里反复出现“循环冗余检查”和“err:23 数据错误循环冗余检查”大家熟悉的场景是解压文件时报错。但在信道编码语境下循环冗余和极化码组合成了 CA-Polar 方案发端在 K 个信息比特后面附加 L 位 CRC一并做极化码编码收端 SCL 译码器维护 L 条候选译码路径最后用 CRC 从多条候选中选出正确路径。这个“用 CRC 选路径”的动作让极化码在短码下的性能直接提升了一个量级。5G NR 的上下行控制信道用的就是 CA-Polar下行 DCI 用 24 位 CRC上行 UCI 根据 payload 大小选 11 位或 6 位 CRC。这解释了为什么热词里“极化码”和“循环冗余”频繁绑定出现——在 5G 标准语境下它俩是一套组合不是两个独立的编码方案。BCH 和循环冗余同时出现在级联链路里也不冲突BCH 做外码做前向纠错CRC 做路径选择/检错。把两者分开理解级联结构就一目了然。3. 在 5G NR 参数下搭 BCH-Polar 级联码率、CRC 长度与信道配置3.1 5G NR 的极化码参数从 K 到 E 的一张表工程实现级联第一步是把参数定清楚。5G NR 里极化码的输入输出定义如下K 是信息比特长度包含 CRCE 是速率匹配后的输出比特长度N 是母码长度必须是 2 的幂。上行控制信息 UCI 和下行控制信息 DCI 的极化码配置不完全相同但核心流程一致。参数场景典型值说明KUCI/DCI 信息长度12~140 bit已含 CRC 位CRC 长度上行 UCI6 / 11 bitK≤19 用 6K≥20 用 11CRC 长度下行 DCI24 bit生成多项式 gCRC24C(D)E编码后输出长度与物理资源匹配通常 ≤ NN母码长度2 的幂≥ E取最小的 2 幂 ≥ ELSCL 列表宽度4 / 8越大性能越好延迟越大自己做 BCH 级联实验时不需要完全照抄 5G 参数但建议把 CRC 长度和极化码母码长度对齐到近似的量级这样仿真结果能和 3GPP 公开曲线做横向对比。我一般用 K56、CRC8、BCH(127,113,2) 缩短到适配 K64E 从 128 到 512 扫几组覆盖码率从 1/2 到 1/8。3.2 用 Python 把极化码构造和 BCH 外码粘成一个验证链路明确的极化码构造方法很多最容易理解的是密度进化DE和高斯近似GA。工程仿真里常用高斯近似构造因为运算量小、精度够。下面给一个最小可跑的链路BCH 编码 → 极化码编码 → AWGN 信道 → SC 译码 → BCH 译码。import numpy as np from math import log2 def bch_encode(info_bits, gx): BCH编码信息位左移n-k位后对生成多项式做模2除法余数作为校验位 n_k len(gx) - 1 msg np.array(info_bits [0]*n_k, dtypeint) # 模2除法等效于循环移位寄存器 for i in range(len(info_bits)): if msg[i] 1: for j in range(len(gx)): msg[ij] ^ gx[j] return msg[:len(info_bits)] msg[len(info_bits):] def polar_encode(u, n): 极化码编码u * G_n, G_n G_2 的克罗内克幂 g2 np.array([[1,0],[1,1]]) gn np.array([1]) for _ in range(int(log2(n))): gn np.kron(g2, gn) # 克罗内克积逐级扩展 return (u gn) % 2 # 例BCH(15,7,2) 生成多项式 gx x^8 x^7 x^6 x^4 1 gx [1,1,1,0,1,0,0,0,1] info np.random.randint(0,2,7) bch_codeword bch_encode(list(info), gx) print(BCH codeword:, bch_codeword)这段代码里两个函数分属两级bch_encode用生成多项式逐位异或求余得到校验位polar_encode通过克罗内克积构造生成矩阵G_n再把输入向量乘上去。注意polar_encode的矩阵构造是“每迭代一次矩阵翻倍”这里我用的是与 3GPP 一致的生成矩阵定义不是文献里常见的另一种排序两者差在比特顺序仿真时按同一套顺序收发即可。3.3 批量扫参数用 shell 脚本跑 for 循环拿误码曲线一次仿真只能拿一个点误码曲线需要批量跑。我习惯把 Python 仿真写成一个可接受命令行参数的脚本然后用 shell 的 for 循环批量提交。#!/bin/bash # 批量扫 Eb/N0 参数从 1dB 到 5dB步进 0.5dB for ebn0 in 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0; do python3 polar_bch_link.py --ebn0 ${ebn0} --n 256 --k 56 \ --crc_len 8 --sc_list 4 --frames 5000 results.log done # 结果追加写入 results.log之后用 grep 抽出各行 grep EB/N0 results.log | awk {print $2, $4, $6} ber_curve.dat脚本逻辑是外层 for 循环变量ebn0逐点传给 Python 链路链路每次跑 5000 帧统计误码率输出按行追加到results.log。最后用grep和awk把需要的列抽出来画图用。5000 帧在码率 1/2、N256 时SC 译码大概跑几分钟一个点整条曲线半小时内能出。如果想跑 SCL 列表宽度 8把--sc_list 4改成--sc_list 8时间约翻倍性能在高 SNR 区大约能再挤 0.2~0.3dB。提示批量仿真最容易出的问题是帧数不一致导致曲线抖动。固定帧数不要用“跑到 N 个错误就停”的早期终止除非你评估的指标本身需要这种终止条件。4. 复现 CA-Polar 的最小实现生成矩阵、速率匹配与 LLR 计算4.1 极化码生成矩阵构造从 N8 开始手推很多第一次接触极化码的人直接跳到 N1024结果生成矩阵写成什么样、比特顺序怎么排全凭复制粘贴。出问题后根本没法定位。我的建议是先从 N8 手推把矩阵结构看明白再上大码长。# 用递推方式构造 G_N并从高斯近似得到信道可靠性排序 def polar_gen_matrix(n): fn np.array([1]) for _ in range(int(log2(n))): fn np.kron(np.array([[1,0],[1,1]]), fn) return fn % 2 N 8 G8 polar_gen_matrix(N) print(G_8:\n, G8) # 用简单的高斯近似计算每个比特信道的可靠性 def gaussian_approx(llr_mean, n): 输入初始llr均值返回n个比特信道的可靠性估计 z np.array([llr_mean]*n) for _ in range(int(log2(n))): # 合并层较好信道取 2*phi_inv(1-(1-phi(z1))*(1-phi(z2))) # 简化起见用文献常见近似 phi(z) exp(0.0564*z^2 - 0.4856*z) pass # 完整实现需要phi函数代码里G_8矩阵的行代表比特信道从上到下可靠性递增经过比特置换后。实际工程中用的可靠性排序表可以用密度进化离线算好存成静态表运行时查表不现场算。这样实现简单且和标准方案一致。手工算可靠性时用高斯近似公式逐个递推注意公式里phi(z)的近似表达式在低 LLR 区误差较大做定量仿真时建议用查表或离线密度进化。4.2 速率匹配与比特选择E 小于 N 时的打孔与重复极化码编码永远输出 N 个比特但物理信道分配的 RE 数未必等于 N。E N 时要从 N 里挑 E 个发出去这就是速率匹配。5G NR 里用的是基于可靠性的速率匹配优先发送可靠信道上的编码比特低可靠位置被打孔不发送。这个选择直接影响性能——如果打孔打到了高可靠位置等效于把好信道丢了误码率立刻恶化。def rate_match(coded_bits, reliability_order, E): 按可靠性从高到低选E个编码比特发送 # reliability_order: 按可靠性降序排列的索引数组 # 5G NR标准做法是交织后按位置发送这里简化为直接选 selected_idx sorted(range(len(coded_bits)), keylambda i: reliability_order[i], reverseTrue) return coded_bits[selected_idx[:E]]这个函数做的事情很简单但关键发送端选择的 E 个比特在接收端译码时对应位置的 LLR 才有值其余位置 LLR 置 0相当于该信道完全不可靠。SC/SCL 译码器会把 0 LLR 当作等概率信息处理配合冻结比特的先验知识照样能译。注意标准里的速率匹配还有交织器交织的主要目的是把打孔位置尽量打散避免连续丢比特。4.3 译码端 LLRBCH 硬判决之前为什么要先软信息SC 译码器内部全部在 LLR 域工作最后一步才做硬判决。如果直接对接收信号先硬判决再送进译码器约等于把软信息全扔了性能损失 2dB 以上。def bpsk_llr(received, noise_var): BPSK调制下计算对数似然比 L 2*y/σ^2 return 2.0 * received / noise_var # 假设接收符号 y (1/-1) n, n ~ N(0, noise_var) y np.array([1.2, -0.7, 0.3, -1.5]) sigma2 0.8 llr bpsk_llr(y, sigma2) print(LLR:, llr) # 硬判决: LLR 0 - bit0, LLR 0 - bit1与映射有关LLR 的计算公式在上面的代码里噪声方差从信道估计那里拿。注意如果调制不是 BPSK比如 QPSK 每个符号携带两个比特需要把符号 LLR 拆成比特级软信息再送进译码器。BCH 译码器工作在硬判决域所以极化码译码器输出硬比特塞给 BCH 之前中间不需要保留软信息。这也是级联设计的巧妙之处内码用软译码尽量把比特判对外码用代数手段清理残错两级各吃各的饭。5. 极化码BCH 联调的避坑指南我从误码平台里捞回来的五个教训5.1 发端 CRC 算进去了收端却把 CRC 比特当信息比特误码率直接失控现象仿真曲线在高 SNR 区出现平台误码率降到 1e-3 后不再下降。原因SCL 译码器路径选择依赖 CRC 校验如果收端把 CRC 位当作普通信息位参与译码路径度量计算CRC 就失去了“选路径”的功能路径选错后错误会直接传到上层。本质是收发两端对“哪些位是 CRC、哪些位是信息”的约定不一致。解决在仿真代码里把 K 拆成 K_info L_crc 两段收发两端用同一个掩码区分。这条看起来简单但我至少见到三个项目在这里翻过车。排查方法是打印收发两端的信息位索引逐一比对。5.2 BCH 译码器纠错越界t2 却把 3 个错当 2 个纠剩余误码不降反升现象加上 BCH 外码后误码率在中低 SNR 区反而比不加还差。原因BCH 译码器在错误个数超过 t 时如果仍然强行纠错会把错误的校验子映射到一个错误的“校正子”等于把更多比特改错。这种现象叫“误纠”miscorrection。它比不纠更危险因为外码把好比特改坏了。解决BCH 译码器必须做好“拒绝”逻辑——当校验子计算出的错误位置数超过 t 时直接放弃这一帧纠错保持原始比特不动并上报译码失败。在级联方案里此时可以让上层的 CRC 决定是否重传。注意BCH 的误纠概率随着 t 和码长增大而上升自己实现译码器时最容易漏掉的就是这个分支。开源的 Berlekamp-Massey 算法大多实现了该逻辑但用错版本或裁剪过度时容易丢。建议单独写测试向量验证误纠分支。5.3 速率匹配打孔位置影响极化权重盲目打孔丢掉高可靠信道现象N512、E256 时打孔率 50%仿真误码率比理论上限差 1.5dB 以上。原因速率匹配如果直接删末尾 256 个比特但末尾恰好包含多个高可靠比特信道的编码输出等效于发端把好信道丢了。标准里的速率匹配顺序是经过精心设计的自己随意打孔会破坏极化码最核心的“好信道优先”原则。解决打孔位置必须依据可靠性排序表倒序选择——优先打掉可靠性最低的比特位置。或者直接用 5G NR 标准定义的速率匹配顺序它对每个 (N, E) 组合都预先算好了打孔/重复位置直接查表。5.4 先硬判决再进 BCH软判决拒绝次数比想象中高块错误率抬高 0.3dB现象BCH 外码感觉没起作用纠错成功次数很少块错误率曲线比参考值差 0.3~0.5dB。原因级联链路设计里内码译码出的软信息没有传递给外码。BCH 是代数译码器只吃硬比特但内码 SC 译码输出的 LLR 置信度信息其实是可用的——比如 LLR 绝对值很小的比特它的硬判决可能是错的。如果有软信息可以先按 LLR 排序尝试翻转低置信度的几个比特后再做 BCH 校验这种“软判决辅助 BCH”能降低 BCH 的译码失败率。解决工程里常见做法是内码译码后输出 LLR 和硬比特BCH 译码失败时把 LLR 绝对值最小的 5~10 个比特逐一翻转重试。实现成本低块错误率通常能再降 0.2~0.3dB。5.5 冻结比特集没有按可靠性排序仿真结果比标称差 1dB 还找不到原因现象中低信噪比区域误码率尚可高信噪比区域比参考曲线差 1dB 左右且 BCH 外码几乎完全无效。原因冻结比特的选择依赖可靠性排序表。如果用的是自算的高斯近似结果中高可靠信道排序错误率较高而排序错误恰好发生在中等可靠信道区域——也就是高 SNR 时仍会出错的区域。我遇到过一次是因为用错了排序表的比特顺序自然序 vs. 比特逆序整张表错了一半位置。解决直接用 3GPP TS 38.212 里给出的 N 最大为 1024 的可靠性排序表先跑通再考虑替换。自算排序表只用于验证理解不要用于正式仿真。还有一点冻结比特的值必须收发双方一致通常全 0但某些实现里会用已知伪随机序列填充以避免全 0 码字带来的 DC 分量问题。6. 把级联链路推进到实现级浮点转定点、迭代与标准差异验证6.1 浮点仿真转定点量化噪声比信道噪声更早到误码率仿真通过后下一步一定是定点化。极化码的 SC/SCL 译码器内部全是 LLR 加减比较定点化相对友好。我一般先把 LLR 量化为 Q6.26 位整数位、2 位小数位观察误码率曲线是否损失超过 0.1dB然后逐步缩位。关键是 LLR 饱和处理——BPSK 解调出来的 LLR 极端情况下可能很大不做饱和直接截断等效于给每个比特叠加随机噪声。def quantize_llr(llr, int_bits, frac_bits): 定点化LLR饱和截断 max_val 2**(int_bits-1) - 1.0/2**frac_bits llr np.clip(llr, -max_val, max_val) scale 2**frac_bits return np.round(llr * scale) / scale量化测试要配合固定随机种子保证同一批噪声样本在浮点和定点下都跑过逐比特比对译码输出首次出现不一致的位置就是量化误差最早溢出的位置。6.2 和标准参考结果对拍比误码率更重要的是码字对不对完成级联链路后最后一步是验证“发端码字是否正确”。很多团队只盯误码率曲线结果曲线看着没问题但发端的码字排列和标准不一致导致与别家设备对接时全部失败。我习惯做一个独立的码字级验证构造一组固定输入比特经过自己的编码器得到码字再和参考实现如 MATLAB 5G Toolbox 或开源库的码字逐比特比对不一致就逐级排查。# 码字级对拍脚本输入相同比特序列输出逐比特比对结果 python3 code_compare.py --ref_codeword ref.bin --my_codeword my.bin # 期望输出Bit mismatch count: 0对码字通过后再跑误码率曲线此时曲线才有意义。我养成的习惯是每次改参数都跑一遍码字对拍防止新改动引入比特顺序错误。这个习惯救过我至少两次其中一次是把信息位和冻结位的顺序搞反了误码率几乎没变但对接直接失败。最后说一句经验级联方案的工程落地难点从来不在某个编码器的数学推导而在收发两侧的比特级约定——CRC 放哪、打孔按什么序、冻结比特用什么填充、BCH 失败后是纠是弃。把这些边界条件定死并写成自动化验证脚本误码率曲线自然就对了。希望帮到你。本文还有配套的精品资源点击获取
返回列表