本文对数字通信技术中的信道编码及译码进行学习记录。
数字通信,就是把一切声音,图像,文字,都变成 0,1 这种二进制代码,这种转换过来的数据,我们可以称之为原始数据 bit 那么,这种原始的 bit,是否可以直接调制,转换成电磁波发送出去呢?答案是不可以,因为电磁波传输过程中,一定会存在于扰和噪声,从而产生差错
首先引用书上的定义对信道编码进行一下讲解:
信道编码(Channel Coding)的作用是进行差错控制。数字信号在传输过程中会受到噪声等影响而发生差错。为了减小差错,信道编码器对传输的信息码元按一定的规则加入保护成分(监督码元),组成所谓“抗干扰编码”。接收端的信道译码器按相应的逆规则进行解码,从中发现错误或纠正错误,提高通信系统的可靠性。
下面我们举个例子形象描述一下。
假设,我们把 “你好” 这两个字转换成 0,1 代码,比如用 00 和 01 分别代表你和好,在我们发送 00 时,因干扰原因,导致我们发送的数据变成了 01,那么我们发送的信息本来应该是“你”,而被接收端识别成了“好”,如下图所示:
我们为了抗干扰,就需要增加一个步骤,来让我们的数据具备“一定程度上的纠正干扰产生的差错的能力”,这个步骤就叫信道编码。
在进行信道编码时,需要增加冗余数据来达到抗干扰的效果,以下图为例,在运输花瓶时,为避免花瓶运输路途中破碎,我们增加一个泡沫箱子,再打包后通过快递进行发送,这里面的泡沫箱子及顺丰快递箱子就可以类似于冗余数据。
我们这里列举一些常用的冗余数据。
原始数据 100101100
增加的 1bit 位,为校验位,也就是冗余 bit
假设使用奇校验:1001011001
奇偶校验码只具备检错的能力,而不具备纠错能力
原始信息 1 或者 0
编码
当受到干扰导致错 1 位的时候,可以达到纠错的效果
当受到干扰导致错 2 位的时候,不可以达到纠错的效果
R = K / N R=K/N R=K/N
K K K:有用 bit 数据
N N N:编码后的 bit 数据
以前面的码为例,原始数据 100101100 共 9 bit,奇校验: 1001011001 共 10 bit,编码率 R = 9 / 10 = 0.9 R=9/10=0.9 R=9/10=0.9
重复码编码率 R = 1 / 3 R=1/3 R=1/3
编码率越低,包含的几余信息越多,纠错的能力越强,抗干扰的能力越强,传输的有效数据越小
4G 的信道编码包括卷积码和 turbo 码,5G 的信道编码包括 polar 和 ldpc 码。
卷积码一般使用( n , K , N n,K,N n,K,N)表示卷积编码器。
卷积码将 K K K 个信息码元编为 n n n 个码元时,这 n n n 个码元不仅与当前的 K K K 个信息有关,也与前面的 N − 1 N-1 N−1 段信息有关
维特比译码是根据接收序列,在网格图上找出一条与接收序列汉明距离最小的一种算法
汉明距离 Hamming:两个码组对应码位上具有不同二进制码元的位数,为两码组的距离,简称码距
举个例子:
码 1:000
码 2:101
这两个码的码距为 2
下面我们继续举个例子:
注意:上面接收序列由于干扰导致出现了差错,现在我们看一下维特比译码是如何纠错的
每一种序列,都是网格图上的一条路径,0 用实线,1 用虚线
因此,最后解码序列为:111 110 010 100,达到了纠错的效果
如果更复杂的,可能会出现有两个汉明距离,那么我们就会随机选作为它的译码,导致出现错了,这也就是为什么维特比译码叫做概率译码
在进行卷积编码的过程中,使用的函数是 convenc()
函数和 vitdec()
函数,同时需要 poly2trellis()
函数。
先看 poly2trellis()
函数,用来生成卷积编码所需要的网表。ploy2trellis 函数有两种调用形式:
trellis=poly2trellis(ConstraintLength,CodeGenerator);
trellis=poly2trellis(ConstraintLength,CodeGenerator,...FeedbackConnection)
后者应用于有负反馈的情形
这个函数相当于定义了编码器中寄存器的结构,一般来说,在查看卷积编码的相关资料中都是类似于(2,1,3)这种结构,但是这个函数只需要两个参数:
ConstraintLength
参数代表约束长度CodeGenerator
代码生成器,指定为八进制数的K
乘N
矩阵、多项式字符向量的K
乘N
单元阵列或K
乘N
字符串阵列。CodeGenerator
为编码器的 K
个输入比特流中的每一个指定 N
个输出连接。通俗理解这两个参数的含义和作用,首先约束长度决定了再译码时候的回溯深度的大小,根据不同的编码速率有不同的关系,技术文档中对此有介绍:
根据设定的 n
,k
,和 L
值来确定,如对于 (2,1,3)
结构类型的卷积编码器,其中 rate = k/n = 1/2
,对应第一种计算方式,那么回溯深度为 5*(L-1)
,结果为10,在进行译码的时候利用这个值对齐或者截断,这个在下面使用过程中会有。
回到函数本身,如设计 (2,1,7)
类型的编码器,可以用:
L = 7;
trellis = poly2trellis(L,[171 133]);
其中的第二个参数是 8 进制数据的表现形式,对应二进制为[111_1111, 101_1011]
。可以理解为进入1bit
数据,输出2bit
数据,所以这个是2维的,同时这个7bit
每一位代表寄存器的抽头,可以根据这个画出编码器的连接形式。
ploy2treliis 顾名思义:多项式 ploy 到网格图 trellis
卷积码的生成多项式可以由一系列多项式描述,我们将多项式转化为 trellis 结构,这种结构又可以作为 matalb 中线形卷积编码函数 convenc 和或者其解码(如 Viterbi 解码函数 vitdec)的输入。
==========================================================
那么 CodeGenerator 就是[23,35,0; 0,5,13]
运行
trellis=poly2trellis([5,4],[23,35,0;0,05,13])
输出如下:
trellis =
包含以下字段的 struct:
numInputSymbols: 4
numOutputSymbols: 8
numStates: 128
nextStates: [128×4 double]
outputs: [128×4 double]
>> trellis.nextStates(1:5,:)
ans =
0 64 8 72
0 64 8 72
1 65 9 73
1 65 9 73
2 66 10 74
上面我们列出了nextStates的 1-5 行
运行
trellis = poly2trellis(5,[37 33],37)
输出如下:
trellis =
包含以下字段的 struct:
numInputSymbols: 2
numOutputSymbols: 4
numStates: 16
nextStates: [16×2 double]
outputs: [16×2 double]
结构体中的变量含义与第一节一致。
再看 convenc() 函数
codedout = convenc(msg,trellis)
codedout = convenc(msg,trellis,puncpat)
简单使用卷积编码就用这个第1种调用方式就可以了,其中
msg
参数是需要编码的信息,这个需要输入0,1二进制数据。trellis
参数就是上面函数生成的结果,传输到这里。在使用时,需要注意的是 msg
数据的长度要是k
的整数倍,同时利用上面的约束长度,计算回溯深度,利用回溯深度在信息后面添加0
。
最后看译码函数
decodedout = vitdec(codedin,trellis,tbdepth,opmode,dectype)
decodedout = vitdec(codedin,trellis,tbdepth,opmode,'soft',nsdec)
decodedout = vitdec(codedin,trellis,tbdepth,opmode,dectype,puncpat)
译码函数的参数相对校多,主要是需要设置模式:
codedin
参数是需要译码的结果,模式选择硬判决的时候必须输入是整数,模式选择软判决的时候可以输入小数。trellis
参数与编码函数的一致。tbdepth
这个参数参数就是回溯深度,前面利用约束长度计算的结果。opmode
这个参数有三个不同的选项,分别为cont
,term
和trunc
。dectype
参数用来设置是硬解码hard
还是软解码soft
,硬解码输入的数据都是0
和1
,软解码可以说输入小数。有一点需要注意的是,对于term
和trunk
模式,回溯深度tbdepth
必须是一个正整数,并且小于或等于输入编码中的输入符号数,说白了就是在编码前的信息msg
长度就得大于等于回溯深度,要不然不够译码的。
这三个参数的差别,term
模式下没有延时,就是译码的第一个数据就是编码的第一个数据。trunc
参数也没有延时,cont
参数有延时,是回溯深度。还有差别就是连续编码的区别。
利用(2,1,7)
结构的形式进行卷积编译码,如下是测试代码,选择cont
和hard
模式。
close all;
clear;
clc;
msg_source = [randi([0 1],1,50),zeros([1,30])]; % 这行代码生成一个长度为80的随机二进制消息序列。它使用randi函数在0和1之间随机生成50个比特,并在后面添加30个零。
% 这些变量定义了卷积码的编码率。在这个例子中,n表示输出比特数,k表示输入比特数,因此编码率为1/2。
n = 2;
k = 1;
rate = k/n; % rate为 1/2
% 这些变量定义了卷积码的约束长度和回溯深度。在这个例子中,约束长度为7,回溯深度为5*(7-1)=30。
L = 7;
tblen = 5*(L-1); % 回溯深度
trellis = poly2trellis(L,[171 133]); % 使用poly2trellis函数生成一个约束长度为7的卷积码的Trellis结构。生成多项式为[171 133],定义了卷积码的生成过程。
code_data = convenc(msg_source,trellis); % 使用convenc函数对消息序列进行卷积码编码。它将msg_source输入到卷积码编码器中,使用之前生成的Trellis结构,得到编码后的数据序列code_data。
zero=zeros(1,2*tblen); % 生成一个长度为2*tblen的零向量。
code_data=[code_data(:,1:end),zero]; % 在编码数据的末尾添加了零,以增加解码过程的缓冲区大小
d = vitdec(code_data,trellis,tblen,'cont','hard'); % 使用vitdec函数对编码数据进行卷积码译码。它使用之前生成的Trellis结构和回溯深度,采用硬判决方式进行译码,得到译码后的数据序列d。
d = d(tblen*k+1:end); % 从译码数据中去除多余的部分,保留原始消息序列的长度。
figure(1);
stairs(msg_source(1:end),':*r'); % 绘制原始消息序列的阶梯图,使用红色星号表示
hold on; % 保持图形窗口的当前内容,以便后续的绘图命令在同一窗口中显示。
stairs(d,':+b'); % 绘制译码后的消息序列的阶梯图,使用蓝色加号表示。
ylim([-0.1,1.1]);
grid on;
legend('原始信息','恢复信息');
仿真结果:
可以看到编码后的 80bit 数据译码后与原数据一一对应。
参考文献:
1、【小白也能看懂】信道编码—卷积码等相关内容
2、卷积码matlab实现之ploy2trellis函数
3、信道编码:MATLAB使用卷积编译码函数
我的qq:2442391036,欢迎交流!