为了配合学生借助 taskbus进行纯控制台STDIO模块的开发,我们收集了手头的通用纠错算法,便于学生进行开发学习。今天介绍卷积码以及维特比译码算法。在我自己以前学习《通信原理》的时候,总是对维特比算法的实质有些把握不清楚,实现起来,颇煞费周章。

1. 卷积纠错

对一个无限长的二进制序列,每个时钟时间轴向前流淌1比特。按照特定的位置关系,将当前时钟数据与过去若干时钟的数据模二加,得到校验结果,相当于把一个无限长多项式与校验多项式进行卷积运算。

1.1 序列角度

比如一个最简单的2,1,3码,其生成式八进制可以是13,17, 第一路13相当于二进制 001 011,也就是 1+y2+y31+y^2+y^31+y2+y3。这个多项式与无限长序列卷积,可看做下表:

时钟 x−3x_{-3}x−3​ x−2x_{-2}x−2​ x−1x_{-1}x−1​ x0x_0x0​ x1x_1x1​ x2x_2x2​ x3x_3x3​ x4x_4x4​ x5x_5x5​
0 111 111 0 1
1 111 111 0 1
2 111 111 0 1
3 111 111 0 1
4 111 111 0 1
结果 x−3+x−2+x0x_{-3}+x_{-2}+x_0x−3​+x−2​+x0​ x−2+x−1+x1x_{-2}+x_{-1}+x_1x−2​+x−1​+x1​ x−1+x0+x2x_{-1}+x_{0}+x_2x−1​+x0​+x2​ x3+x1+x0x_3+x_1+x_0x3​+x1​+x0​ x4+x2+x1x_4+x_2+x_1x4​+x2​+x1​

从时钟0开始,每个时钟输出的结果是当前时钟与之前2、3个时钟的模二加。第二路1,7,也就是二进制 001 111,原理是一样的。经过这样处理后,输入一路数据,输出2路校验,形成了典型的1/2速率卷积码。

1.2 电路图角度

这种结构也可以用电路图表示:

一个具备3个缓存器的电路,每个时钟刷新一次。输出为当前时钟数据与缓存器中的若干状态的模二加。下个时钟,缓存器向右移存,新的数据推入,周而复始。

1.3 状态机角度

如果把缓存器的状态看做一个整数,则每次输入作为激励,会让这个状态不停地跳转。比如上述最简单的2,1,3码,其生成式八进制可以是13,17, 对应状态图

寄存器有3个,状态共2^3 = 8个,由于输入不同,跳转也不同。比如,状态跳转为s0->s4->s2->s5->s6->s7->s3->s1->s0…, 会产生输出2,1,0,1,1,1,0,2…

2 维特比译码

维特比算法会找出一条最优的路径,按照这条路径跳转产生的输出,与接收到的实际输出之间空间距离(硬判决用汉明距离,即比特重量)最小。接着上面的例子,因为传输错误,接收到的输出为2,1,0,1,[0],1,0,2…,第五个输出错了。通过译码,可以还原出原始路径,理由是按照这条路径,产生的全路径误差最小。

状态机是维特比的核心,具体输入什么,跳到哪去,完全取决于相应的编码。上述前向(无反馈)卷积码可以产生状态机,有反馈的feedback码,同样产生状态机。理论上,哪怕有人新发明一种纠错码,只要可用LSM来描述,就可以使用维特比译码。当然,要保证用好生成式确保好的代数性质,使得正确路径与其他路径产生的输出尽可能不同。

维特比算法并不是只针对卷积码的,可以说,它是一个普适的有限状态机算法(LSM)。只要有一个LSM,能够受不同输入的触发,在各个状态间跳转,并产生输出,就满足可基本条件,可根据状态机利用Viterbi 来从污染的输出中还原输入。

2.1 基本原理

建立一个动态的跳转结构,记录并更新每个时钟下,抵达各个状态的最优路径。
上图显示了前后两个时钟的状态。

  1. 根据状态机,当假设当前时钟输入各为0,1时,可得到下一时钟的状态。同时,跳转过程导致新的输出,追加到当前时钟的输出队列尾部(队列过长后,首部的数据就弹出丢弃)。
  2. 各个状态都进行跳转,得到新的状态。每一新的状态,都对应了2个队列,分别来自不同的跳转路径。
  3. 将各个状态的队列,与实际接收到的结果比对,只保留最优的1组。
  4. 时钟推进,周而复始。
  5. 每次当队列首部最老的数据弹出前,看看总误差最小的队列是谁,把对应位置的输入作为译码的结果。

2.2 数据结构

译码器采用结构体描述上述算法

//存储从当前状态到下一状态的跳转
typedef  struct {int * nNextStatus; //下一状态int * nOutput;        //跳转输出
} tag_statusjmp_next;//存储当前状态来自哪个旧状态的跳转
typedef  struct {int * nLastInput;  //上一次的触发int * nLastStatus;  //上一状态int * nLastOutput;    //跳转输出
} tag_statusjmp_last;//最优路径表,采用double_buffer技术,避免频繁动态内存分配
typedef  struct {unsigned long long nCurrentHamming[2];         //当前全路径汉明int nInitialStatus[2];          //本路径的起始初态unsigned long long *nHamming[2];             //各个跳转的汉明int *nInChain[2];              //各个跳转的输入(触发)
} tag_path;/** \brief 有限状态机编译码器*   有限状态机有若干状态,因用户输入的触发,在状态间跳转。跳转的过程中*   跳转过程中,产生了跳转输出。受到信道污染的输出被重新送入译码器,*    通过维特比算法,译码器会纠正一定量的错误。*/
typedef struct
{int reg_status;  //寄存器状态数,例子2,1,3为8int data_status; //输入状态数,例子为2, 只有两个状态0,1int code_bitlen; //输出比特数,例子为2, 双路输出int L;//队列记忆长度,如64tag_statusjmp_next  * table_nextjmp/*reg_status 组 */;tag_statusjmp_last  *  table_lastjmp/*reg_status 组*/;tag_path  *  best_path/*reg_status 组*/;int nClock;                   //当前时钟int * vec_decoded_data;       //存储译码结果的缓存int vec_decoded_data_msz;int vec_decoded_data_sz;long long   nBestHamming;           //译码器当前的最优汉明int * nBestStatus;                  //所有最优路径的索引int nBestStatus_sz;
} clsm_codec_arg;

2.3 环状双缓存技术

提高性能的关键技术是避免在跳转过程中的动态内存分配、内存搬移。

  1. 采用环状的缓存器,结合时钟取模,可以模拟有限有效长度下的无限序列。
  2. 采用奇偶两组镜像缓存,结合时钟取模,读A写B,读B写A,效率高高。

3. 调用方式

实际的例子如下:

int demo_216()
{int pins[] = {133,171};void * codec = new_conv_codec(2,1,6,pins,0,64);printf ("---------------\n216 hard viterbi dec\n");int inital_status = 0;int data[1024],code[1024],recv[1024];//100Kb data, 200KbCodefor (int i=0;i<100;++i){for (int j=0;j<1024;++j)data[j] = i*1717%2;inital_status = encode(codec,data,1024,code,inital_status);for (int i=0;i<5;++i)code[rand()%1024] ^= rand() % 4;decode(codec,code,1024,false);int e = curr_best_hamming(codec);int poped = pop_data(codec,recv,1024);printf ("group %03d Ham=%7d, poped %d symbols\r",i+1,e,poped);}delete_lsm_codec(codec);printf ("\n");return 0;
}

4. C重封装

主要接口如下:

//==============================卷积纠错码编译码================================
/*** @brief oc_conv_new_codec 分配一个卷积码编译码器* @param n 校验每符号n比特* @param k 信息每符号k比特* @param m 寄存器 m 时钟* @param poctPins 八进制前向电路管脚数组,如 133,171* @param pfbPins  八进制前向电路管脚数组,如 103,157* @param L 维特比记忆长度* @return 译码器指针*/
void * oc_conv_new_codec(int n, int k, int m,const int * poctPins/*k*n*/,const int * pfbPins/*k*k*/,int L);/*** @brief oc_conv_delete_codec 释放卷积码编译码器 codec* @param codec 译码器指针*/
void oc_conv_delete_codec(void * codec);/*** @brief oc_conv_reset_status 重置卷积码编译码器 codec 的判决路径,* 用于突发重置卷积译码* @param codec 卷积码编译码器*/
void oc_conv_reset_status(void *codec);/*** @brief oc_conv_encode 使用卷积码编译码器 codec 对长度为nLen时钟的各个* 输入符号 inputArray 进行编码。* @param codec 卷积码编译码器* @param inputArray 输入符号,过去时钟在高位。比如k=2时,* 第一符号 2bit为  [i=0] [i=1]* @param nLen  符号长度* @param outputArray   输出符号,n比特,过去时钟在高位,* 如第一符号 2bit 为 [i=0] [i=1]* @param InitialStatus 编码时的初态。注意,过去时钟寄存器在低位。* @return 编码完成后的下一初态,可以进行迭代连续编码。*/
int oc_conv_encode(void * codec,const int inputArray[],int nLen, int outputArray[], int InitialStatus);/*** @brief oc_conv_decode 把nSymbolLen长度的硬判决符号CodeArray压入译码队列。* @param codec  卷积码编译码器* @param CodeArray CodeArray的高n~2n-1位是删除标记,表示对应的0-n-1位是否无效。(0:有效,1:无效)* 这主要用于打孔恢复。被打孔的位置,对应高位为1.如,  1 0   0 1, 说明本符号的高位是无效的,低位是1* @param nSymbolLen 符号长度* @param bFinished 如果是最末的数据,则用 bFinished = 1进行指示。* @return 可弹出的时钟数。除非是 bFinished == 1,否则,压入 nSymbolLen 个符号只能弹出 nSymbolLen-L个符号。* 其余的符号要等到冲洗到位后才能弹出。*/
int oc_conv_decode(void * codec,const int CodeArray[], int nSymbolLen, int bFinished);/*** @brief oc_conv_decode_soft 把nBitLen长度的软判决比特CodeArray压入译码队列。* @param codec 卷积码编译码器* @param CodeArray 编码软比特* @param nBitLen  软比特个数* @param v0 0的电平,如-8192* @param v1 1的电平,如+8192* @param vOmit中间电平,如0.0* @param bFinished 如果是最末的数据,则用 bFinished = 1进行指示。* @return*/
int oc_conv_decode_soft(void * codec,const int CodeArray[], int nBitLen, int v0, int v1, int vOmit, int bFinished);/*!* \brief oc_conv_curr_best_hamming 返回当前最优路径的错误个数(汉明距离)* \param codec 卷积码编译码器* \return  硬判hamming,或软判欧式距离*/
unsigned long long oc_conv_curr_best_hamming(void * codec);/*** @brief oc_conv_pop_data 尝试从路径图中弹出nLen组信息,,每个符号放在DataArray里。* @param codec  卷积码编译码器* @param DataArray 用于存放输出时钟数据的数组* @param nLen  最大要弹出的时钟* @return  弹出时钟数*/
int oc_conv_pop_data(void * codec,int DataArray[], int nLen);

5. 演示GUI

如果安装了Qt,则可以使用图形界面演示效果。

6. 代码链接

参考
https://gitcode.net/coloreaglestdio/open_codec

可用于SDR的C语言纠错编码通用算法收集(3)-卷积码与Viterbi译码相关推荐

  1. 可用于SDR的C语言纠错编码通用算法收集(1)-朴素字典查表BCH纠错

    在开设了SDR软件无线电课程后,有余力的学生开始尝试开发自己的小通信电台,实现Hello World的无线传输.在实验过程中,一定会遇到纠错的问题.即使是在实验室,理想化的信道也是不存在的.没有纠错的 ...

  2. 可用于SDR的C语言纠错编码通用算法收集(4)-LDPC低密度奇偶校验码

    为了配合学生借助 taskbus进行纯控制台STDIO模块的开发,我们收集了手头的通用纠错算法,便于学生进行开发学习.今天介绍LDPC编码. 1. 低密度奇偶校验编码LDPC 低密度奇偶校验码LDPC ...

  3. 一步一步写算法(之通用算法的编写)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们写过各种各样的算法,什么排序.查找.二叉树.队列.堆栈等等.但是我们在编写这些代码的时 ...

  4. c语言系统的通用数据结构,(转载)C语言实现通用数据结构的高效设计

    (转载)C语言实现通用数据结构的高效设计 [复制链接] 使用宏替代模板的方案 最近在阅读一个开源的C++代码,里面用到了大量的STL里面的东西.也许是自己一直用C而很少用C++来实现算法的原因,STL ...

  5. 基于 Ruby 谈谈——程序设计语言的通用框架

    基于 Ruby GScript 谈谈--程序设计语言的通用框架 目录 基于 Ruby GScript 谈谈--程序设计语言的通用框架 一.架构思维导图 1.Ruby GScript 介绍 2.实践: ...

  6. ICCV 2021| GRF: 用于三维表征和渲染的通用神经辐射场(已开源)

    作者丨Bo Yang@知乎 来源丨https://zhuanlan.zhihu.com/p/399867075 编辑丨3D视觉工坊 论文链接: https://arxiv.org/abs/2010.0 ...

  7. C语言实现通用链表初步(一)

    注意:本文讨论的是无头单向非循环链表. 假设不采用Linux内核链表的思路,怎样用C语言实现通用链表呢? 一种常用的做法是: typedef int element_t; struct node_in ...

  8. boost::regex模块用于测试特定于语言环境的表达式的帮助程序类

    boost::regex模块用于测试特定于语言环境的表达式的帮助程序类 实现功能 C++实现代码 实现功能 boost::regex模块用于测试特定于语言环境的表达式的帮助程序类 C++实现代码 #i ...

  9. 制作加密狗程序_【火腿DIY】用于SDR应用程序的自定义热键键盘 | 视障人士的选择...

    Christoph用于SDR应用程序的自制自定义热键键盘 上周,我在SDRplay 社交页面上看到了Christoph Jahn的精彩文章. 克里斯托夫(Christoph)制作了一个与SDRuno一 ...

最新文章

  1. leetcode算法题--1比特与2比特字符
  2. java spring框架 注解_详解Java的Spring框架中的注解的用法
  3. modbus3-关于Modicon Modbus Protocol和modscan32
  4. 配置文件*.xml中 classpath: 与 classpath*: 的区别
  5. leetcode双指针(python与c++)
  6. 支付宝ios SDK官方下载页面
  7. JDK8 Stream操作整理
  8. WEB系统技术开发方向
  9. 事务例子_耗时3周!7000+字的Spring事务总结来啦
  10. 2021世界机器人大赛— 青少年机器人设计大赛
  11. 英特尔驱动程序下载_如何修复英特尔计算机上的“此计算机未验证正在安装的驱动程序”...
  12. SECS/GEM 产品开发和介绍
  13. popperjs V2 之应用库 tippy.js 源码阅读
  14. Django之 migration 原理
  15. ArcEngine加载图层的五个步…
  16. PHP语言是什么语言及能解决当下什么问题-动态更新
  17. 浅谈魔兽世界的BUFF系统和阵营系统
  18. ASP.NET MVC里ModelState.IsValid总是true或者总是false
  19. 网络编程--TCP实例
  20. realmeq参数配置详情_realmeq参数配置-realmeq手机性能规格详情

热门文章

  1. RTT之设备管理机制
  2. java学习之路 之 多线程练习题
  3. 上门做饭系统源码,私厨上门烹饪美食
  4. iPhone手机打开这个功能,开会不用愁
  5. 笔试面试总结,Android砥砺前行
  6. 投影仪上能安装摄像头上互动网课吗?保姆级投影仪上网课教程分享
  7. 为什么我的相机卡总是出问题,相机内存卡里的照片是格式化好还是删除好?
  8. 30Wqps+闲鱼优惠中台,如何架构的?
  9. 实例:函数定义来计算面积,体积
  10. 10 个顶级web设计趋势,让你的应用程序脱颖而出