内容
本文设计的是(n,k,L)卷积码,其中输出路数n可变,但由于时间紧张k只能固定为1,寄存器个数L也可变,但最大不能超过64个寄存器。编码为0尾卷积码。
本文设计的信道是高斯白噪声的信道,噪声的均值和方差均可变,由于是白噪声均值为0。采用的是软判决的维特比译码,其中采用窗化法减小时延,其中窗长可变。
最后,观察不同的噪声功率和不同的窗长时的误码率,并可视化由于时间消耗较长,图线比较粗糙。
设计思路
首先,寄存器的表示用无符号long long类型的数据存储,long long类型数据有64比特,所以寄存器个数最大不超过64,寄存器的状态转移用C语言中的移位操作实现。抽头状态也用无符号long long数据表示,64位中有抽头用1表示,无抽头用0表示。
对于状态转移图,用一个结构体数组来存储,结构体定义的是当前状态转移过来的前两个状态,以及对应的输出和输入。
维特比译码时,对每一步的过程用一个一维数组来存储,该一维数组的索引是寄存器状态,存储的是一个结构体,结构体里为上一个转移到此的状态,相关值的累积,以及对应的输入。在设计窗化法时,当二维数组的长度达到了设计的窗长,就往前回溯,如遇到了路径合并就直接输出,并清空二维数组中合并的部分,如果没有唯一的路径就选择相关累积最大的一条路径输出,并清除二维数组中窗里的部分,最后译码时可能长度没达到窗长,由于是0尾码,就从0状态回溯得到输出结果。
关于高斯信道,生成高斯白噪声采用的是box-muller算法。发送时0对应-1,1对应+1。
功能模块
1、 计算在不同的状态下各路输出
2、 零尾编码模块
3、 高斯信道模块
4、 计算状态转移图模块
5、 计算软判决相关值模块
6、 软判决+窗化法+零尾的维特比译码模块
7、 将输入的抽头的字符串转换成long long型整数模块
8、 计算窗长和噪声功率对误码率的影响模块
9、 测试模块
结果展示
结果分析
生成的高斯噪声展示
高斯噪声的均值为0,方差为10。从结果可以看出生成的噪声数据近似服从高斯分布。
译码时窗长对误码率的影响
测试时设置(3,1,2)卷积码,抽头依次是100、110、101,高斯噪声均值为0,方差有1和0.5两种情况。结果如下,从图中可以看出窗长对误码率有周期性的影响,但这里我不确定是不是有问题。
噪声功率对误码率的影响
测试时设置(3,1,2)卷积码,抽头依次是100、110、101,译码时窗长为20,高斯噪声均值为0,方差变化。
从图中可以看出当噪声功率较小时误码率为0,随着噪声功率增大误码率增大,最大到了0.5即“胡猜”的情况,此时卷积码没有任何纠错能力。
完整代码
# include<stdio.h>
# include<iostream>
# include<vector>
# include<stdlib.h>
# include<cmath>
# include <time.h>
# include<string>
# include<fstream>
# define pi 3.1415926897932384
using namespace std;
typedef unsigned long long LL;
typedef unsigned int sint;
int num_one(LL n){
//统计LL数里面1的个数,奇数个输出1,偶数个输出0
int ans=0;
for(int i=0;i<64;i++) ans+=1&(n>>i);
return ans%2;
}
int ozone(LL a,LL b){
//求每路输出
//a 寄存器状态
//b 设置的抽头
LL ans;
ans=a&b;
return num_one(ans);
}
void coder(vector<sint> &v,vector<LL> reg,vector<sint> m,int outn,int L){
//vector<LL> reg;//输入,各抽头的情况,注意是倒序排列
//vector<unsigned short int> m;//输入,信息位码流
//int outn;//输入,输出路数
//int L;//输入,寄存器个数
//vector<unsigned short int> v;//输出,输出编码后码流
for(int i=0;i<L;i++) m.push_back(0);//零尾,需要输入L个零
int state=0;//寄存器状态,共有L+1位
L=L+1;
LL temp=0;
for(int i=0;i<m.size();i++){
state=state>>1;
temp=(m[i])<<(L-1);
state=state|temp;
for(int j=0;j<outn;j++) v.push_back(ozone(state,reg[j]));
}
}
void channl(vector<double> &r,vector<sint> v,double u,double sigma){
//01比特流变成+1,-1波形1->+1,0->-1;后经过高斯加性信道
//vector<s_int> v;输入,编码后的01比特流
//vector<double> r;输出,接收含高斯噪声的序列
//double u;均值,输入
//double sigma;方差 ,输入
for(int i=0;i<v.size();i++){
srand((unsigned)time(NULL));//随机数种子时变
double u1=double(rand()%10000)/10000;
double u2=double(rand()%10000)/10000;
double gauss;
gauss=u+sqrt(sigma)*sqrt(-2.0*(log(u1)/log(exp(1.0))))*cos(2*pi*u2);
if(v[i]) r.push_back(1+gauss);
else r.push_back(-1+gauss);
}
}
struct last{
LL a0,b0;//转移到此的两个状态
int a1,b1;//代表的是输出
int input;//代表的输入
};
struct node{
bool state;//false代表没激活,true代表激活
double grade;//相关系数和(软判决)
LL q;//前一个状态
int input;//对应的输入
};
vector<last> move;//状态转移图全局变量
void excel(vector<last> &move,vector<LL> reg,int outn,int L){
//计算状态转移表
//vector<last> move;输出,前状态转移表
//vector<LL> reg;输入,各抽头的情况,注意是倒序排列
//int outn;输入,输出路数
//int L;输入,寄存器个数
move.clear();
LL numsta=1<<L;//状态的总个数
int enter;//输入的情况
int temp;
for(LL i=0;i<numsta;i++){
last t_last={0};
enter=i>>(L-1);
t_last.input=enter;
temp=i<<1;
t_last.a0=temp&((1<<L)-1);
for(int j=0;j<outn;j++){
t_last.a1=t_last.a1|(ozone(temp,reg[j])<<j);
}
temp+=1;
t_last.b0=temp&((1<<L)-1);
for(int j=0;j<outn;j++){
t_last.b1=t_last.b1|(ozone(temp,reg[j])<<j);
}
move.push_back(t_last);
}
}
double deviation(vector<double> tr,LL ou,int nou){
//计算每步接收和输出对应的得分
//double smul=0;输出,得分
//vector<double> tr,输入,接收序列
//LL ou,输入,状态输出
//int nou,输入,输出的路数
vector<double> dou;
int temp;
for(int i=0;i<nou;i++){
temp=(ou>>i)&1;
if(temp) dou.push_back(1);
else dou.push_back(-1);
}
double smul=0;//输出
for(int i=0;i<nou;i++){
smul+=dou[i]*tr[i];
}
return smul;
}
void soft_decoder(vector<sint> &c,vector<double> r,int outn,int L,int window){
//卷积码的维特比译码
//vector<sint> c; //输出,译码得到的结果
//vector<double> r; //输入,接收的软判决序列
//int outn; //输入,输出路数
//int L; //输入,寄存器个数
//int window; //输入,窗长为L*window
vector<vector<node> > step;//译码网格图
LL numsta=1<<L;//状态的总个数
vector<node> vt;
node temp={0};
for(int i=0;i<numsta;i++) vt.push_back(temp);
vt[0].grade=0;vt[0].state=true;
step.push_back(vt);
int col=0;
while(col<r.size()){
vt.clear();
for(int i=0;i<numsta;i++) vt.push_back(temp);
vector<double> tr;//截取中间的接收片段
for(int i=0;i<outn;i++) tr.push_back(r[col+i]);
for(int i=0;i<numsta;i++){
node node_a={0},node_b={0};
if(col>=r.size()-outn*L){/零尾
if(i<=(1<<(L-1))-1){
node_a.q=move[i].a0;
node_a.grade=step[step.size()-1][node_a.q].grade+deviation(tr,move[i].a1,outn);
node_a.input=move[i].input;
node_a.state=step[step.size()-1][node_a.q].state;
node_b.q=move[i].b0;
node_b.grade=step[step.size()-1][node_b.q].grade+deviation(tr,move[i].b1,outn);
node_b.input=move[i].input;
node_b.state=step[step.size()-1][node_b.q].state;
if(node_a.state && !node_b.state) vt[i]=node_a;
if(!node_a.state && node_b.state) vt[i]=node_b;
if(node_a.state && node_b.state){
if(node_a.grade>node_b.grade) vt[i]=node_a;
else vt[i]=node_b;
}
}
}
node_a.q=move[i].a0;
node_a.grade=step[step.size()-1][node_a.q].grade+deviation(tr,move[i].a1,outn);
node_a.input=move[i].input;
node_a.state=step[step.size()-1][node_a.q].state;
node_b.q=move[i].b0;
node_b.grade=step[step.size()-1][node_b.q].grade+deviation(tr,move[i].b1,outn);
node_b.input=move[i].input;
node_b.state=step[step.size()-1][node_b.q].state;
if(node_a.state && !node_b.state) vt[i]=node_a;
if(!node_a.state && node_b.state) vt[i]=node_b;
if(node_a.state && node_b.state){
if(node_a.grade>node_b.grade) vt[i]=node_a;
else vt[i]=node_b;
}
}
step.push_back(vt);
col+=outn;
tr.clear();
//==================================================
//=================窗化法设计=======================
//==================================================
if(step.size()>=window){//达到了窗长
vector<sint> tem;//存储暂时回溯得到的倒序序列
vector<LL> previous;//记录前一步有的路径
for(int kk=1;kk<numsta;kk++) previous.push_back(kk);
int len_prev=previous.size();//记录当前previous中的长度
int index=-1;//索引,路径合并处的索引
bool have=false;//有重合的路径
for(int j=step.size()-1;j>=1;j--){
for(int kk=0;kk<len_prev;kk++){
previous.push_back(step[j][previous[kk]].q);
}
if(previous.size()-len_prev==1){//找到了合并路径
have=true;
index=j-1;
previous.erase(previous.begin(),previous.begin()+len_prev);
break;
}
previous.erase(previous.begin(),previous.begin()+len_prev);
len_prev=previous.size();
}
LL now_state;
if(have){//有重合的路径
now_state=previous[0];
}
else{//没有重合路径,就选择最大的一条输出
index=step.size()-2;
double temp_grade=-100000;
for(int k=0;k<numsta;k++){
if(step[index][k].grade>temp_grade){
temp_grade=step[index][k].grade;
now_state=k;
}
}
}
for(int j=index;j>=1;j--){
tem.push_back(step[j][now_state].input);
now_state=step[j][now_state].q;
}
for(int k=tem.size()-1;k>=0;k--) c.push_back(tem[k]);
tem.clear();
step.erase(step.begin(),step.begin()+index);
}
}
//================窗移后对剩下的部分译码==============
LL last_state=0;
vector<sint> last_tem;
for(int k=step.size()-1;k>=1;k--){
last_tem.push_back(step[k][last_state].input);
last_state=step[k][last_state].q;
}
for(int k=last_tem.size()-1;k>=0;k--) c.push_back(last_tem[k]);
}
LL str_int(string s){
//将对应输入的抽头转化成整数
//string s;抽头的描述,1代表有,0代表没有
LL number=0;//输出
for(int i=0;i<s.size();i++){
number+=(s[i]-'0')*(1<<(s.size()-1-i));
}
return number;
}
double test(int length,int outn,int L,int window,double u,double sigma,vector<LL>reg){
srand(time(NULL));//随机数种子时变
//int length;//比特流的长度
//int outn;输出的路数
//int L;寄存器个数
//int window;移动窗长
//vector<LL> reg;抽头
//double u;高斯加性噪声均值
//double sigma;//高斯加性噪声方差
vector<sint> b;//生成的比特流
while(length--) b.push_back(rand()%2);
//===================编码=================
vector<sint> v;//编码输出流
coder(v,reg,b,outn,L);//编码
//=============经高斯信道传输==============
vector<double> r;//高斯信道接收码流,1->+1,0->-1
channl(r,v,u,sigma);
//===============软判决译码================
vector<sint> c;//译码恢复信息流
soft_decoder(c,r,outn,L,window);
for(int j=0;j<L;j++) b.push_back(0);
int ans=0;
for(int i=0;i<b.size();i++){
if(c[i]!=b[i]) ans++;
}
return ans*1.0/c.size();
}
void gauss_test(){
//测试生成的高斯噪声分布,并生成文件
srand((unsigned)time(NULL));//随机数种子时变
double u=0,sigma=10;
double *gauss_test;
int N=1000;
gauss_test=(double*)malloc(N*sizeof(double));
for(int i=0;i<N;i++){
double u1=double(rand()%10000)/10000;
double u2=double(rand()%10000)/10000;
double gauss;
gauss=u+sqrt(sigma)*sqrt(-2.0*(log(u1)/log(exp(1.0))))*cos(2*pi*u2);
cout<<gauss<<endl;
gauss_test[i]=gauss;
}
ofstream fout("gauss_test.txt");
for(int i=0;i<N;i++){
fout<<gauss_test[i]<<endl;
}
fout.close();
free(gauss_test);
}
void plt(){
int length=10000;//比特流的长度
int outn=3;//输出的路数
int L=2;//寄存器个数
int window;//移动窗长
vector<LL> reg;//抽头
double u=0;//高斯加性噪声均值
double sigma=0.5;//高斯加性噪声方差
vector<double> er;
reg.push_back(4);reg.push_back(6);reg.push_back(5);
excel(move,reg,outn,L);
double ans=0;
//===========测试窗长对误码率的影响================
for(window=4;window<1000;window+=10){
ans=0;
for(int i=0;i<10;i++){
ans+=test(length,outn,L,window,u,sigma,reg);
}
er.push_back(ans/10);
cout<<ans/10<<endl;
}
//============测试噪声功率对误码率的影响============
/*window=20;
for(sigma=0;sigma<5;sigma+=0.05){
ans=0;
for(int i=0;i<10;i++){
ans+=test(length,outn,L,window,u,sigma,reg);
}
er.push_back(ans/10);
cout<<ans/10<<endl;
}*/
ofstream fout("window05_error.txt");
for(int j=0;j<er.size();j++){
fout<<er[j]<<endl;
}
fout.close();
}
int main(){
//gauss_test();
//plt();
int length;//比特流的长度
int outn;//输出的路数
int L;//寄存器个数
int window;//移动窗长
vector<LL> reg;//抽头
double u;//高斯加性噪声均值
double sigma;//高斯加性噪声方差
string s;
int a;//是否测试
cout<<"开始测试 是[1]/否[0]";
scanf("%d",&a);
while(a){
cout<<"============================="<<endl;
cout<<"请确定输出路数:";cin>>outn;
cout<<"请确定寄存器个数:";cin>>L;
cout<<">>>>>请按要求输入各路抽头<<<<<"<<endl;
cout<<" 每路需要从左至右输入"<<L+1<<"个抽头状态"<<endl;
for(int i=0;i<outn;i++){
s.clear();
cout<<" 第"<<i+1<<"路抽头:";cin>>s;
reg.push_back(str_int(s));
}
cout<<"请确定译码的窗长: ";cin>>window;
cout<<"请确定比特流长度:";cin>>length;
cout<<"请确定高斯加性噪声均值:";cin>>u;
cout<<"请确定高斯加性噪声方差:";cin>>sigma;
excel(move,reg,outn,L);//状态转移图
cout<<"******************************"<<endl;
cout<<" 现在惊喜即将呈现 "<<endl;
cout<<"软判决维特比译码误码率为:"<<test(length,outn,L,window,u,sigma,reg)<<endl;
cout<<endl<<endl<<endl;
cout<<"继续测试[1]/离开[0]";scanf("%d",&a);
}
return 0;
}