信道编码基础:线性分组码、循环码、卷积码、Turbo码、LDPC码
各位同学好,今天我们来聊聊信道编码。说实话,这玩意儿是通信系统的"保命符"。没有它,你的数据在空中飞一圈,回来可能就面目全非了。
我刚开始做通信系统那会儿,总觉得信道编码是理论家的玩具。直到有一次,我在一个物联网项目里,设备在强干扰环境下死活连不上——加了编码之后,问题迎刃而解。嗯,从那以后我再也不敢小看它了。
核心思想:信道编码的本质,就是在发送的信息里加入冗余,让接收端能发现甚至纠正传输中发生的错误。说白了,就是用带宽换可靠性。
1. 线性分组码:最基础的纠错方案
线性分组码,我个人觉得是入门最好的选择。它把每k个信息比特,映射成n个编码比特,记作(n,k)码。为什么叫"线性"?因为任意两个码字的和(模2加)还是码字。
你想想看,这有什么好处?意味着我们可以用矩阵运算来搞定编码和解码,硬件实现特别方便。
汉明码是最经典的线性分组码。比如(7,4)汉明码,4个信息位加3个校验位,能纠正1个错误。我在一个卫星通信项目里用过它——信道条件还不错,偶尔蹦个单比特错,汉明码刚好够用。
// (7,4)汉明码编码示例(C语言风格)
// 输入:4位信息位 d[0..3]
// 输出:7位码字 c[0..6]
void hamming_74_encode(int d[4], int c[7]) {
c[2] = d[0]; // 信息位
c[4] = d[1];
c[5] = d[2];
c[6] = d[3];
c[0] = d[0] ^ d[1] ^ d[3]; // 校验位
c[1] = d[0] ^ d[2] ^ d[3];
c[3] = d[1] ^ d[2] ^ d[3];
}
小提示:实际项目中,我建议用查表法实现编码,比实时计算快得多。特别是FPGA实现时,一个LUT就搞定了。
2. 循环码:多项式运算的优雅
循环码是线性分组码的一个子集,但多了一个漂亮的性质:码字循环移位后还是码字。这玩意儿用多项式来表示特别自然——编码就是多项式乘法,解码就是多项式除法。
CRC(循环冗余校验)是大家最熟悉的循环码应用。以太网、USB、WiFi,到处都有它的身影。不过要注意,CRC主要用于检错,不是纠错。
BCH码就不一样了,它能纠多个错误。我记得有一次做存储系统的纠错设计,需要纠正3个随机错误,BCH码就是我的首选。它的代数结构很漂亮,纠错能力可以精确设计。
| 编码类型 | 纠错能力 | 典型应用 | 硬件复杂度 |
|---|---|---|---|
| CRC-32 | 检错(不纠错) | 以太网帧 | 低 |
| BCH(63,51) | 纠正2个错误 | 存储系统 | 中 |
| RS(255,223) | 纠正16个符号错误 | 光盘、QR码 | 高 |
注意:循环码的生成多项式选择很关键。选错了,纠错能力会大打折扣。我曾经吃过这个亏——选了个本原多项式,结果发现和硬件除法器不匹配,折腾了两天才搞定。
3. 卷积码:有记忆的编码
线性分组码是"无记忆"的——每个码字只依赖当前的信息块。卷积码不一样,它引入了状态,当前输出不仅和当前输入有关,还和之前几个输入有关。
这听起来复杂,但换来的是更好的纠错性能。卷积码常用Viterbi译码,说白了就是找一条最可能的路径。我在3G手机基带芯片里做过卷积码的硬件实现,Viterbi译码器占了不小的面积。
// 卷积码编码器示例(约束长度K=3,码率1/2)
// 生成多项式:g0 = 7(八进制),g1 = 5(八进制)
void conv_encode(int input, int *state, int *out0, int *out1) {
int shift_reg = (*state << 1) | input;
*out0 = (shift_reg & 0b101) ^ ((shift_reg >> 1) & 0b101); // 多项式7
*out1 = (shift_reg & 0b111) ^ ((shift_reg >> 2) & 0b001); // 多项式5
*state = shift_reg & 0b011; // 更新状态
}
为什么卷积码在深空通信里用得那么多?因为它配合软判决译码,性能可以逼近香农限。不过代价是译码复杂度随约束长度指数增长——K=9时,Viterbi译码器就有256个状态了。
4. Turbo码:接近香农极限的突破
1993年,Turbo码的提出震惊了整个通信界。它第一次让实际系统如此接近香农极限。我记得当时看到那篇论文,第一反应是"这也能行?"
Turbo码的核心思想是迭代译码——两个分量译码器互相交换"软信息",像两个人讨论问题一样,越聊越确定。每次迭代,置信度就提高一点。
3G/4G移动通信里,Turbo码是标配。但说实话,它的译码延迟是个问题。迭代次数多了性能好,但延迟大;迭代少了又不够。我做过一个优化,在性能和延迟之间取了个平衡——8次迭代,再多了收益就不明显了。
关键参数:Turbo码的性能取决于交织器设计、迭代次数、分量码选择。一个好的交织器能让两个分量译码器的输入尽量不相关,这是性能的关键。
5. LDPC码:稀疏矩阵的魔力
LDPC码其实比Turbo码提出得更早(1960年代),但当时计算机能力不够,被埋没了。直到1990年代被重新发现,现在成了5G、WiFi 6、DVB-S2等标准的主力。
LDPC的校验矩阵是稀疏的——大部分元素是0,只有少量1。这个特性让它的译码可以用置信传播算法高效实现。说白了,就是在变量节点和校验节点之间来回传递概率信息。
我个人觉得LDPC比Turbo码更"优雅"——它的并行性更好,硬件实现更友好。在FPGA上做LDPC译码器,吞吐量可以做到几十Gbps。5G的LDPC码还支持速率自适应,同一个编码器可以输出不同码率,非常灵活。
// LDPC译码的置信传播算法(伪代码)
// 输入:接收到的软信息 L_ch
// 输出:译码后的比特
for (iter = 0; iter < MAX_ITER; iter++) {
// 步骤1:变量节点到校验节点
for (每个变量节点 v) {
for (每个相连的校验节点 c) {
L_v2c = L_ch[v] + sum(除c外所有相连校验节点的L_c2v);
}
}
// 步骤2:校验节点到变量节点
for (每个校验节点 c) {
for (每个相连的变量节点 v) {
L_c2v = 2 * atanh(prod(除v外所有相连变量节点的tanh(L_v2c/2)));
}
}
// 步骤3:硬判决
if (所有校验方程满足) break; // 提前退出
}
工程经验:LDPC译码的迭代次数一般设10-20次。我习惯加一个提前终止机制——如果所有校验方程都满足了,就立即停止,能省不少功耗。
小结:怎么选?
好了,五种编码都聊完了。你可能会问:实际项目里到底用哪个?
我的建议是这样的:
- 信道条件好、偶尔出错 → 汉明码或BCH码,简单够用
- 需要检错但不纠错 → CRC,轻量级
- 中等纠错需求、延迟敏感 → 卷积码,Viterbi译码延迟可控
- 逼近香农极限、延迟不敏感 → Turbo码,适合深空通信
- 高吞吐、高性能、5G标准 → LDPC码,现代通信的首选
记住一点:没有完美的编码,只有适合的编码。选型时要综合考虑性能、复杂度、延迟、功耗——这就是软硬件协同设计的精髓。
公众号:蓝海资料掘金营,微信deep3321