卷积码编译码

内容

本文设计的是(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;
}
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值