【详解】【通信编码】Vertibi算法实现卷积码解码

前言 最近真是忙忙的咧,数一数真是九门功课同步学,而且五门硬课(考前突击致挂率很高的那种)。再加上老前辈的肺腑之言——基础才是本科生该关注的。所以看完基础编程的《Unix网络编程》也将告一段落辣,待我打好基础再与你一战!
言归正传,接下来将讨论的Vertibi编码依然来自通信编码老师的课后作业。现在时间不早了咱就省掉前戏了。
原理 关于Vertibi算法的原理,网上相关的资料很多,况且来看这篇文章的同学对原理应该不陌生,在这里只是简单提提。我们以(2,1)卷积编码器为例,其核心可用下面几幅图表示
【详解】【通信编码】Vertibi算法实现卷积码解码
文章图片

  • 每个卷积编码器可用状态转移来表示,因此图中a,b,c,d代表编码器所处的4个状态,而j表示时刻,实线表示输入为1,虚线表示输入为0,而线上的数字表示输入对应的输入;打个比方,图中(a,0)点到(c,1)点的连接表示:当输入为0时,寄存器的状态从0时刻的a(我们设为两个寄存器的状态全为0,记为00)到1时刻的c(记为10),且伴随着输出为11
【【详解】【通信编码】Vertibi算法实现卷积码解码】而Vertibi译码则是从上图中找出一条路,使这条路径对应的输出序列与输入序列的汉明距离最小(既“最像”)
面对这种问题,我们首先想到的便是树的深度遍历,既找出所有路径然后看看谁“最像”。然而。Vertibi就是靠这个成为南加州大学知名校友,创立高通公司,并且成功让系主任花半节课将他的生平,自然有其妙♂处。
请看下图
【详解】【通信编码】Vertibi算法实现卷积码解码
文章图片

它阐述了一个定理:如果P是最短路径,对于P上任意一点x,P1一定是最短路径。
这个定理可以很轻松的用反证法证明
之后的东西好难用语言描述所以我们直接看伪代码吧
【详解】【通信编码】Vertibi算法实现卷积码解码
文章图片

在这里做些解释
首先定义路径长度为两个状态转移时的输出序列与相应的输入序列的汉明距离
  • u[j][s]代表J时刻到达s状态的累计路径长度
  • l[t][s]代表从t状态转到l状态的的路径长度
  • B[s]代表s状态的对应解码序列
由于最后路径会回到a状态(既寄存器为00状态),因此按照以上方法执行后,B(a)即为结果
C代码
int vertibi(int *seq, int seqLen, int outnum,int innum,int stat[][MAXSTAT], int statRow, int out[][MAXSTAT][MAXBLOCK],int *res , int *fixed) {/*seq为输入序列,seqLen为输入序列长度,outnum为输出数(对于(2,1)卷积码而言为2),innum位输入数(对于(2,1)卷积码而言为1),stat为二维的状态转移表(比如说,若状态a转到状态c需要输入1,则stat[1][3]=1,无法转移的值设为-1),statRow为状态转移表的列数(既状态数量),out[][][]为三维的数组,存储两个状态间转换时的输出序列,res为解码结果序列,fixed为纠正后的输入序列*/int u[MAXJ][MAXSTAT]; int B[MAXSTAT][MAXLEN/2]; int BB[MAXSTAT][MAXLEN]; /*BB[s]表示s状态对应的纠正后序列*/memset(u, 0, sizeof(u)); memset(B, 0, sizeof(B)); memset(BB, 0, sizeof(BB)); /*置零*/for (int j = 0; j < statRow; j++) { if (!j) u[0][j] = 0; else u[0][j] = 100; } /*因为状态的起点为状态a,所以在0时刻除了状态a(即u[0][0])外其它值都设为无穷大(此处定义为100)*/int curSeq[MAXBLOCK]; //当前用于计算路径距离的输入序列段for (int J = 1; J < seqLen/(outnum*innum); J++) {//此处的J代表时刻 for (int ii = 0; ii < outnum*innum; ii++) { curSeq[ii] = seq[(J-1)*outnum*innum+ii]; } //从输入中取出当前段 for (int i = 0; i < statRow; i++) { //遍历J时刻的状态 int min = 100; int minj = 0; //记录下保留路径的起点 for (int j = 0; j < statRow; j++) { //遍历J-1时刻的状态 int l=0; for (int jj = 0; jj < outnum*innum; jj++) { //遍历当前段的每个字符来计算路径距离 if (stat[j][i] < 0) { //若两个状态本来就无转移关系,则将l设为无穷大,这样这条路径就不可能被选择了 l = 100; break; } if (curSeq[jj] != out[j][i][jj]) //计算汉明距离 l++; }if ((u[J - 1][j] + l) < min) { min = u[J - 1][j] + l; minj = j; } //选出累计路径长度最小的 }u[J][i] = min; //将最小的累计路径赋给J时刻的i状态for (int ii = 0; ii < (J - 1) *outnum* innum; ii++) { BB[i][ii] = BB[minj][ii]; } for (int jj = 0; jj < outnum*innum; jj++) { BB[i][(J - 1)*outnum*innum + jj] = out[minj][i][jj]; } //上面两个for循环实现从状态minj继承纠正序列,再往上面填上对应于当前段的纠正段 for (int ii = 0; ii < (J - 1) * innum; ii++) { B[i][ii] = B[minj][ii]; } for (int jj = 0; jj < innum; jj++) { B[i][(J - 1)*innum + jj] = stat[minj][i]; } //尾加对应于纠正段的输入段,原理同上 }}for (int i = 0; i < seqLen/outnum; i++) { res[i] = B[0][i]; }for (int i = 0; i < seqLen; i++) { fixed[i] = BB[0][i]; } //赋值给res和fixed return 1; }

结语 宝宝学识浅薄,也许没有完全讲清楚。各位如有疑问可以留言,我会尽力解答的辣
(??`ω′?)

    推荐阅读